← Back toDrag and drop in React

Build a magnifying glass for images

Written byPhuoc Nguyen
Created
11 Nov, 2023
Tags
React image magnifier
An image magnifier is a handy tool that lets users zoom in on images and see them in greater detail. It's useful in many real-life situations. For example, on e-commerce websites that sell clothing, customers may want to inspect the fabric texture and pattern before buying. An image magnifier allows them to zoom in on the fabric, view it in high resolution, and get a clearer idea of what they're purchasing.
Similarly, websites that sell jewelry or watches can use an image magnifier to show intricate details like gemstones or watch faces. This allows customers to appreciate the quality and craftsmanship of the product before making a purchase. By letting customers see these details up close, they can make more informed decisions and feel confident in their choices.
Image magnifiers are also useful in fields like art and photography. On websites that sell prints or photographs, an image magnifier can help customers examine fine details like brushstrokes or grain structure. This can help them better understand the artist's technique and style.
Overall, an image magnifier is a versatile tool that can enhance the user experience on any website that features images with fine details. In this post, we'll show you how to create an image magnifier with React. Let's dive in!

Setting up the magnifier layout

To set up the magnifier, you'll need to focus on two main elements: the image and the glass. The image element displays the original image, while the glass element magnifies a portion of the image.
tsx
<div className="magnifier">
<img src="..." />
<div className="magnifier__glass" />
</div>
To position the glass element inside the magnifier using CSS, we follow a few simple steps. First, we set the position of the magnifier to `relative` so that any child elements we position will be relative to it. Then, we set the position of the glass element to `absolute` so that we can position it precisely within its parent container.
To center the glass over the image, we use negative `top` and `left` values equal to half of its width and height. This aligns the top-left corner of the glass with the center point of its parent container.
Finally, we can use a `transform` property to adjust any additional positioning as needed. In this case, we're not applying any additional transforms, but this property can come in handy for adjusting scale or rotation if desired.
css
.magnifier {
position: relative;
}
.magnifier__glass {
position: absolute;
top: -4rem;
left: -4rem;
transform: translate(0, 0);

width: 8rem;
height: 8rem;
}
To keep the magnifier glass element within its parent container, we use `overflow: hidden`. This is necessary because the glass element has absolute positioning, which means it can go beyond its parent's boundaries. By setting `overflow: hidden`, we ensure that any overflow is clipped and the glass stays within its parent, invisible outside of it.
css
.magnifier {
overflow: hidden;
}
To create a circular appearance for the glass element, we can use CSS to add a `border-radius` property. By setting the value of `border-radius` to 50%, the corners of the element will be rounded until they meet in the center, forming a perfect circle.
css
.magnifier__glass {
border-radius: 50%;
}
When users see a magnified part of an image, they need to know what they're looking at. That's where the border style of the glass element comes in. But sometimes, it can be hard to spot the glass element because of its transparent background. To make it stand out and emphasize its purpose, we can add a border around it. We can even customize the width and color of the border to match our website's design aesthetic. Depending on what we need, we can choose a thicker border for a more prominent effect or a lighter color to blend in better with the rest of the page.
css
.magnifier__glass {
border: 0.25rem solid rgb(203 213 225 / 0.5);
}
In order to help users understand how to zoom in on any part of an image using the magnifying glass, we'll create a plus sign at the center of the glass element. We can do this without using an actual element by using two pseudo-elements, `::before` and `::after`.
To create the plus sign, we position these elements in the middle of the glass using absolute positioning and then adjust their height and width. For example, to create the horizontal line of the plus sign, we use the `::after` element and set its height to `0.125rem` and its width to `2rem`.
To position the horizontal line in the center of the magnifying glass, we use absolute positioning and set `top: 50%;` and `left: 50%;`. Then, we translate it upwards by half of its height and horizontally by half of its width using `transform: translate(-50%, -50%);`. This centers the horizontal line perfectly within the magnifying glass.
css
.magnifier__glass::after {
background: rgb(203 213 225 / 0.5);

content: '';
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);

height: 0.125rem;
width: 2rem;
}
We can apply the same method to create the vertical line by utilizing the `::before` element.
css
.magnifier__glass::before {
background: rgb(203 213 225 / 0.5);

content: '';
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);

height: 2rem;
width: 0.125rem;
}

Determining the initial size of the image

When it comes to magnifying an image, the original image can come in various sizes. That's why we need to fit it within a container element. To do this, we use an internal state with two properties, `imageWidth` and `imageHeight`. These properties store the width and height of the image that is being magnified. We initially set these values to 0 because we don't know the size of the image until it has finished loading.
ts
const [{ imageWidth, imageHeight }, setImageSize] = React.useState({
imageWidth: 0,
imageHeight: 0,
});
After the image has finished loading, we can update some state values to reflect its actual dimensions. To accomplish this, we use the `handleImageLoad` function to handle the `onLoad` event of the image.
When the `handleImageLoad` function is called, it first retrieves the container element using a reference. Then, it gets the natural width and height of the loaded image using `e.target.naturalWidth` and `e.target.naturalHeight`.
Using this information, the function calculates the aspect ratio of the image by dividing its width by its height. With this data, we can calculate the height of the image based on the width of its container.
Finally, we update the state with the new dimensions using `setImageSize({ imageWidth, imageHeight })`. This triggers a re-render, which allows us to apply updated styles to our HTML elements based on their new dimensions.
tsx
const handleImageLoad = (e) => {
const container = containerRef.current;
const naturalWidth = e.target.naturalWidth;
const naturalHeight = e.target.naturalHeight;
const ratio = naturalWidth / naturalHeight;

const imageWidth = container.getBoundingClientRect().width;
const imageHeight = imageWidth / ratio;

setImageSize({ imageWidth, imageHeight });
};
By calculating these values dynamically based on the size of our loaded image, we can make sure that our magnifier container and its contents are always accurately sized and positioned, no matter what changes occur in viewport size or device resolution.
To achieve this, we can rely on the `imageWidth` and `imageHeight` state variables to automatically update the styles of both the image and container elements.
tsx
<div
className="magnifier"
ref={containerRef}
style={{
width: imageWidth === 0 ? "" : `${imageWidth}px`,
height: imageHeight === 0 ? "" : `${imageHeight}px`,
}}
>
<img
src="..."
onLoad={handleImageLoad}
style={{
width: imageWidth === 0 ? "" : `${imageWidth}px`,
}}
/>
</div>

Making the magnifier glass movable

To allow users to zoom in on specific portions of the image, they need to be able to move the magnifier glass around. We can make this possible by using the `useDraggable` hook we created earlier. This hook not only makes the glass draggable but also ensures that it stays within its container, preventing users from dragging it outside of the container.
To use this custom hook, you only need to call it, and it will return an array of three items.
ts
const [glassRef, dx, dy] = useDraggable();
The first one is a reference to the element we want to make draggable. We can attach this reference to the target element using the `ref` attribute.
The second and third items are the horizontal and vertical distances that indicate how far the element should be moved. We can use these values to update the position of the glass element using the `transform` property.
tsx
<div
className="magnifier__glass"
ref={glassRef}
style={{
transform: `translate3d(${dx}px, ${dy}px, 0)`,
}}
/>
Now, let's check out the first demo. You can easily move the glass around, but it will only budge within the container. Give it a try and see for yourself!

Zooming in on a selected portion of the image

We've successfully made the glass draggable. However, as users move the glass around, we need to zoom in on the desired portion of the image. For this purpose, we'll assume that we'll zoom in the original image three times.
Currently, we have created a hard-coded constant for this zoom level. However, it would be better to turn it into a configurable aspect of the component. This would allow for greater flexibility and customization in the future.
ts
const ZOOM = 3;
To achieve the zooming effect, we use a background image for the magnifying glass element. This background image is set to the same source as the original image. We scale up the size of the background image by a factor of `ZOOM` to ensure that it is large enough to fill the magnifier container when zoomed in.
tsx
<div
className="magnifier__glass"
style={{
backgroundImage: `url(${imageUrl})`,
backgroundRepeat: 'no-repeat',
backgroundSize: `${ZOOM * imageWidth}px ${ZOOM * imageHeight}px`,
}}
/>
To position the background image inside the magnifying glass element, we use the `backgroundPosition` property with two values: horizontal and vertical positions of the image.
To calculate these values, we take the distance we've dragged the glass element and multiply it by our `ZOOM` level. Then, we can use a template literal to update our CSS styles in real-time based on how far we've dragged the glass element.
tsx
<div
className="magnifier__glass"
style={{
backgroundPosition: `${-dx * ZOOM}px ${-dy * ZOOM}px`,
}}
/>
This setup allows users to drag a glass over the original image and view a magnified version of the area under the glass in real-time. This feature is particularly useful for examining intricate details within artwork or photographs.
Take a look at the final demo below to see how it works.

Conclusion

In conclusion, using React to create an image magnifier is an excellent way to improve your website's user experience. By allowing users to zoom in on specific areas of an image, you can provide them with a more detailed and engaging view of your content.
With the use of custom hooks, internal state management, and CSS styling, we can create a fully functional magnifying glass that is both responsive and user-friendly. Thanks to React's component-based architecture, building and maintaining this feature as part of any larger project is a breeze.
Overall, creating an image magnifier in React is a fun and rewarding experience. It lets us take advantage of the framework's strengths while delivering a unique and engaging user experience for our visitors.
If you found this post helpful, please consider giving the repository a star on GitHub or sharing the post on your favorite social networks 😍. Your support would mean a lot to me!

Questions? 🙋

Do you have any questions about front-end development? If so, feel free to create a new issue on GitHub using the button below. I'm happy to help with any topic you'd like to learn more about, even beyond what's covered in this post.
While I have a long list of upcoming topics, I'm always eager to prioritize your questions and ideas for future content. Let's learn and grow together! Sharing knowledge is the best way to elevate ourselves 🥷.
Ask me questions

Recent posts ⚡

Newsletter 🔔

If you're into front-end technologies and you want to see more of the content I'm creating, then you might want to consider subscribing to my newsletter.
By subscribing, you'll be the first to know about new articles, products, and exclusive promotions.
Don't worry, I won't spam you. And if you ever change your mind, you can unsubscribe at any time.
Phước Nguyễn