I’m trying to build a magnifying glass effect. This is where, when you move the cursor over an image, a magnified (ie larger) part of the image underneath the cursor is shown. I’ve gotten close with my coding. An example of what I have is this:
let zoom = 2;
window.addEventListener('load', () => {
const image = document.querySelector('.image'),
{ width, height } = image.getBoundingClientRect(),
glass = document.createElement('div');
document.querySelector('.image-wrapper').append(glass);
glass.classList.add('glass');
glass.style.backgroundSize = `${zoom * width}px ${zoom * height}px`;
glass.style.backgroundImage = `url(${image.src})`;
image.addEventListener('mousemove', (e) => {
const { offsetX, offsetY } = e,
glassX = offsetX - glass.offsetWidth / 2,
glassY = offsetY - glass.offsetHeight / 2;
glass.style.left = `${glassX}px`;
glass.style.top = `${glassY}px`;
glass.style.backgroundPosition = `-${offsetX * zoom}px -${offsetY * zoom}px`;
});
});
body {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background-color: #f0f0f0;
}
.image-wrapper {
position: relative;
box-sizing: border-box;
border: 1px dashed gray;
}
.image {
width: 300px;
height: auto;
cursor: none;
}
.image-wrapper .glass {
opacity: 0;
}
.image-wrapper:hover .glass {
opacity: 1;
}
.glass {
position: absolute;
width: 100px;
height: 100px;
border-radius: 50%;
cursor: none;
background-repeat: no-repeat;
pointer-events: none;
transition: opacity 0.2s;
border: 1px solid black;
}
<div class="image-wrapper">
<img src="https://i.sstatic.net/JzS3fR2C.jpg" alt="Sample Image" style="opacity: 0.1;" class="image">
</div>
While the magnifying glass mostly works it "fails" around the edges. When the cursor gets to the edge of the image I want the glass to show a "semi circle" of the image with the other semi being white. That is I want this:
How to fix this so that it behaves itself around all 4 edges?
4
Answers
After some gnashing of teeth I got it to work by changing the mousemove event's code to:
The issue you’re seeing is that the background of
glass
isn’t aligned correctly.The
glass
div is positioned from its top-left corner. Your code corrects for this by subtracting half ofglass
‘s width and height when calculating its new position:However, the
background-position
is also positioned relative to the top-left corner ofglass
. If the top-left corner were at the same position as the cursor, this would work great. But remember that you correctedglass
‘s position above so the cursor would be at its center. Therefore, you’ll need to move thebackground-position
"back" to where it would be.So when you’re calculating
background-positon
:you need to add
glass.offsetWidth / 2
andglass.offsetHeight / 2
back in:But wait! The expressions you’re using in your template string could produce negative numbers when near the top or left edges of the image! Since you included a
-
in the template string, this would generate something like--13.2px
…, which isn’t valid CSS.This is a simple fix, however. Just move the negation inside the expression, like so:
Now we won’t generate invalid CSS.
Working snippet:
Page layout note: most of the calculations that you’re currently doing in JS in order to calculate layout positioning and sizing can be done purely in CSS using variables and
calc()
expressions, requiring drastically less JS while also running faster.Working in your own solution, but implement in CSS rather than JS:
Based on your approach, I made two tweaks to fix the issue:
glassSize
property so you could use it in both CSS and JavaScript calculations:.glass
.You want the offset of the background position to be offset by half of the
glassSize
, so the edges of the image can be offset correctly.Notice it’s better to put the negative sign inside of calculation:
${-(x)}
instead of-${x}
, so whenx
became a negative value (e.g.-42
), the-${x}
won’t become something like--42
which is not going be interpreted correctly.