Классы состояний

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


Интерфейс Interactable и состояние элементов

Каждый объект, созданный через interact(selector) или interact(element), становится interactable, обладающим собственным набором состояний. Состояния определяют:

  • Текущую активность взаимодействия (dragging, resizing, gesturing).
  • Параметры взаимодействия (ограничения, инерция, оси движения).
  • Текущие координаты элемента и смещения.
  • Флаги начала и окончания событий.

Пример создания interactable:

const element = document.querySelector('.draggable');

interact(element)
  .draggable({
    inertia: true,
    modifiers: [
      interact.modifiers.restrictRect({
        restriction: 'parent',
        endOnly: true
      })
    ],
    listeners: {
      start(event) {
        console.log('Начало перетаскивания', event);
      },
      move(event) {
        const { dx, dy } = event;
        event.target.style.transform =
          `translate(${dx}px, ${dy}px)`;
      },
      end(event) {
        console.log('Конец перетаскивания', event);
      }
    }
  });

В этом примере состояние элемента хранит текущие смещения dx и dy, а также флаги inertia и active. Эти данные используются внутренне библиотекой для расчета последующих изменений.


Основные классы состояния

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

1. Interaction

Класс Interaction является центральным для всех событий. Он хранит данные о текущем взаимодействии с элементом:

  • target — элемент, с которым происходит взаимодействие.
  • pointerType — тип указателя (mouse, touch, pen).
  • interactionType — тип действия (drag, resize, gesture).
  • interactable — объект interactable, связанный с элементом.
  • pointerId — идентификатор указателя, поддерживает мультитач.
  • Методы для отслеживания позиции: getPointerDelta(), getPointerStart(), getPointerCurrent().

Пример использования:

interact('.resizable').on('resizemove', event => {
  const interaction = event.interaction;
  console.log(interaction.pointerType, interaction.interactionType);
});

2. Interactable и состояние модификаторов

Interactable управляет модификаторами (modifiers) — объектами, которые изменяют поведение элемента во время взаимодействия. Модификаторы используют внутренние состояния для ограничения движения или размеров:

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

Пример использования модификатора snap:

interact('.draggable').draggable({
  modifiers: [
    interact.modifiers.snap({
      targets: [
        interact.createSnapGrid({ x: 50, y: 50 })
      ],
      range: Infinity,
      relativePoints: [{ x: 0, y: 0 }]
    })
  ]
});

Состояние snap хранит текущую цель (target) и вычисленное смещение, обновляя dx и dy при каждом событии move.


3. Класс PointerEvent и синхронизация состояния

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

  • event.clientX и event.clientY — текущие координаты указателя.
  • event.deltaX и event.deltaY — смещение от предыдущего события.
  • event.pageX и event.pageY — абсолютные координаты на странице.
  • event.pointerId — уникальный идентификатор для мультитач.

Это обеспечивает консистентное управление состоянием, независимо от источника ввода.


Управление активными состояниями

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

  • Active — взаимодействие активно, элемент реагирует на события.
  • Inertia — элемент продолжает движение после окончания перетаскивания.
  • Pending — событие инициировано, но не достигло точки активации (например, короткое касание, которое не превратилось в drag).

Для контроля этих состояний применяются свойства event.interaction.active и методы interaction.stop() для принудительной остановки любого активного взаимодействия.


Расширенные возможности состояния

Состояния позволяют реализовать сложные сценарии:

  1. Множественные взаимодействия: Drag и resize одновременно. Interact.js синхронизирует координаты через одно состояние Interaction.
  2. Обработка инерции: Состояние хранит скорость и направление для плавного движения.
  3. Динамическое изменение ограничений: Модификаторы могут менять ограничения во время взаимодействия, сохраняя историю состояний.

Пример динамического ограничения:

interact('.draggable').draggable({
  modifiers: [
    interact.modifiers.restrictRect({
      restriction: (element, interaction) => {
        if (interaction.pointerType === 'touch') {
          return element.parentNode;
        } else {
          return document.body;
        }
      },
      endOnly: true
    })
  ]
});

Здесь состояние restriction вычисляется динамически на основе типа указателя.


Ключевые принципы работы с классами состояний

  • Состояние — источник правды: Все координаты, флаги и модификаторы хранятся в объектах состояния и используются для расчета событий.
  • Разделение логики: Interactable хранит конфигурацию, Interaction — текущее активное состояние, а PointerEvent — данные указателя.
  • Многоуровневое управление: Модификаторы могут работать независимо друг от друга, изменяя состояние без вмешательства в основное взаимодействие.
  • Поддержка мультитач и жестов: Каждый указатель имеет собственное состояние, что позволяет обрабатывать сложные жесты, такие как pinch и rotate.

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