Pull to refresh

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

Reading time3 min
Views9.3K
Рис 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}

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.

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

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

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

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

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

Tags:
Hubs:
Total votes 9: ↑9 and ↓0+9
Comments7

Articles