
Как сделан zoom в редакторе блок-схем dgrm.net.
Zoom-ить можно:
колесиком мышки,
touchpad-ом
и двумя пальцами на телефонах и планшетах.
Готовая функция zoom-а SVG для ваших проектов прилагается. Для HTML можно переделать.
Алгоритм zoom-а
В SVG и HTML есть масштабирование. Масштабирование в HTML.
Только менять масштаб не достаточно. Изображение будет уезжать.
При увеличении круг в центре экрана съезжает вниз вправо.


Черный прямоугольник это экран. Синий это увеличенное изображение. Увеличенный круг находится в центре увеличенного изображения, но съехал относительно экрана.
Нужно увеличивать и сдвигать, тогда центр изображения не уедет.

На рисунке 4 центр изображения не съезжает. Но карты работают не так. Карты зумятся не в центр. Карты зумятся относительно курсора. Место, куда указывает курсор, не сдвигается относительно экрана.
Здание в центре карты уезжает вниз. Оно выделено красным. Здание под курсором остается на месте. Выделено синим.

Функция зума должна сдвигать изображение так, чтобы точка под курсором оставалась на месте.
/** * @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.
