Тач события

Мобильное взаимодействие в картографических интерфейсах строится поверх абстракции жестов, которая объединяет касания (touch), мышь (mouse) и указательные события (pointer events). В Google Maps JavaScript API эта модель инкапсулирована в систему жестов карты, где базовые операции — перетаскивание, масштабирование, поворот и двойной тап — обрабатываются единым механизмом независимо от типа устройства.

Ключевой принцип: карта не работает напрямую с touchstart или touchmove как основной API уровня библиотеки. Вместо этого она преобразует низкоуровневые события браузера в высокоуровневые жесты.


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

Внутри движка карты события проходят несколько слоёв:

  1. DOM-события браузера

    • touchstart
    • touchmove
    • touchend
    • pointerdown / pointermove / pointerup
    • mousedown / mousemove / mouseup
  2. Слой нормализации Преобразует разные типы ввода в унифицированные жесты.

  3. Gesture Manager Определяет:

    • drag (перетаскивание карты)
    • pinch zoom (масштабирование)
    • double tap zoom
    • rotation (если включено)
  4. API-события Google Maps

    • dragstart, drag, dragend
    • zoom_changed
    • center_changed
    • click, dblclick
    • idle

Включение и управление жестами

Поведение тач-событий задаётся через параметры карты:

const map = new google.maps.Map(document.getElementById("map"), {
  center: { lat: 50.4501, lng: 30.5234 },
  zoom: 10,
  gestureHandling: "auto"
});

gestureHandling

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

  • auto — поведение по умолчанию, адаптивное к устройству
  • greedy — все жесты передаются карте без ограничений
  • cooperative — карта требует Ctrl/Cmd + прокрутку для zoom
  • none — отключение жестов

greedy наиболее чувствителен к тач-событиям и часто используется в мобильных интерфейсах.


Поведение touch и mouse в единой модели

На мобильных устройствах один жест может порождать несколько событий:

  • одиночное касание → mousedown + click
  • свайп → серия mousemove
  • pinch → два pointer-события

Google Maps JavaScript API абстрагирует это в единые события карты, исключая необходимость ручной обработки touch*.


Перехват DOM touch событий поверх карты

Иногда требуется обработка касаний на уровне DOM, поверх карты:

const mapDiv = document.getElementById("map");

mapDiv.addEventListener("touchstart", (e) => {
  console.log("touchstart on map container");
});

Однако карта может перехватывать события, особенно при gestureHandling: "greedy".


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

При работе с оверлеями важно предотвращать «утекание» касаний в карту:

const overlay = document.getElementById("overlay");

overlay.addEventListener("touchmove", (e) => {
  e.stopPropagation();
});

Если этого не сделать, карта продолжит обрабатывать жест как pan или zoom.


Поведение на оверлеях и маркерах

Маркеры

Маркер генерирует собственные события:

marker.addListener("click", () => {
  console.log("marker tapped");
});

На мобильных устройствах:

  • tap → click
  • long press → может интерпретироваться как context interaction (зависит от платформы)

InfoWindow

Информационные окна перехватывают касания внутри своего DOM:

const infoWindow = new google.maps.InfoWindow({
  content: "<div>Точка интереса</div>"
});

Касания внутри окна не передаются карте, если не происходит выход за пределы DOM контейнера.


Touch и Pointer Events

Современные браузеры используют Pointer Events как универсальный слой:

  • pointerdown
  • pointermove
  • pointerup

Google Maps JavaScript API не требует ручного управления pointer events, но внутри использует их для унификации ввода.

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

  • один API вместо touch + mouse
  • поддержка стилусов
  • одинаковая модель координат

Проблемы с passive listeners

Мобильные браузеры применяют passive: true к touch-событиям для повышения производительности. Это означает:

  • нельзя вызвать preventDefault()
  • скролл не блокируется

В контексте карты это важно, потому что жесты должны оставаться плавными.

Пример конфликтной ситуации:

mapDiv.addEventListener("touchmove", (e) => {
  e.preventDefault(); // может игнорироваться в passive режиме
});

drag и touch-взаимодействие

Перетаскивание карты — основной жест:

google.maps.event.addListener(map, "dragstart", () => {
  console.log("drag start");
});

google.maps.event.addListener(map, "dragend", () => {
  console.log("drag end");
});

На мобильных устройствах:

  • drag инициируется одним пальцем
  • pinch-zoom блокирует drag
  • inertia применяется после отпускания

pinch-to-zoom и масштабирование

Жест pinch обрабатывается как изменение zoom:

map.setOptions({
  zoomControl: true
});

При pinch:

  • центр карты динамически пересчитывается
  • zoom изменяется относительно midpoint пальцев
  • событие zoom_changed вызывается после завершения жеста

long press и контекстные действия

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

  • добавления маркера
  • открытия меню
  • определения координат
map.addListener("rightclick", (e) => {
  console.log(e.latLng.toString());
});

На мобильных устройствах rightclick часто эмулируется long press.


Координаты касаний

При обработке жестов ключевой объект — MouseEvent или MapMouseEvent:

map.addListener("click", (e) => {
  console.log(e.latLng.lat(), e.latLng.lng());
});

На тач-устройствах координаты вычисляются из touch point через projection map.


Конфликты жестов в сложных интерфейсах

Типичные проблемы:

1. Scroll страницы vs pan карты

Решение:

  • gestureHandling: cooperative
  • фиксированная высота контейнера

2. Swipe внутри карты

Решение:

  • stopPropagation() на внутренних элементах

3. Zoom при scroll

Решение:

  • отключение wheel zoom
map.setOptions({
  scrollwheel: false
});

Работа с кастомными слоями и canvas

При использовании OverlayView тач-события обрабатываются вручную:

class CustomOverlay extends google.maps.OverlayView {
  onAdd() {
    this.div = document.createElement("div");

    this.div.addEventListener("touchstart", (e) => {
      console.log("overlay touch");
    });
  }

  draw() {
    const projection = this.getProjection();
  }
}

Важно учитывать:

  • overlay не получает map events автоматически
  • требуется ручная синхронизация координат
  • жесты могут конфликтовать с картой

Оптимизация touch-интеракций

Практические аспекты производительности:

  • минимизация DOM внутри overlay
  • избегание частых reflow при touchmove
  • использование requestAnimationFrame для реакций на жесты
  • отказ от тяжёлых обработчиков в touchmove

Итоговая модель взаимодействия

Внутренне система тач-событий в Google Maps JavaScript API строится вокруг трёх уровней:

  • браузерные события (touch/pointer/mouse)
  • интерпретация жестов
  • API-события карты

Эта архитектура позволяет единообразно обрабатывать мобильные и десктопные сценарии без прямой работы с низкоуровневыми touch API.