DEV Community

Anxiny
Anxiny

Posted on

 

Create an Image Magnifier with React

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>
  );
}
Enter fullscreen mode Exit fullscreen mode

Then, we need to add a state that controll the display of magnifier

 const [showMagnifier, setShowMagnifier] = useState(false);
Enter fullscreen mode Exit fullscreen mode

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"}
      />

...
Enter fullscreen mode Exit fullscreen mode

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]);
    }}
    ...
/>

Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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);
    }}
    ...
/>

Enter fullscreen mode Exit fullscreen mode

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",
    }}
/>

Enter fullscreen mode Exit fullscreen mode

Then, we need to zoom the image inside the magnifier.

    ...
    //calculate zoomed image size
    backgroundSize: `${imgWidth * zoomLevel}px ${imgHeight * zoomLevel}px`,
    ...
Enter fullscreen mode Exit fullscreen mode

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`
    ...
Enter fullscreen mode Exit fullscreen mode

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>
  );
}
Enter fullscreen mode Exit fullscreen mode

Thank you all!

Top comments (9)

Collapse
 
aliiiu profile image
Aliu Salaudeen

I had to create an account to say a very big thank you! A very good article! works like magic!

Collapse
 
drarig29 profile image
Corentin Girard

This line is beautiful:

const [[x, y], setXY] = useState([0, 0]);
Enter fullscreen mode Exit fullscreen mode
Collapse
 
dev_emmy profile image
nshimiye_emmy

nice article

Collapse
 
santokhan profile image
Santo Khan

amazing design

Collapse
 
bajwolf profile image
Bajro

Good job I love it <3

Collapse
 
dev_emmy profile image
nshimiye_emmy • Edited

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

Collapse
 
anxiny profile image
Anxiny • Edited

You can set the left and top of magnifier at any position you need it to be

Collapse
 
syahbes profile image
Shlomi Yahbes

THANK YOU !✨🏆🏆

Collapse
 
jagch profile image
José GCH

Thanks man

Visualizing Promises and Async/Await 🤓

async await

☝️ Check out this all-time classic DEV post on visualizing Promises and Async/Await 🤓