Трансформации и матрицы

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


Матрицы трансформации

В основе трансформаций лежит матрица 2D-преобразований. Она описывает линейное преобразование точек на плоскости и позволяет объединять несколько операций в одну. Стандартная матрица 2D имеет вид:

[a, c, e]
[b, d, f]
[0, 0, 1]

Где:

  • a и d — масштабирование по осям X и Y
  • b и c — наклон (skew)
  • e и f — смещение (translate)

Interact.js использует такие матрицы для отображения текущего состояния элемента, объединяя все примененные трансформации.


Работа с трансформациями в Interact.js

Interact.js позволяет управлять трансформациями через несколько ключевых событий и методов:

  1. draggable — управление перемещением элементов.
  2. resizable — изменение размеров с учетом ограничений.
  3. gesturable — поддержка мультитач жестов, включая вращение и масштабирование.

Каждое из этих действий формирует объект interaction, содержащий текущие значения трансформации, например:

interaction.target.style.transform = `translate(${x}px, ${y}px) rotate(${angle}deg) scale(${scale})`;

Комбинирование трансформаций

Одновременное применение нескольких эффектов — перемещения, масштабирования и вращения — требует аккуратного объединения матриц. Interact.js автоматизирует этот процесс, предоставляя объект interaction, в котором хранится текущая матрица трансформации элемента. Пример:

interact('.draggable')
  .draggable({
    listeners: {
      move(event) {
        const target = event.target;
        const x = (parseFloat(target.getAttribute('data-x')) || 0) + event.dx;
        const y = (parseFloat(target.getAttribute('data-y')) || 0) + event.dy;

        target.style.transform =
          `translate(${x}px, ${y}px) ` +
          `rotate(${parseFloat(target.getAttribute('data-rotate')) || 0}deg) ` +
          `scale(${parseFloat(target.getAttribute('data-scale')) || 1})`;

        target.setAttribute('data-x', x);
        target.setAttribute('data-y', y);
      }
    }
  });

В этом примере каждое перемещение аккумулируется, сохраняя текущее положение и одновременно сохраняя другие трансформации.


Управление вращением и масштабом

Для жестов мультитача используется модуль gesturable. Он автоматически предоставляет параметры event.angle и event.scale, которые можно интегрировать в текущую трансформацию:

interact('.gesturable')
  .gesturable({
    listeners: {
      move(event) {
        const target = event.target;
        const currentAngle = parseFloat(target.getAttribute('data-rotate')) || 0;
        const currentScale = parseFloat(target.getAttribute('data-scale')) || 1;

        const newAngle = currentAngle + event.da;
        const newScale = currentScale * event.ds;

        target.style.transform =
          `rotate(${newAngle}deg) scale(${newScale}) ` +
          `translate(${parseFloat(target.getAttribute('data-x')) || 0}px, ${parseFloat(target.getAttribute('data-y')) || 0}px)`;

        target.setAttribute('data-rotate', newAngle);
        target.setAttribute('data-scale', newScale);
      }
    }
  });

Здесь event.da — изменение угла в градусах, event.ds — коэффициент масштабирования. С помощью таких данных можно динамически объединять трансляцию, вращение и масштаб.


Работа с комбинированными матрицами

Interact.js предоставляет возможность использовать встроенные функции для объединения матриц, чтобы создавать сложные анимации и трансформации без ручного расчета. Например, библиотека позволяет конвертировать текущие значения трансформации в матрицу DOMMatrix, которая поддерживает методы multiply, invert и translate:

const matrix = new DOMMatrix(window.getComputedStyle(target).transform);
matrix.translateSelf(dx, dy);
matrix.rotateSelf(dAngle);
matrix.scaleSelf(dScale);

target.style.transform = matrix.toString();

Преимущество такого подхода — возможность точно комбинировать любые преобразования, даже если они происходят одновременно, и легко управлять обратными преобразованиями.


Ограничения и контроль трансформаций

Interact.js позволяет задавать ограничения трансформаций, чтобы контролировать движение и размер элементов:

  • restrict — ограничение по границам контейнера.
  • modifiers — дополнительные фильтры для трансформаций (например, ограничение углов поворота или масштаба).

Пример с ограничением перемещения внутри родительского блока:

interact('.draggable')
  .draggable({
    modifiers: [
      interact.modifiers.restrictRect({
        restriction: 'parent',
        endOnly: true
      })
    ]
  });

Практическое использование матриц для сложных интерфейсов

Использование матриц особенно важно, когда необходимо:

  • Создавать многоуровневые трансформации, где элемент одновременно масштабируется и вращается относительно точки.
  • Реализовать интерактивные канвасы или визуальные редакторы.
  • Обеспечивать обратимые действия, например, отмену последнего изменения позиции или масштаба.

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