JavaScript: Zoom как в картах для SVG/HTML

от автора

Рис 1. Zoom в редакторе блок-схем dgrm.net
Рис 1. Zoom в редакторе блок-схем dgrm.net

dgrm.net | GitHub

Как сделан zoom в редакторе блок-схем dgrm.net.
Zoom-ить можно:

  • колесиком мышки,

  • touchpad-ом

  • и двумя пальцами на телефонах и планшетах.

Готовая функция zoom-а SVG для ваших проектов прилагается. Для HTML можно переделать.

Алгоритм zoom-а

В SVG и HTML есть масштабирование. Масштабирование в HTML.

Только менять масштаб не достаточно. Изображение будет уезжать.
При увеличении круг в центре экрана съезжает вниз вправо.

Рис 2. Не правильно: при увеличении круг в центре экрана съезжает вниз вправо.
Рис 2. Не правильно: при увеличении круг в центре экрана съезжает вниз вправо.
Рис 3. При увеличении круг в центре экрана съезжает вниз вправо - схема.
Рис 3. При увеличении круг в центре экрана съезжает вниз вправо — схема.

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

Нужно увеличивать и сдвигать, тогда центр изображения не уедет.

Рис 4. Увеличиваем и сдвигаем вверх влево. Круг остается в центре экрана.
Рис 4. Увеличиваем и сдвигаем вверх влево. Круг остается в центре экрана.

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

Здание в центре карты уезжает вниз. Оно выделено красным. Здание под курсором остается на месте. Выделено синим.

Рис 5. Карта зумятся не в центр, а относительно курсора. Место, куда указывает курсор, не сдвигается относительно экрана.
Рис 5. Карта зумятся не в центр, а относительно курсора. Место, куда указывает курсор, не сдвигается относительно экрана.

Значит функция зума должна сдвигать изображение так, чтобы точка под курсором оставалась на месте.

/**  * @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}   // 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); });

Листинг 2. Подписываемся на событие колесика мышки. Щипок touchpad запускает это же событие. Полный код смотрите на GitHub.

Zoom двумя пальцам на телефонах и планшетах

Для zoom-а пальцами фиксированная точка — это точка посередине между пальцами. Изменение масштаба зависит от изменения расстояния между пальцами.

Еще нужно учитывать что изображение можно одновременно зумить и двигать.

// calc nextScale   // distance between fingers const distanceNew = Math.hypot(     firstFinger.x - secondFinger.x,     firstFinger.y - secondFinger.y);   // 'distance' variable is previous distance between fingers const scaleDelta = (distanceNew - distance) * 0.01; const nextScale = scale + scaleDelta; // 'scale' is previous scale     // 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.

Другие статьи про dgrm.net

Как поддержать проект

  • Начните использовать, расскажите что думаете.
    Любым способом: комментарии, личные сообщения, на GitHub.
    Все читаю, веду список предложений.

  • Расскажите знакомым.

  • Ставьте звездочки на GitHub.


ссылка на оригинал статьи https://habr.com/ru/post/696266/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *