Одновременное использование жестов

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

Ключевым моментом является конфликт жестов, когда два или более действия могут выполняться одновременно. Interact.js использует механизм pointer-events и внутренние приоритеты событий для их согласованного выполнения.


Настройка элементов для нескольких жестов

Для одновременного использования жестов необходимо правильно настроить объект Interactable:

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);
      }
    }
  })
  .resizable({
    edges: { left: true, right: true, bottom: true, top: true },
    modifiers: [
      interact.modifiers.restrictEdges({
        outer: 'parent',
        endOnly: true
      })
    ],
    listeners: {
      move(event) {
        const { width, height } = event.rect;
        const target = event.target;

        target.style.width = `${width}px`;
        target.style.height = `${height}px`;
      }
    }
  });

Здесь элемент одновременно поддерживает перетаскивание и изменение размера. Использование атрибутов data-x и data-y позволяет синхронизировать положение элемента при движении и масштабировании.


Обработка одновременных событий

Interact.js предоставляет объект interaction, который содержит информацию о всех активных указателях. Для одновременного жеста важно использовать методы interactable.options и interaction:

  • interaction.pointerIsDown — проверка, удерживается ли указатель.
  • interaction.pointers — массив всех активных указателей, что позволяет реализовать жесты с несколькими пальцами.
  • interaction.prepared — текущая подготовка к событию, помогает отслеживать активные действия на элементе.

Пример обработки многофакторного жеста (drag + resize):

interact('.multi-touch')
  .gesturable({
    listeners: {
      start(event) {
        event.target.dataset.startAngle = event.angle;
      },
      move(event) {
        const rotation = event.angle - parseFloat(event.target.dataset.startAngle);
        event.target.style.transform = `rotate(${rotation}deg)`;
      }
    }
  })
  .draggable({
    listeners: {
      move(event) {
        const x = (parseFloat(event.target.dataset.x) || 0) + event.dx;
        const y = (parseFloat(event.target.dataset.y) || 0) + event.dy;

        event.target.style.transform = `translate(${x}px, ${y}px)`;
        event.target.dataset.x = x;
        event.target.dataset.y = y;
      }
    }
  });

Использование gesturable совместно с draggable позволяет одновременно вращать и перемещать элемент, при этом Interact.js корректно синхронизирует изменения трансформации.


Модификаторы для безопасного совмещения жестов

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

  • restrictRect — ограничивает перемещение и размер элементами родителя.
  • snap — фиксирует координаты к сетке или заранее заданным точкам.
  • restrictEdges — предотвращает выход границ элемента за пределы контейнера.
  • restrictSize — ограничивает минимальные и максимальные размеры при изменении размера.

Пример сочетания snap и draggable:

interact('.snapped-draggable')
  .draggable({
    modifiers: [
      interact.modifiers.snap({
        targets: [
          interact.createSnapGrid({ x: 50, y: 50 })
        ],
        range: Infinity,
        relativePoints: [{ x: 0, y: 0 }]
      })
    ],
    listeners: {
      move(event) {
        const x = (parseFloat(event.target.dataset.x) || 0) + event.dx;
        const y = (parseFloat(event.target.dataset.y) || 0) + event.dy;

        event.target.style.transform = `translate(${x}px, ${y}px)`;
        event.target.dataset.x = x;
        event.target.dataset.y = y;
      }
    }
  });

Модификаторы гарантируют, что при одновременном использовании жестов элемент не выйдет за пределы разрешённой области и не пересечёт запрещённые координаты.


Управление приоритетами жестов

Interact.js позволяет контролировать приоритет событий, что важно при наложении drag, resize и gesturable. Основные инструменты:

  • autoScroll — приоритет скроллинга контейнера во время drag.
  • ignoreFrom и allowFrom — разрешают или запрещают обработку событий на определённых вложенных элементах.
  • pointerTypes — фильтрация типов указателей для разных жестов (мышь, touch, pen).

Пример приоритета drag над gesturable:

interact('.priority-example')
  .gesturable({
    pointerTypes: ['touch'],
    listeners: { move: event => console.log('Gesture detected') }
  })
  .draggable({
    pointerTypes: ['mouse', 'touch'],
    ignoreFrom: '.no-drag',
    listeners: { move: event => console.log('Drag detected') }
  });

Таким образом, можно настроить гибкую логику, где разные жесты активируются в зависимости от источника указателя и конкретной области элемента.


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

  • Всегда хранить текущие координаты и размеры элемента через dataset или отдельные переменные, чтобы синхронизировать изменения нескольких жестов.
  • Использовать modifiers для предотвращения конфликтов при одновременном использовании drag и resize.
  • Следить за трансформациями CSS: при совмещении translate, rotate и scale их нужно объединять в единую строку transform.
  • Для multi-touch жестов обязательно проверять interaction.pointers.length, чтобы корректно обрабатывать события с несколькими пальцами.
  • Разделять логику движения, масштабирования и вращения, чтобы один жест не перезаписывал другой.

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