mathsvgslidercoordinateszooming

# Calculating svg transform values for zooming-in and dragging an image

We have available svg area of 1300x620px, wanting to vertically and horizontally center 300x500px image with ability to zoom in and out, with default zoom level of 3.

We start by calculating the image size by dividing the available area size by the default zoom level, ending with 433x207px image, which we wrap in a `<g>` tag with `transform="translate(0,0) scale(3)"` and the result looks like that:

The question is, how to calculate the proper x and y here `translate(x, y) scale(3.5)` so that the image is still centered.

The second question is, how to calculate minimum and maximum x,y coordinates of the image.

The idea is to implement sliders for the x and y axis and 2 buttons to zoom-in / zoom-out which will modify the `transform()` values for the slider functionality, but we need to know the min-max values on each axis as well as to properly transform the image so that it keeps to be centered no matter of the current transform applied.

Solution

• I'll list the principal calculation in pseudocode first, and than demonstrate its functioning graphically. Finally we'll sum up things.

### Calculus

``````// SVG viewport size
set viewWidth = 1300px;
set viewHeight = 620px;

// initial scale factor and according image size
set initScale = 3;
set imgWidth = viewWidth / initScale;
set imgHeight = viewHeight / initScale;
``````

To adjust the position of your image we have to apply a factor to the image size:

``````set newScale = 3.5;
set adjustFactor = 0.5 * (1 - newScale / initScale);
scale(newScale);
``````

When zooming in, e.g. with `newScale = 3.5`, we get (rounded):

``````scale(3.5);
translate(-36, -17);
``````

Now lets zoom out (rounded again):

``````scale(2.5);
translate(36, 17);
``````

That behavior is to be expected. To center we have to left-/up-shift when zooming in, and right-/down-shift when zooming out.

### Scheming

Fig.1 illustrates what is done here. We have a grayisch square within a `10 x 6 px` "canvas". We have a frame of the same size, which remains unchanged and shall always show the square centered, no matter how we scale the canvas (together with the square). Now, when scaling up the canvas without an adjusting translation, the canvas slips out of the frame, and we see only the top-left corner of the square. Using the above formula and the resulting adjustment factor of `0.5` we push the canvas back by `-5px` along the x-axis and `-3px` along the y-axis till the square is centered within the frame again.

Fig.1 Adjusting the position of an image slippen out of the frame by scaling, to be shown centered in that frame.

The coordinates are correspondingly:

``````minX = adjustFactor * imgWidth;
maxX = minX + imgWidth * (newScale / initScale);

maxY = minY + imgHeight * (newScale / initScale);
``````

The max-Values are basically min-Value plus newly scaled width resp. height.

### Crunching

Note that

``````maxX = minX + imgWidth * (newScale / initScale)
= adjustFactor * imgWidth + imgWidth * (newScale / initScale)
= imgWidth * (adjustFactor + newScale / initScale)
= imgWidth * (0.5 * (1 - newScale / initScale) + newScale / initScale)
= imgWidth * (0.5 - 0.5 * newScale / initScale + newScale / initScale)
= imgWidth * (0.5 + 0.5 * newScale / initScale)
= imgWidth * 0.5 * (1 + newScale / initScale)
``````

Remembers of something, doesn't it?

``````adjustFactor = 0.5 * (1 - newScale / initScale);
``````

It's just a plus instead of a minus. The difference between `0.5 * (1 + newScale / initScale)` and `0.5 * (1 - newScale / initScale)` is `newScale / initScale`. We may want to consider this, as divisions are costly concerning performance. The less the better.

### Mopping Up

So in total we get:

``````set a = 0.5 * (1 - newScale / initScale); // our former "adjustFactor"
set b = a + newScale / initScale;

set minX = a * imgWidth;
set maxX = b * imgWidth;

set minY = a * imgHeight;
set maxY = b * imgHeight;

scale(newScale);
translate(minX, minY);  // looks sane, right?
``````

You can use the min/max-values for scrolling checks. It might make sense to turn off scrolling for an axis when the respective min-value is `>= 0`.