DEV Community

loading...

Create an Image Magnifier with React

anxinyang profile image AnxinYang ・3 min read

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!

Discussion (6)

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 • 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
anxinyang profile image
AnxinYang Author • Edited

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

Collapse
santokhan1999 profile image
Santo Khan

amazing design

Collapse
bajro17 profile image
Bajro

Good job I love it <3

Collapse
dev_emmy profile image
nshimiye_emmy

nice article

Forem Open with the Forem app