Home > Software engineering >  Centering on a specific point when zooming using SVG viewBox with arbitrary initial state
Centering on a specific point when zooming using SVG viewBox with arbitrary initial state

Time:10-17

I have a SVG HTML element and I have implemented panning and zooming into it using the mouse. The current implementation of the zooming functionality just multiplies the original width and height of the element by a number that changes when the user scrolls the mouse.

This implementation preserves the origin (0,0) and all other points appear to move closer/further away from it depending on the direction of the zoom.

Intuitively and based o coordinate changes when scaling a viewBox

the offset (min-x and min-y; drawn green) of a viewBox is abbsolute and does not depend on the width and height of the viewBox. The mouse coordinates relative to the SVG element (coordinates drawn in black, SVG element drawn in red) are relative to the size of the viewBox. If I enlarge the viewBox, then the part of the picture I can see inside of it shrinks and 100px line drawn by the mouse will cover more of the image.

If we set the size of the dimensions of the viewBox to be the same as the size of the SVG element (initial state), we have a 1:1 scale between the image and the viewBox (the red rectangle would cover the entire image, bordered black). When we make the viewBox smaller we will not fit the entire image into it and therefore the image will appear to be larger.

If we want to compute the absolute position of our mouse in relation to the entire image we can do it like this (same for Y): position = offsetX zoomFactor * mouseX (mouseX relative to the SVG element).

When we zoom, we change the factor, but don't change the position of the mouse. If we want the absolute position under the mouse to remain the same, we have to solve the following set of equations:

oldPosition = oldOffsetX oldZoomFactor * mouseX

newPosition = newOffsetX newZoomFactor * mouseX

oldPosition = newPosition

we know the mouse position, both zoom factors and the old offset, therefore we solve for the new offset and get:

newOffsetX = oldOffsetX mouseX * (oldZoomFactor - newZoomFactor)

which is the final formula and very similar to this answer.

Put together we get the final working solution:

processMouseScroll(event: WheelEvent) {
    const oldZoomFactor = zoomFactor(this.scroll);
    const newZoomFactor = zoomFactor(this.scroll   event.deltaY);

    // mouse position relative to the SVG element
    const mouseX = event.pageX - (event.target as SVGElement).getBoundingClientRect().x;
    const mouseY = event.pageY - (event.target as SVGElement).getBoundingClientRect().y;

    this.scroll = this.scroll   event.deltaY;
    this.offsetX = this.offsetX   mouseX * (oldZoomFactor - newZoomFactor);
    this.offsetY = this.offsetY   mouseY * (oldZoomFactor - newZoomFactor);
}
  • Related