Create an Image Magnifier with React

anxiny

Anxiny

Posted on May 2, 2021

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!

💖 💪 🙅 🚩
anxiny
Anxiny

Posted on May 2, 2021

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related