![Рис 1. Zoom в редакторе блок-схем dgrm.net Рис 1. Zoom в редакторе блок-схем dgrm.net](https://habrastorage.org/getpro/habr/upload_files/0d9/b18/dd4/0d9b18dd46bcd39ed3d141f9ff3cd120.gif)
Как сделан zoom в редакторе блок-схем dgrm.net.
Zoom-ить можно:
колесиком мышки,
touchpad-ом
и двумя пальцами на телефонах и планшетах.
Готовая функция zoom-а SVG для ваших проектов прилагается. Для HTML можно переделать.
Алгоритм zoom-а
В SVG и HTML есть масштабирование. Масштабирование в HTML.
Только менять масштаб не достаточно. Изображение будет уезжать.
При увеличении круг в центре экрана съезжает вниз вправо.
![Рис 2. Не правильно: при увеличении круг в центре экрана съезжает вниз вправо. Рис 2. Не правильно: при увеличении круг в центре экрана съезжает вниз вправо.](https://habrastorage.org/getpro/habr/upload_files/f1f/f94/df6/f1ff94df667c1671c38b629ba74ccdce.gif)
![Рис 3. При увеличении круг в центре экрана съезжает вниз вправо - схема. Рис 3. При увеличении круг в центре экрана съезжает вниз вправо - схема.](https://habrastorage.org/getpro/habr/upload_files/f08/d72/f3a/f08d72f3ae163568da73a96e69c7baf7.png)
Черный прямоугольник это экран. Синий это увеличенное изображение. Увеличенный круг находится в центре увеличенного изображения, но съехал относительно экрана.
Нужно увеличивать и сдвигать, тогда центр изображения не уедет.
![Рис 4. Увеличиваем и сдвигаем вверх влево. Круг остается в центре экрана. Рис 4. Увеличиваем и сдвигаем вверх влево. Круг остается в центре экрана.](https://habrastorage.org/getpro/habr/upload_files/5b2/da8/53a/5b2da853a76d05b1b8ff0bb454dc208c.png)
На рисунке 4 центр изображения не съезжает. Но карты работают не так. Карты зумятся не в центр. Карты зумятся относительно курсора. Место, куда указывает курсор, не сдвигается относительно экрана.
Здание в центре карты уезжает вниз. Оно выделено красным. Здание под курсором остается на месте. Выделено синим.
![Рис 5. Карта зумятся не в центр, а относительно курсора. Место, куда указывает курсор, не сдвигается относительно экрана. Рис 5. Карта зумятся не в центр, а относительно курсора. Место, куда указывает курсор, не сдвигается относительно экрана.](https://habrastorage.org/getpro/habr/upload_files/7d4/860/999/7d48609994fb48fe3ddcf4e0221d19aa.gif)
Функция зума должна сдвигать изображение так, чтобы точка под курсором оставалась на месте.
/**
* @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);
}
Листинг 1. Функция зума. Сдвигает изображение так, что fixedPoint остается на месте.
Вспомогательные функции svgPositionGet, svgPositionSet, ensureTransform смотрите на GitHub.
Zoom колесиком мышки и touchpad-ом
Подписываемся на событие колесика мышки “wheel”. Для щипка двумя пальцами на touchpad отдельного события нет. Щипок использует это же событие “wheel”.
Для колесика масштаб изменяется с шагом 0.25, а для touchpad 0.05. Значения подобраны так чтобы:
колесико мышки не нужно было долго крутить,
а на touchpad изображение не скакало.
// '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 previous scale
// calc fixedPoint
const fixedPoint = { x: evt.clientX, y: evt.clientY };
// scale
// 'svgEl' is element to scale
svgScale(svgEl, fixedPoint, scale, nextScale);
scale = nextScale;
});
Листинг 2. Подписываемся на событие колесика мышки. Щипок touchpad запускает это же событие. Полный код смотрите на GitHub.
Zoom двумя пальцам на телефонах и планшетах
Для zoom-а пальцами фиксированная точка - это точка посередине между пальцами. Изменение масштаба зависит от изменения расстояния между пальцами.
Еще нужно учитывать что изображение можно одновременно зумить и двигать.
// 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);
// don't forget to also move the canvas behind your fingers
Листинг 3. При zoom-е пальцами фиксированная точка это точка посередине между пальцами. Изменение масштаба зависит от изменения расстояния между пальцами. Полный код смотрите на GitHub.