Множественные действия на одном элементе

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

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

или

npm install @interactjs/interactjs

После подключения создаётся интерактивный элемент с помощью метода interact(target), где target — CSS-селектор или DOM-элемент.

interact('.draggable-resizable')
  .draggable({ /* настройки */ })
  .resizable({ /* настройки */ });

Важное отличие работы с множественными действиями: все методы (draggable, resizable, gesturable) можно вызывать последовательно на одном и том же элементе, а библиотека автоматически управляет конфликтами между ними.


Настройка множественных действий

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

  1. Перетаскивание (drag) Позволяет перемещать элемент по странице. Основные параметры:

    • inertia — включение инерции после отпускания мыши.
    • modifiers — ограничения движения (например, сетка, границы контейнера).
    • listeners — события start, move, end.
interact('.draggable-resizable')
  .draggable({
    inertia: true,
    modifiers: [
      interact.modifiers.restrictRect({
        restriction: 'parent',
        endOnly: true
      })
    ],
    listeners: {
      move(event) {
        const { dx, dy } = event;
        const target = event.target;
        const x = (parseFloat(target.getAttribute('data-x')) || 0) + dx;
        const y = (parseFloat(target.getAttribute('data-y')) || 0) + dy;

        target.style.transform = `translate(${x}px, ${y}px)`;
        target.setAttribute('data-x', x);
        target.setAttribute('data-y', y);
      }
    }
  });
  1. Изменение размера (resize) Позволяет изменять ширину и высоту элемента. Настройки включают:

    • edges — определяет, с каких сторон можно изменять размер.
    • modifiers — ограничения минимальных и максимальных размеров.
    • listeners — события start, move, end.
interact('.draggable-resizable')
  .resizable({
    edges: { left: true, right: true, bottom: true, top: true },
    modifiers: [
      interact.modifiers.restrictSize({
        min: { width: 50, height: 50 },
        max: { width: 500, height: 500 }
      })
    ],
    listeners: {
      move(event) {
        const target = event.target;
        let { width, height } = event.rect;

        target.style.width = width + 'px';
        target.style.height = height + 'px';

        const x = (parseFloat(target.getAttribute('data-x')) || 0) + event.deltaRect.left;
        const y = (parseFloat(target.getAttribute('data-y')) || 0) + event.deltaRect.top;

        target.style.transform = `translate(${x}px, ${y}px)`;
        target.setAttribute('data-x', x);
        target.setAttribute('data-y', y);
      }
    }
  });
  1. Жесты (gesturable) Позволяет работать с поворотом и масштабированием через мультитач. Настройки:

    • listeners — события start, move, end.
    • inertia — аналогично drag, поддерживает инерцию поворота и масштабирования.
interact('.draggable-resizable')
  .gesturable({
    listeners: {
      move(event) {
        const target = event.target;
        const scale = (parseFloat(target.getAttribute('data-scale')) || 1) * (1 + event.ds);
        const angle = (parseFloat(target.getAttribute('data-angle')) || 0) + event.da;

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

        target.setAttribute('data-scale', scale);
        target.setAttribute('data-angle', angle);
      }
    }
  });

Совмещение действий на одном элементе

Ключевой момент: один и тот же элемент может одновременно поддерживать drag, resize и gesturable. Для корректной работы нужно:

  • Всегда хранить состояние позиции, масштаба и поворота в атрибутах элемента (data-x, data-y, data-scale, data-angle).
  • Использовать единый transform для всех изменений. Например:
target.style.transform = `translate(${x}px, ${y}px) rotate(${angle}deg) scale(${scale})`;
  • Обрабатывать delta изменений отдельно для каждой функции, чтобы избежать перезаписи значений.

Пример объединённого обработчика move для drag + resize + gesturable:

function updateTransform(target, dx=0, dy=0, dWidth=0, dHeight=0, dScale=1, dAngle=0) {
  const x = (parseFloat(target.getAttribute('data-x')) || 0) + dx;
  const y = (parseFloat(target.getAttribute('data-y')) || 0) + dy;
  const width = (parseFloat(target.style.width) || target.offsetWidth) + dWidth;
  const height = (parseFloat(target.style.height) || target.offsetHeight) + dHeight;
  const scale = ((parseFloat(target.getAttribute('data-scale')) || 1) * dScale);
  const angle = (parseFloat(target.getAttribute('data-angle')) || 0) + dAngle;

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

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

Ограничения и модификаторы

Interact.js предоставляет модификаторы, которые позволяют:

  • Ограничивать движение: restrictRect, restrictEdges.
  • Фиксировать сетку: snap.
  • Контролировать размер: restrictSize, aspectRatio.

Пример ограничения по сетке при перетаскивании и изменении размера:

modifiers: [
  interact.modifiers.snap({
    targets: [interact.snappers.grid({ x: 20, y: 20 })],
    range: Infinity,
    relativePoints: [{ x: 0, y: 0 }]
  }),
  interact.modifiers.restrictSize({
    min: { width: 50, height: 50 },
    max: { width: 400, height: 400 }
  })
]

Работа с событиями

Interact.js поддерживает следующие события:

  • start — инициирование действия.
  • move — каждое изменение координат/размера/масштаба.
  • end — завершение действия.
  • inertiastart, inertiaresume, inertiaend — при включённой инерции.

Можно подписываться на события глобально через interact(target).on(event, handler) или локально через listeners в настройках действия.


Рекомендации по производительности

  • При множественных действиях важно использовать transform вместо изменения top и left напрямую, это снижает нагрузку на рендер.
  • Ограничивать количество DOM-операций внутри обработчиков move.
  • Хранить состояние в атрибутах или переменных, чтобы не пересчитывать их каждый раз из CSS.
  • Для сложных элементов применять will-change: transform; в CSS для GPU-ускорения.

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