I am writing an image viewer JS component and i'm currently stuck on the calculation for the zooming in. I figured out the zooming in or out on one point (instead of just the center) using css transforms. I'm not using transform-origin because there will be multiple points of zooming in or out, and the position must reflect that. However, that's exactly where i'm stuck. The algorithm i'm using doesn't calculate the offset correctly after trying to zoom in (or out) when the mouse has actually moved from the initial position and I can't for the life of me figure out what's missing in the equation.
The goal is whenever the mouse is moved to another position, the whole image should scale with that position as the origin, meaning the image should scale but the point the mouse was over should remain in its place. For reference, look at this JS component. Load an image, zoom in somewhere, move your mouse to another position and zoom in again.
Here's a pen that demonstrates the issue in my code: https://codepen.io/Emberfire/pen/jOWrVKB
I'd really apreciate it if someone has a clue as to what i'm missing :)
Here's the HTML
<div class="container">
<div class="wrapper">
<div class="image-wrapper">
<img class="viewer-img" src="https://cdn.pixabay.com/photo/2020/06/08/17/54/rain-5275506_960_720.jpg" draggable="false" />
</div>
</div>
</div>
Here's the css that i'm using for the component:
* {
box-sizing: border-box;
}
html, body {
width: 100%;
height: 100%;
margin: 0;
}
.container {
width: 100%;
height: 100%;
}
.wrapper {
width: 100%;
height: 100%;
overflow: hidden;
transition: all .1s;
position: relative;
}
.image-wrapper {
position: absolute;
}
.wrapper img {
position: absolute;
transform-origin: top left;
}
And here's the script that calculates the position and scale:
let wrapper = document.querySelector(".wrapper");
let image = wrapper.querySelector(".image-wrapper");
let scale = 1;
image.querySelector("img").addEventListener("wheel", (e) => {
if (e.deltaY < 0) {
scale = Number((scale * 1.2).toFixed(2));
let scaledX = e.offsetX * scale;
let scaledY = e.offsetY * scale;
imageOffsetX = e.offsetX - scaledX;
imageOffsetY = e.offsetY - scaledY;
e.target.style.transform = `scale3d(${scale}, ${scale}, ${scale})`;
image.style.transform = `translate(${imageOffsetX.toFixed(2)}px, ${imageOffsetY.toFixed(2)}px)`;
} else {
scale = Number((scale / 1.2).toFixed(2));
let scaledX = e.offsetX * scale;
let scaledY = e.offsetY * scale;
imageOffsetX = e.offsetX - scaledX;
imageOffsetY = e.offsetY - scaledY;
e.target.style.transform = `scale3d(${scale}, ${scale}, ${scale})`;
image.style.transform = `translate(${imageOffsetX}px, ${imageOffsetY}px)`;
}
});