Концепция сенсоров ввода

В библиотеке Shopify Draggable сенсоры ввода (input sensors) представляют собой механизм абстракции источников взаимодействия пользователя с интерфейсом. Сенсоры определяют, каким образом библиотека будет отслеживать события начала перетаскивания, его движение и завершение. Они отделяют логическую модель перетаскивания от конкретного устройства ввода, будь то мышь, касание на сенсорном экране или клавиатура.


Основные типы сенсоров

  1. MouseSensor Сенсор мыши отвечает за стандартное взаимодействие через события mousedown, mousemove и mouseup. Он обеспечивает плавное перетаскивание элементов на десктопных устройствах и является базовым сенсором для большинства приложений.

    Ключевые особенности:

    • Поддержка клика и удержания элемента для начала drag.
    • Обработка движения курсора с высокой точностью.
    • Автоматическое преобразование событий мыши в события Draggable (drag:start, drag:move, drag:stop).
  2. TouchSensor Сенсор касаний предназначен для мобильных устройств. Он работает с событиями touchstart, touchmove, touchend и учитывает особенности тач-интерфейсов, включая множественные касания.

    Особенности реализации:

    • Игнорирование случайных коротких тапов.
    • Распознавание свайпа как движения перетаскивания.
    • Поддержка касаний несколькими пальцами без конфликтов с масштабированием или прокруткой.
  3. ForceTouchSensor / PointerSensor В современных версиях Draggable введены сенсоры, использующие API указателя (Pointer Events). Они объединяют поддержку мыши, касаний и стилуса в одном интерфейсе. Это позволяет:

    • Работать с одним событием (pointerdown), вместо множества событий.
    • Обрабатывать различие между стилусом, мышью и пальцем.
    • Обеспечивать более предсказуемую обработку жестов и анимаций.

Архитектура сенсоров

Сенсоры реализованы как отдельные классы, наследующиеся от BaseSensor. Каждому сенсору необходимо реализовать минимальный набор методов:

  • attach() — подключение слушателей событий к DOM-элементам. Обычно вызывается при инициализации Draggable.
  • detach() — удаление слушателей для предотвращения утечек памяти.
  • start(event) — логика начала перетаскивания на основании события пользователя.
  • move(event) — логика движения элемента.
  • stop(event) — завершение перетаскивания.

Ключевая идея архитектуры: сенсор не знает о структуре Draggable и не взаимодействует с DOM напрямую. Он генерирует события, которые библиотека интерпретирует для обновления позиции элементов.


Настройка сенсоров

При создании экземпляра Draggable можно указать массив сенсоров:

import { Draggable, MouseSensor, TouchSensor } from '@shopify/draggable';

const draggable = new Draggable(document.querySelectorAll('.draggable-item'), {
  sensors: [MouseSensor, TouchSensor],
  draggable: '.draggable-item'
});

Пояснения к настройке:

  • sensors — массив классов сенсоров. Порядок важен: первый сенсор получает приоритет при инициализации.
  • Сенсоры можно комбинировать, чтобы поддерживать разные типы устройств.
  • Каждому сенсору автоматически передается элемент контейнера и конфигурация Draggable.

Обработка пользовательских событий через сенсоры

Draggable использует CustomEvent-подход. Сенсоры генерируют события с типами drag:start, drag:move, drag:stop, которые можно слушать:

draggable.on('drag:start', (event) => {
  console.log('Начало перетаскивания:', event.source);
});

draggable.on('drag:move', (event) => {
  event.source.style.transform = `translate(${event.delta.x}px, ${event.delta.y}px)`;
});

draggable.on('drag:stop', (event) => {
  console.log('Перетаскивание завершено');
});

Особенности передачи данных:

  • event.source — DOM-элемент, с которого началось перетаскивание.
  • event.sensorEvent — оригинальное событие (MouseEvent, TouchEvent, PointerEvent), обеспечивающее доступ к координатам и дополнительной информации.
  • event.delta — разница между начальной и текущей позицией курсора.

Кастомизация сенсоров

Можно создавать собственные сенсоры, расширяя BaseSensor:

import { BaseSensor } from '@shopify/draggable';

class CustomSensor extends BaseSensor {
  attach() {
    this.el.addEventListener('mousedown', this.start);
  }
  
  start(event) {
    console.log('Custom drag start');
    super.start(event);
  }
}

Возможности кастомных сенсоров:

  • Поддержка нестандартных устройств (например, джойстиков или VR-контроллеров).
  • Фильтрация событий по типу элемента или координатам.
  • Управление анимацией элементов на лету без вмешательства Draggable.

Взаимодействие сенсоров с плагинами Draggable

Сенсоры могут быть расширены плагинами для:

  • Автосортировки элементов.
  • Индикации “зоны сброса”.
  • Ограничения перетаскивания по оси X или Y.

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

import { Sortable } from '@shopify/draggable';

const sortable = new Sortable(document.querySelectorAll('.list-item'), {
  sensors: [MouseSensor, TouchSensor],
  draggable: '.list-item'
});

В этом случае сенсор передает события плагину, который отвечает за перестановку элементов в DOM, а сенсор остается «чистым» и независимым от конкретной логики сортировки.


Резюме архитектуры

  • Сенсор: отвечает за отслеживание ввода.
  • Draggable: принимает события сенсора и управляет логикой перетаскивания.
  • Плагины: расширяют функциональность, используя события сенсора и Draggable.

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