Жесты и свайпы

Поведение Tom Select на сенсорных устройствах определяется сочетанием нативных событий браузера и внутренних механизмов управления фокусом, скроллом и выбором элементов. Основная сложность заключается в конфликте между прокруткой контейнеров, обработкой касаний и логикой взаимодействия с выбранными элементами (items) и выпадающим списком (dropdown).

Модель сенсорного взаимодействия

Сенсорный ввод в контексте Tom Select опирается на три базовых сценария:

  • прокрутка списка опций внутри dropdown;
  • взаимодействие с выбранными элементами (удаление, фокус, активация);
  • открытие и закрытие выпадающего списка через касание.

Каждый из этих сценариев использует события уровня браузера: touchstart, touchmove, touchend, а в современных реализациях — унифицированные pointerdown, pointermove, pointerup.

Tom Select не реализует полноценную gesture-систему уровня мобильных UI-фреймворков, поэтому поведение жестов формируется комбинацией CSS и JavaScript-обработчиков поверх DOM-структуры.

Обработка касаний и конфликт скролла

Ключевая проблема сенсорного взаимодействия заключается в конкуренции между:

  • вертикальным скроллом списка опций;
  • горизонтальными или диагональными свайпами по элементам;
  • предотвращением «резкого ухода» жеста за пределы компонента.

Внутри dropdown обычно применяется ограничение поведения скролла:

  • контейнер списка получает overflow: auto;
  • включается -webkit-overflow-scrolling: touch для инерции iOS;
  • события touchmove могут частично блокироваться через event.preventDefault() в критических местах.

Однако агрессивная блокировка скролла приводит к ухудшению UX, поэтому используется условная логика: предотвращение стандартного поведения применяется только при распознавании жеста, не связанного со скроллом.

Типовой подход:

  • фиксируется начальная точка касания;
  • вычисляется дельта движения;
  • если горизонтальное смещение превышает вертикальное — жест интерпретируется как свайп;
  • иначе разрешается прокрутка.

Свайп для удаления выбранных элементов

Один из распространённых паттернов мобильного интерфейса — удаление выбранного элемента через свайп по «чипу» (item). В стандартной конфигурации Tom Select удаление происходит через клик по кнопке ×, однако на сенсорных устройствах свайп может служить дополнительным способом взаимодействия.

Логика реализации свайпа:

  • фиксируется touchstart/pointerdown на элементе item;
  • сохраняется начальная координата startX;
  • при движении вычисляется deltaX;
  • при превышении порога (например, 40–60px) инициируется удаление элемента.

Ключевой параметр — порог срабатывания, который предотвращает случайные удаления при обычном скролле.

Пример логики:

  • deltaX > threshold → удаление элемента;
  • deltaX < -threshold → отмена жеста;
  • промежуточные значения → визуальное смещение элемента без действия.

Визуально это часто сопровождается трансформацией:

  • transform: translateX(px)
  • изменение opacity по мере увеличения смещения
  • плавное возвращение при отмене жеста

Pointer Events как современная основа жестов

Современные реализации предпочитают Pointer Events, так как они объединяют мышь, сенсор и стилус в единую модель.

Преимущества:

  • отсутствие дублирования touch/mouse логики;
  • поддержка pointercancel для системных прерываний;
  • возможность отслеживания pointerType (touch, pen, mouse).

Типовая схема:

  • pointerdown — захват начального состояния;
  • pointermove — вычисление смещения;
  • pointerup — завершение жеста;
  • pointercancel — аварийный сброс состояния.

Для корректной работы используется setPointerCapture, позволяющий сохранять поток событий даже при выходе указателя за пределы элемента.

Взаимодействие свайпов и прокрутки dropdown

Dropdown в Tom Select часто содержит большое количество элементов, что делает прокрутку критически важной.

Основная проблема: свайп по элементу может конфликтовать со скроллом списка.

Решения:

  • разделение зон взаимодействия (item vs dropdown list);
  • различение направления жеста;
  • задержка интерпретации жеста (gesture delay ~ 10–30ms);
  • приоритет вертикального движения для scroll-контейнера.

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

Жесты в режиме множественного выбора

При мультивыборе элементы представлены в виде строки «чипов», где каждый элемент потенциально интерактивен.

Особенности:

  • ограниченная ширина контейнера;
  • перенос элементов на новую строку;
  • высокая плотность кликабельных зон.

Свайпы здесь требуют аккуратной реализации, чтобы не конфликтовать с горизонтальным scroll контейнера tags.

Часто применяется правило:

  • если движение строго горизонтальное → свайп;
  • если диагональное или вертикальное → игнор жеста и передача скроллу.

Влияние виртуальной клавиатуры

На мобильных устройствах открытие клавиатуры изменяет viewport, что влияет на жесты:

  • смещение dropdown вверх;
  • изменение координат pointer событий;
  • ложные расчёты delta из-за ресайза viewport.

Для компенсации используются:

  • отслеживание visualViewport;
  • пересчёт координат после resize;
  • фиксация dropdown в пределах видимой области.

Инерция и естественность движения

Для достижения нативного поведения применяются CSS-механизмы:

  • -webkit-overflow-scrolling: touch;
  • scroll-behavior: smooth (ограниченно);
  • transition на transform при свайпе.

Инерционное поведение важно при прокрутке dropdown, но для свайпов по элементам чаще используется строгая дискретная логика без инерции, чтобы избежать неоднозначных состояний.

Производительность жестов

Обработка жестов должна выполняться с учётом частоты событий touchmove (до 120–240 FPS на современных устройствах).

Оптимизация включает:

  • использование requestAnimationFrame для обновления UI;
  • минимизацию DOM-изменений во время движения;
  • кэширование начальных значений координат;
  • избегание layout thrashing (чтение/запись layout в одном кадре).

Особенно критично избегать повторных вычислений getBoundingClientRect() внутри touchmove.

Доступность и альтернативные взаимодействия

Жесты не должны заменять базовые механизмы управления:

  • удаление элементов остаётся доступным через кнопку;
  • навигация возможна через клавиатуру;
  • выбор опций не зависит от свайпов.

ARIA-атрибуты обеспечивают корректную интерпретацию элементов:

  • role="listbox" для dropdown;
  • role="option" для элементов списка;
  • aria-selected для выбранных значений.

Свайпы рассматриваются как дополнительный слой взаимодействия, не влияющий на основную семантику компонента.

Расширение Tom Select жестами через кастомные плагины

Гибкость Tom Select позволяет внедрять собственные gesture-модули через плагины.

Типовая структура расширения:

  • подписка на события initialize;
  • делегирование pointerdown на элементы items;
  • хранение состояния жеста в замыкании;
  • применение трансформаций через CSS классы.

Пример логики плагина:

  • регистрация обработчиков на контейнере items;
  • определение target элемента через closest;
  • вычисление смещения;
  • вызов this.removeItem(value) при превышении порога.

Такой подход сохраняет ядро Tom Select неизменным и переносит логику жестов на уровень расширений.

Ограничения сенсорной модели

Сенсорное взаимодействие в Tom Select имеет ряд ограничений:

  • отсутствие встроенной gesture abstraction;
  • зависимость от поведения браузера;
  • сложность унификации iOS/Android особенностей;
  • потенциальные конфликты с виртуальной клавиатурой.

Эти ограничения компенсируются кастомизацией событийной модели и строгим разделением зон взаимодействия внутри компонента.