Today, let's create a simple image magnifier component.
First, let's create the basic structure of the component.
function ImageMagnifier({
src,
width,
height,
magnifierHeight = 100,
magnifieWidth = 100,
zoomLevel = 1.5
}: {
src: string;
width?: string;
height?: string;
magnifierHeight?: number;
magnifieWidth?: number;
zoomLevel?: number;
}) {
return (
// the container
<div
style={{
position: "relative",
height: height,
width: width
}}
>
// the image
<img
src={src}
style={{ height: height, width: width }}
alt={"img"}
/>
// a div for the magnifier
<div></div>
</div>
);
}
Then, we need to add a state that controll the display of magnifier
const [showMagnifier, setShowMagnifier] = useState(false);
and, the event handler that update the state.
...
<img
src={src}
style={{ height: height, width: width }}
onMouseEnter={(e) => {
setShowMagnifier(true);
}}
onMouseLeave={() => {
setShowMagnifier(false);
}}
alt={"img"}
/>
...
Now, we need to add a state that holds x, y position of magnifier, and update it when mouse move hover the image.
...
const [[x, y], setXY] = useState([0, 0]);
...
<img
...
onMouseMove={(e) => {
// update cursor position
const elem = e.currentTarget;
const { top, left } = elem.getBoundingClientRect();
// calculate cursor position on the image
const x = e.pageX - left - window.pageXOffset;
const y = e.pageY - top - window.pageYOffset;
setXY([x, y]);
}}
...
/>
The pageX/Y
coordinates are relative to the left/top corner of current page.
The pageXOffset/pageYOffset
are the scroll offsets of the page.
The left/top
are the coordinates of the image left/top position.
const x = e.pageX - left - window.pageXOffset;
const y = e.pageY - top - window.pageYOffset;
These two calculations will give us the cursor's x/y coordinates based on the image.
In order to calculate the position of the magnifier, we also need to have the size of image in pixels, so let's update it when mouse is entering the image.
<img
...
onMouseEnter={(e) => {
// update image size and turn-on magnifier
const elem = e.currentTarget;
const { width, height } = elem.getBoundingClientRect();
setSize([width, height]);
setShowMagnifier(true);
}}
...
/>
Now, we can add the position and other basic styles to the magnifier.
<div
style={{
display: showMagnifier ? "" : "none",
position: "absolute",
// prevent magnifier blocks the mousemove event of img
pointerEvents: "none",
// set size of magnifier
height: `${magnifierHeight}px`,
width: `${magnifieWidth}px`,
// move element center to cursor pos
top: `${y - magnifierHeight / 2}px`,
left: `${x - magnifieWidth / 2}px`,
opacity: "1", // reduce opacity so you can verify position
border: "1px solid lightgray", // show the border of magnifier
backgroundColor: "white",
backgroundImage: `url('${src}')`,
backgroundRepeat: "no-repeat",
}}
/>
Then, we need to zoom the image inside the magnifier.
...
//calculate zoomed image size
backgroundSize: `${imgWidth * zoomLevel}px ${imgHeight * zoomLevel}px`,
...
Also, we need to center the image in the magnifier base on cursor position on the image.
...
//calculate position of zoomed image.
backgroundPositionX: `${-x * zoomLevel + magnifieWidth / 2}px`,
backgroundPositionY: `${-y * zoomLevel + magnifierHeight / 2}px`
...
We done it. Here is the full code and a demo:
function ImageMagnifier({
src,
width,
height,
magnifierHeight = 100,
magnifieWidth = 100,
zoomLevel = 1.5
}: {
src: string;
width?: string;
height?: string;
magnifierHeight?: number;
magnifieWidth?: number;
zoomLevel?: number;
}) {
const [[x, y], setXY] = useState([0, 0]);
const [[imgWidth, imgHeight], setSize] = useState([0, 0]);
const [showMagnifier, setShowMagnifier] = useState(false);
return (
<div
style={{
position: "relative",
height: height,
width: width
}}
>
<img
src={src}
style={{ height: height, width: width }}
onMouseEnter={(e) => {
// update image size and turn-on magnifier
const elem = e.currentTarget;
const { width, height } = elem.getBoundingClientRect();
setSize([width, height]);
setShowMagnifier(true);
}}
onMouseMove={(e) => {
// update cursor position
const elem = e.currentTarget;
const { top, left } = elem.getBoundingClientRect();
// calculate cursor position on the image
const x = e.pageX - left - window.pageXOffset;
const y = e.pageY - top - window.pageYOffset;
setXY([x, y]);
}}
onMouseLeave={() => {
// close magnifier
setShowMagnifier(false);
}}
alt={"img"}
/>
<div
style={{
display: showMagnifier ? "" : "none",
position: "absolute",
// prevent magnifier blocks the mousemove event of img
pointerEvents: "none",
// set size of magnifier
height: `${magnifierHeight}px`,
width: `${magnifieWidth}px`,
// move element center to cursor pos
top: `${y - magnifierHeight / 2}px`,
left: `${x - magnifieWidth / 2}px`,
opacity: "1", // reduce opacity so you can verify position
border: "1px solid lightgray",
backgroundColor: "white",
backgroundImage: `url('${src}')`,
backgroundRepeat: "no-repeat",
//calculate zoomed image size
backgroundSize: `${imgWidth * zoomLevel}px ${
imgHeight * zoomLevel
}px`,
//calculate position of zoomed image.
backgroundPositionX: `${-x * zoomLevel + magnifieWidth / 2}px`,
backgroundPositionY: `${-y * zoomLevel + magnifierHeight / 2}px`
}}
></div>
</div>
);
}
Thank you all!
Top comments (14)
This line is beautiful:
I had to create an account to say a very big thank you! A very good article! works like magic!
nice article
i love it
could you please make an article on how to create side by side Image magnifier like how amazon does using react, that would be great. Thanks
You can set the left and top of magnifier at any position you need it to be
amazing design
Good job I love it <3
client-side-unvalidated-url-redirection Allowing unvalidated redirection based on user-specified URLs
This is a great article, I've tried using libraries but I couldn't customize it to fit my use-case, this is perfect, thank you
THANK YOU !✨🏆🏆