Pinch - масштабирование щипком

Для работы с жестом pinch (масштабирование двумя пальцами) необходимо подключить библиотеку Interact.js через CDN или npm. Элементы, к которым будет применяться жест, должны быть определены в HTML с уникальными селекторами. Например:

<div id="pinch-box" style="width: 200px; height: 200px; background-color: lightblue;">
</div>

Подключение библиотеки через CDN:

<script src="https://cdn.jsdelivr.net/npm/@interactjs/interactjs/dist/interact.min.js"></script>

Инициализация pinch и конфигурация событий

Interact.js предоставляет метод .gesturable() для отслеживания жестов pinch и rotate. Для масштабирования необходимо использовать события:

  • gesturestart — начало жеста
  • gesturemove — движение (изменение масштаба)
  • gestureend — завершение жеста

Пример базовой настройки:

let scale = 1;

interact('#pinch-box')
  .gesturable({
    listeners: {
      start (event) {
        event.target.dataset.startScale = scale;
      },
      move (event) {
        scale = parseFloat(event.target.dataset.startScale) * event.scale;
        event.target.style.transform = `scale(${scale})`;
      }
    }
  });

Ключевой момент: свойство event.scale возвращает относительный коэффициент изменения от начала жеста, а не абсолютный размер. Поэтому необходимо хранить начальный масштаб элемента, чтобы корректно умножать на текущий коэффициент.

Ограничение масштаба

Для предотвращения чрезмерного уменьшения или увеличения элемента применяются ограничения:

const minScale = 0.5;
const maxScale = 3;

interact('#pinch-box')
  .gesturable({
    listeners: {
      move (event) {
        scale = Math.max(minScale, Math.min(maxScale, parseFloat(event.target.dataset.startScale) * event.scale));
        event.target.style.transform = `scale(${scale})`;
      }
    }
  });

Важное замечание: без ограничения пользователь может случайно увеличить элемент до огромного размера или уменьшить до нуля, что нарушает UX.

Масштабирование с учётом позиции

Если необходимо масштабировать элемент относительно точки касания (например, центр между пальцами), используется свойство event.dsplacement или вычисление центра на основе event.touches:

interact('#pinch-box')
  .gesturable({
    listeners: {
      move (event) {
        const rect = event.target.getBoundingClientRect();
        const centerX = rect.left + rect.width / 2;
        const centerY = rect.top + rect.height / 2;

        scale = parseFloat(event.target.dataset.startScale) * event.scale;

        event.target.style.transformOrigin = `${centerX}px ${centerY}px`;
        event.target.style.transform = `scale(${scale})`;
      }
    }
  });

Секрет эффективного масштабирования: корректная установка transform-origin гарантирует, что элемент будет увеличиваться именно от точки между пальцами, а не смещаться по экрану.

Комбинирование pinch с drag и rotate

Interact.js позволяет комбинировать жесты. Пример совместного использования:

let posX = 0;
let posY = 0;
let angle = 0;

interact('#pinch-box')
  .draggable({
    listeners: {
      move(event) {
        posX += event.dx;
        posY += event.dy;
        updateTransform();
      }
    }
  })
  .gesturable({
    listeners: {
      move(event) {
        scale = parseFloat(event.target.dataset.startScale) * event.scale;
        angle += event.da;
        updateTransform();
      }
    }
  });

function updateTransform() {
  const el = document.getElementById('pinch-box');
  el.style.transform = `translate(${posX}px, ${posY}px) rotate(${angle}deg) scale(${scale})`;
}

Важное: при одновременном drag и pinch необходимо объединять все трансформации в одну строку transform. Иначе могут возникнуть неожиданные сдвиги или искажения.

События gesturestart и gestureend

  • gesturestart используется для инициализации значений: начальный масштаб, позиция, угол.
  • gestureend часто применяется для сохранения итогового состояния или плавного возвращения к исходным границам при выходе за пределы min/max:
interact('#pinch-box')
  .gesturable({
    listeners: {
      end(event) {
        scale = Math.max(minScale, Math.min(maxScale, scale));
        event.target.style.transform = `scale(${scale})`;
      }
    }
  });

Это предотвращает накопление некорректных значений при последовательных жестах.

Использование data-атрибутов для хранения состояния

Interact.js не хранит внутреннее состояние элемента между событиями. Для надёжной работы часто используют dataset:

event.target.dataset.startScale = scale;

Такой подход позволяет корректно рассчитывать новые значения при каждом событии gesturemove, не теряя информации о предыдущем состоянии.

Практические рекомендации

  • Для плавности анимации можно добавлять CSS-переход на свойство transform.
  • При работе с мобильными браузерами важно учитывать задержку событий touch и предотвращать конфликт с прокруткой.
  • Для сложных интерфейсов pinch часто комбинируют с ограничениями контейнера, чтобы элемент не выходил за видимую область.

Все эти техники формируют базу для создания интерактивных элементов с масштабированием pinch, обеспечивая стабильное и предсказуемое поведение на любых устройствах.