Create an Image Magnifier with React
Tags: react, javascript, webdev, css
Published: 2021-05-02 20:53:39

Today, let's create a simple image magnifier component.

First, let's create the basic structure of the component.


```typescript
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

```typescript
 const [showMagnifier, setShowMagnifier] = useState(false);
```

and, the event handler that update the state.

```typescript
...
    <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.

```typescript
...
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.

```typescript
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.

```typescript
<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.
```typescript
<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.


```typescript
    ...
    //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.

```typescript
    ...
    //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:


```typescript
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>
  );
}
```

{% codesandbox 3jsqs %}


Thank you all!