DEV Community

Cover image for Zoom + Pan, clamped to a container
Mykolas Mankevicius
Mykolas Mankevicius

Posted on

Zoom + Pan, clamped to a container

I recently needed to create a zoom + pan image preview, which should stick to a container for 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.


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.


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. 😀


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.


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!

Top comments (0)