Zoom + Pan + Clamp image preview with vanilla javascript.
I recently needed to create a zoom + pan image preview, which should stick to a container for marko.ch. You can click on the squared image to open a dialog and see the result.
For the code, see this stackblitz.
It’s a simplified version of the complete setup I made for Marko. If you want the full gallery experience, let me know, and I’ll do a comprehensive write-up or finally film a little series.
References:
Most (about 90%) of the renderer code was inspired by this post:
Kacper Wdowik: Implementing Zoom and Pan in Just 69 Lines of Javascript.
I’ve adapted it into TypeScript and added clamping.
Then, there’s this tutorial by Sam Selikoff: Pan and Pinch to Zoom with React Use Gesture.
Renderer
The core of rendering the image within the container is within the `renderer.ts`. This file solely focuses on calculating the style for the image from provided values.
The math is mostly explained in Sam Selikoff’s tutorial, but I’ve used `transformOrigin` to calculate the offset for zoom/pinch zoom to the point of origin.
Note: This behaves oddly while the image is smaller than the container, but it works perfectly afterward.
I spent a lot of time figuring out the correct math for this and even wrote a simple tool to see if my assumptions were correct. 😀
Preview
The `preview.ts` is responsible for adding event listeners and providing the correct information to the renderer.
Note: I specifically used `MouseEvents` and `TouchEvents` rather than `PointerEvents`. The main reason is that iOS Safari only recently started supporting these events. The other reason is that I wanted perfect control over the gestures. There are many scripts/packages out there dealing with these, but I find them too opaque and unreliable. Using native event listeners for capturing gestures isn’t too difficult and offers better control.
Summary
I hope this demonstrates that seemingly challenging tasks, like implementing pinch zoom and pan functionality, can be achieved using just vanilla JavaScript and some HTML/CSS, without any external dependencies.
Thank you, and have a great day!