События касания

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

Ключевая особенность модели — абстрагирование различий между мышью и касанием. Один и тот же обработчик может реагировать на взаимодействие независимо от устройства ввода, при этом объект события содержит расширенные данные о координатах и контексте.

Основные принципы обработки касаний:

  • унификация событий мыши и touch-инпутов;
  • привязка событий к объекту карты (L.Map);
  • доступ к географическим координатам через latlng;
  • сохранение исходного DOM-события в originalEvent.

Включение и отключение сенсорных возможностей карты

Leaflet предоставляет набор опций, влияющих на поведение касаний. Они задаются при создании карты или изменяются через методы управления состоянием.

Основные опции

  • touchZoom — масштабирование жестом двумя пальцами
  • tap — обработка одиночных касаний как кликов
  • doubleClickZoom — зум по двойному нажатию
  • bounceAtZoomLimits — визуальная инерция на границах масштаба

Пример конфигурации:

const map = L.map('map', {
  center: [51.505, -0.09],
  zoom: 13,
  touchZoom: true,
  tap: true,
  doubleClickZoom: true,
  bounceAtZoomLimits: true
});

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

map.touchZoom.disable();
map.doubleClickZoom.disable();
map.tap?.disable();

Базовые события касания карты

Leaflet проксирует низкоуровневые touch-события в систему событий карты. Основные из них:

touchstart

Срабатывает при первом касании поверхности карты.

map.on('touchstart', function (e) {
  console.log('Начало касания', e);
});

touchend

Срабатывает при завершении касания.

map.on('touchend', function (e) {
  console.log('Окончание касания');
});

touchmove

Генерируется при перемещении пальца по экрану.

map.on('touchmove', function (e) {
  console.log('Движение касания');
});

Эти события полезны при реализации кастомных жестов, рисования на карте и анализа поведения пользователя.


Унифицированные события взаимодействия

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

click

Срабатывает после завершения короткого касания или клика мышью.

map.on('click', function (e) {
  console.log(e.latlng);
});

dblclick

Двойное касание или двойной клик.

map.on('dblclick', function (e) {
  console.log('Двойное взаимодействие', e.latlng);
});

mousedown / mouseup

На сенсорных устройствах могут соответствовать началу и завершению касания в упрощённой форме.

map.on('mousedown', () => console.log('Нажатие'));
map.on('mouseup', () => console.log('Отпускание'));

Объект события касания

Каждое событие Leaflet содержит унифицированную структуру данных.

Основные поля

  • latlng — географические координаты точки взаимодействия
  • containerPoint — координаты относительно контейнера карты
  • layerPoint — координаты внутри слоя карты
  • originalEvent — исходное DOM-событие touch

Пример анализа события:

map.on('touchstart', function (e) {
  const coords = e.latlng;
  const screenPoint = e.containerPoint;
  const nativeEvent = e.originalEvent;

  console.log(coords, screenPoint, nativeEvent.touches);
});

Жест масштабирования двумя пальцами

Масштабирование реализуется через встроенный механизм touchZoom. Он преобразует расстояние между пальцами в изменение уровня зума карты.

При включённой опции:

const map = L.map('map', {
  touchZoom: true
});

Leaflet автоматически:

  • отслеживает расстояние между двумя точками касания;
  • вычисляет коэффициент масштабирования;
  • центрирует карту между пальцами;
  • плавно обновляет уровень zoom.

Отключение:

map.touchZoom.disable();

Имитация долгого нажатия

Leaflet не предоставляет отдельного события long press, но его можно реализовать через комбинацию touchstart и touchend.

let pressTimer;

map.on('touchstart', function (e) {
  pressTimer = setTimeout(() => {
    console.log('Долгое нажатие', e.latlng);
  }, 600);
});

map.on('touchend', function () {
  clearTimeout(pressTimer);
});

Такой подход используется для:

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

Отличия поведения касаний от мыши

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

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

  • touch-события часто генерируют дополнительные промежуточные состояния;
  • click может задерживаться до определения, является ли жест прокруткой;
  • двойное касание интерпретируется как жест масштабирования, а не как dblclick в классическом смысле;
  • движение пальца при активной карте блокирует стандартный scroll страницы.

Взаимодействие касаний с перетаскиванием карты

Перетаскивание (dragging) тесно связано с touchmove.

map.on('dragstart', () => {
  console.log('Начало перемещения карты');
});

map.on('drag', () => {
  console.log('Перемещение');
});

map.on('dragend', () => {
  console.log('Окончание перемещения');
});

На сенсорных устройствах drag активируется при:

  • удержании пальца;
  • смещении точки касания после порога движения.

Конфликты жестов и прокрутки страницы

При работе с картой внутри мобильных интерфейсов возникает конфликт между прокруткой страницы и жестами карты.

Leaflet решает его следующим образом:

  • перехватывает touchmove внутри контейнера карты;
  • блокирует native scroll при активном взаимодействии;
  • управляет preventDefault() внутри обработчиков.

Дополнительная настройка:

const map = L.map('map', {
  dragging: true,
  touchZoom: true,
  scrollWheelZoom: true
});

Оптимизация обработки касаний

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

Типовые подходы оптимизации:

  • минимизация вычислений в touchmove;
  • использование throttling для тяжёлых операций;
  • кэширование координат;
  • перенос логики в requestAnimationFrame.

Пример ограничения частоты:

let lastRun = 0;

map.on('touchmove', function (e) {
  const now = Date.now();
  if (now - lastRun < 50) return;

  lastRun = now;
  console.log(e.latlng);
});

Использование касаний для пользовательских инструментов

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

Рисование линий

const points = [];

map.on('touchmove', function (e) {
  points.push(e.latlng);
});

Позиционирование объектов

map.on('click', function (e) {
  L.marker(e.latlng).addTo(map);
});

Интерактивные зоны

map.on('touchstart', function (e) {
  L.circle(e.latlng, { radius: 50 }).addTo(map);
});

Работа с multi-touch данными

Хотя Leaflet абстрагирует сложные жесты, исходные данные доступны через originalEvent.touches.

map.on('touchstart', function (e) {
  const touches = e.originalEvent.touches;
  console.log('Количество касаний:', touches.length);
});

Это позволяет реализовать:

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

Поведение событий при отключённой сенсорной поддержке

При отключении touchZoom и связанных опций:

  • жесты масштабирования не обрабатываются;
  • касания интерпретируются как простые клики;
  • прокрутка страницы может конкурировать с картой;
  • часть событий не генерируется на уровне карты.
map.touchZoom.disable();
map.dragging.disable();

Такая конфигурация применяется в статичных картах и встраиваемых мини-картах без интерактивности.