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

  • JavaScript редактор SVG диаграмм который весит в 6,5 раз меньше bootstrap (библиотека с открытым исходным кодом)

  • JavaScript редактор диаграмм, который открывает диаграммы из PNG картинок (open source)

  • JavaScript редактор текста для SVG

  • UI редактора блок-схем

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

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

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

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

Источник: https://habr.com/ru/post/696266/


Интересные статьи

Интересные статьи

Настройка любой площадки для CMS — это рутинный процесс, который должен быть доведен до автоматизма в каждой уважающей себя компании. А потому частенько воспринимается, как восход солнца — это происхо...
За окном 2020 год. Удалённая работа — это уже совершенно нормально. Десятилетия рекламы ведущих телекоммуникационных компаний наконец сделали своё дело — научили мою маму совершать ви...
Всем привет. Когда я искал информацию о журналировании (аудите событий) в Bitrix, на Хабре не было ни чего, в остальном рунете кое что было, но кто же там найдёт? Для пополнения базы знаний...
Ранее в одном из наших КП добавление задач обрабатывалось бизнес-процессами, сейчас задач стало столько, что бизнес-процессы стали неуместны, и понадобился инструмент для массовой заливки задач на КП.
В 1С Битрикс есть специальные сущности под названием “Информационные блоки, сокращенно (инфоблоки)“, я думаю каждый с ними знаком, но не каждый понимает, что это такое и для чего они нужны