JavaScript: Zoom like in maps for SVG/HTML
Tags: javascript, webdev, programming, opensource
Published: 2022-11-01 15:02:48


![Fig 1. Zoom in dgrm.net flowchart editor](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mt13r83m0l6g39cvx03c.gif) _Fig 1. Zoom in dgrm.net flowchart editor_

[dgrm.net](https://dgrm.net/) | [GitHub](https://github.com/AlexeyBoiko/DgrmJS)

How zoom is done in the [dgrm.net](https://dgrm.net/) flowchart editor.
You can zooming with

- mouse wheel,
- touchpad
- and two fingers on phones and tablets.

A ready-made SVG zoom function for your projects is included.

## Zoom algorithm
SVG and HTML have scaling. [Scaling in HTML](https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function/scale).

Just changing the scale is not enough. The image will slide away.
When zoomed in, the circle in the center of the screen slides down to the right.


![Fig 2. Wrong: when zoomed in, the circle in the center of the screen slides down to the right.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8hdp5t849i5vru4rrlnl.gif) _Fig 2. Wrong: when zoomed in, the circle in the center of the screen slides down to the right._

  
![Fig 3. When zoomed in, the circle in the center of the screen slides down to the right — scheme.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/97nm2jfelq5rfauydyro.png) _Fig 3. When zoomed in, the circle in the center of the screen slides down to the right — scheme._

The black rectangle is the screen. Blue is an enlarged image.
The enlarged circle is in the center of the enlarged image, but has slid away relative to the screen.

It is necessary to scale and shift, then the center of the image will not leave.


![Fig 4. Zoom in and shift up to the left. The circle remains in the center of the screen.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7flyq2bzt7usfnp5qway.png) _Fig 4. Zoom in and shift up to the left. The circle remains in the center of the screen._

In figure 4, the center of the image does not slide away.
But that’s not how maps work. Maps are not zooming to the center. The maps are zoomed in relative to the cursor. The place where the cursor points does not move relative to the screen.

The building in the center of the map goes down. It is highlighted in red. The building under the cursor remains in place. Highlighted in blue.


![Fig 5. The map is not zooming to the center, but relative to the cursor. The place where the cursor points does not move relative to the screen.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qjftig4ccf6jm8864p07.gif) _Fig 5. The map is not zooming to the center, but relative to the cursor. The place where the cursor points does not move relative to the screen._

The zoom function should move the image so that the point under the cursor remains in place.

```js
/**
 * @param {SVGGraphicsElement} svgEl
 * @param {Point} fixedPoint
 *                this point will not change position while scale
 * @param {number} scale
 * @param {number} nextScale
 */
export function svgScale(svgEl, fixedPoint, scale, nextScale) {
    const position = svgPositionGet(svgEl);
 
    svgPositionSet(svgEl, {
        x: nextScale / scale * (position.x - fixedPoint.x) +     
           fixedPoint.x,
        y: nextScale / scale * (position.y - fixedPoint.y) + 
           fixedPoint.y
    });
 
    ensureTransform(svgEl, SVGTransform.SVG_TRANSFORM_SCALE)
        .setScale(nextScale, nextScale);
}
```
_Listing 1. Zoom function. Shifts the image so that the fixedPoint stays in place.
Helper functions svgPositionGet, svgPositionSet, ensureTransform are available on [GitHub](https://github.com/AlexeyBoiko/DgrmJS/blob/main/src/diagram/infrastructure/svg-utils.js)._

## Zoom with mouse wheel and touchpad
Subscribe to the mouse wheel event “wheel”. There is no separate event for pinching with two fingers on the touchpad. The pinch uses the same “wheel” event.

For the wheel, the scale changes in increments of 0.25, and for the touchpad 0.05. The values are chosen so that:

- the mouse wheel did not need to be twisted for a long time
- and on the touchpad the image did not jump

```js
// 'svg' is type of {SVGSVGElement}
let scale = 1;
// mouse wheel, trackpad pitch
svg.addEventListener('wheel', /** @param {WheelEvent} evt */ evt => {
    evt.preventDefault();
 
 
    // calc nextScale
 
    const delta = evt.deltaY || evt.deltaX;
    const scaleStep = Math.abs(delta) < 50
        ? 0.05  // touchpad pitch
        : 0.25; // mouse wheel
 
    const scaleDelta = delta < 0 ? scaleStep : -scaleStep;
    const nextScale = scale + scaleDelta; // 'scale' is prev scale
 
 
    // calc fixedPoint
    const fixedPoint = { x: evt.clientX, y: evt.clientY };
 
 
    // scale
    // 'svgEl' is element to scale
    svgScale(svgEl, fixedPoint, scale, nextScale);
    scale = nextScale;
});
```
_Listing 2. Subscribe to the mouse wheel event. A touchpad pinch fires the same event. See the full code on [GitHub](https://github.com/AlexeyBoiko/DgrmJS/blob/main/src/diagram-extensions/scale-feature.js)._

## Zoom with two fingers on phones and tablets
For finger zoom, the fixed point is the midway point between the fingers. The change in scale depends on the change in the distance between the fingers.

You also need to consider that the image can be zoomed and moved at the same time.

```js
// calc nextScale
 
// distance between fingers
const distanceNew = Math.hypot(
    firstFinger.x - secondFinger.x,
    firstFinger.y - secondFinger.y);
 
// 'distance' is previous distance between fingers
// 'scale' is previous scale
const nextScale = scale / distance * distanceNew;
 
 
// calc fixedPoint
const fixedPoint = {
    x: (firstFinger.x + secondFinger.x) / 2,
    y: (firstFinger.y + secondFinger.y) / 2
};
 
 
// scale
// 'svgEl' is element to scale
svgScale(svgEl, fixedPoint, scale, nextScale);
scale = nextScale;
 
// don't forget to also move the canvas behind your fingers
```
_Listing 3. For finger zoom, the fixed point is the midway point between the fingers. The change in scale depends on the change in the distance between the fingers. See the full code on [GitHub](https://github.com/AlexeyBoiko/DgrmJS/blob/main/src/diagram-extensions/scale-feature.js)._

## Other articles about dgrm.net
- [JavaScript SVG diagram editor 3.9 KB (open source library)](https://dev.to/alexboyko/javascript-svg-diagram-editor-39-kb-open-source-library-2eh)
- [JavaScript diagram editor that renders diagrams from PNG images (open source)](https://dev.to/alexboyko/javascript-diagram-editor-that-renders-diagrams-from-png-images-open-source-a63)
- [JavaScript text editor for SVG](https://dev.to/alexboyko/javascript-text-editor-for-svg-13aa)
- [Flowchart editor UI](https://dev.to/alexboyko/flowchart-editor-ui-58a1)

## How to support the project

- Start using the flowchart editor [dgrm.net](https://dgrm.net/).  
Tell me what you think. Comments, private messages, on [GitHub](https://github.com/AlexeyBoiko/DgrmJS).  
I read everything, I keep a list of proposals.
- Tell your friends.
- Give a star on [GitHub](https://github.com/AlexeyBoiko/DgrmJS).