Жесты и их обработка

В основе обработки жестов в OpenLayers лежит подсистема взаимодействий (Interactions). Она определяет, каким образом пользователь управляет картой: перемещение, масштабирование, поворот, выделение объектов и комбинированные сценарии ввода. Каждое взаимодействие представляет собой отдельный модуль, который подключается к объекту карты и перехватывает события указателя, мыши или сенсорного ввода.

Архитектура построена вокруг класса ol/interaction/Interaction, от которого наследуются все стандартные жесты. Такой подход позволяет гибко комбинировать поведение карты, добавлять собственные механики и управлять приоритетами обработки событий.


Базовые жесты и стандартные взаимодействия

OpenLayers включает набор готовых взаимодействий, которые покрывают большинство сценариев работы с картой.

Перемещение карты (DragPan)

Жест перемещения реализован через взаимодействие перетаскивания карты.

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

  • срабатывает при зажатой левой кнопке мыши или одном пальце на тач-устройстве
  • изменяет центр карты
  • поддерживает инерцию (momentum)

Класс: ol/interaction/DragPan

Ключевая логика основана на вычислении смещения указателя между событиями pointerdown и pointermove, после чего пересчитывается центр карты в координатах проекции.


Масштабирование колесом мыши (MouseWheelZoom)

Колёсная прокрутка преобразуется в изменение масштаба карты.

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

  • дискретное или плавное масштабирование
  • настройка скорости реакции
  • поддержка интервалов между событиями для сглаживания

Класс: ol/interaction/MouseWheelZoom

Внутри взаимодействия используется накопление дельты прокрутки и преобразование её в коэффициент масштабирования с экспоненциальной функцией.


Двойной клик (DoubleClickZoom)

Двойной клик выполняет приближение к точке курсора.

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

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

Класс: ol/interaction/DoubleClickZoom

Данный жест полезен в сценариях быстрого навигационного приближения без использования колесика мыши.


Масштабирование жестом (PinchZoom)

Для сенсорных устройств применяется pinch-жест (сведение/разведение пальцев).

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

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

Класс: ol/interaction/PinchZoom

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


Поворот карты (PinchRotate)

Часто совмещается с pinch-zoom.

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

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

Класс: ol/interaction/PinchRotate


Управление набором жестов

В OpenLayers набор взаимодействий задаётся при создании карты.

import Map from 'ol/Map.js';
import View from 'ol/View.js';
import {defaults as defaultInteractions} from 'ol/interaction/defaults.js';

const map = new Map({
  target: 'map',
  view: new View({
    center: [0, 0],
    zoom: 2
  }),
  interactions: defaultInteractions()
});

Функция defaultInteractions() возвращает стандартный набор жестов:

  • DragPan
  • MouseWheelZoom
  • DoubleClickZoom
  • PinchZoom
  • PinchRotate

Отключение и модификация жестов

Контроль над поведением осуществляется путём явного задания списка взаимодействий.

Пример отключения вращения:

import {defaults as defaultInteractions, MouseWheelZoom, DragPan} from 'ol/interaction.js';

const map = new Map({
  target: 'map',
  interactions: defaultInteractions({
    mouseWheelZoom: true,
    doubleClickZoom: true,
    pinchRotate: false
  })
});

Другой подход — полное переопределение набора:

const map = new Map({
  interactions: [
    new DragPan(),
    new MouseWheelZoom()
  ]
});

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


Условная активация жестов

Каждое взаимодействие поддерживает функцию condition, которая определяет, когда жест должен срабатывать.

Пример ограничения DragPan только при зажатом Shift:

import DragPan from 'ol/interaction/DragPan.js';
import {shiftKeyOnly} from 'ol/events/condition.js';

const dragPan = new DragPan({
  condition: shiftKeyOnly
});

Функции условий:

  • singleClick
  • doubleClick
  • shiftKeyOnly
  • altKeyOnly
  • platformModifierKeyOnly

Механизм основан на анализе объекта события pointer/keyboard и позволяет строить сложные комбинации управления.


Приоритеты взаимодействий

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

Каждое взаимодействие имеет:

  • priority (внутренний порядок обработки)
  • состояние активности (active)
  • возможность блокировки событий (stopPropagation)

Например, PinchZoom имеет более высокий приоритет, чем DragPan, чтобы мультитач не интерпретировался как перемещение карты.


Обработка pointer-событий

Современная реализация OpenLayers опирается на Pointer Events.

Основной поток событий:

  • pointerdown — начало жеста
  • pointermove — изменение положения
  • pointerup — завершение жеста
  • pointercancel — прерывание системой

Каждое взаимодействие подписывается на эти события через единый диспетчер и получает унифицированный объект события, содержащий:

  • координаты в пикселях
  • состояние клавиш модификации
  • идентификатор pointer-а

Кастомные жесты и собственные взаимодействия

Создание собственного жеста выполняется через наследование от базового класса Interaction.

import Interaction from 'ol/interaction/Interaction.js';

class CustomDrag extends Interaction {
  constructor() {
    super();
  }

  handleEvent(mapBrowserEvent) {
    if (mapBrowserEvent.type === 'pointerdrag') {
      const deltaX = mapBrowserEvent.pixel[0];
      const deltaY = mapBrowserEvent.pixel[1];

      // пользовательская логика обработки
    }

    return true;
  }
}

Метод handleEvent является центральной точкой обработки событий. Он может:

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

Работа с инерцией и анимациями жестов

Многие жесты используют внутреннюю систему анимации view.

При DragPan после отпускания указателя может применяться инерция:

  • вычисляется скорость последнего движения
  • создаётся анимация смещения центра
  • применяется затухание (deceleration)

Параметры:

  • decay — коэффициент затухания
  • duration — длительность анимации
  • easing — функция плавности

Сенсорные устройства и мультитач-логика

На мобильных устройствах жесты обрабатываются через комбинацию pointer-идентификаторов.

Основные сценарии:

  • один палец — перемещение
  • два пальца — масштабирование и вращение
  • три и более — блокировка или пользовательская обработка

Система отслеживает:

  • количество активных pointer-ов
  • их относительные координаты
  • изменение геометрии между кадрами

Блокировка и приостановка жестов

Любое взаимодействие может быть временно отключено:

interaction.setActive(false);

Это используется при:

  • открытии модальных окон
  • рисовании поверх карты
  • загрузке данных и блокировке UI

Также возможно динамическое включение/выключение отдельных жестов в зависимости от состояния приложения.


Комбинирование жестов с рисованием и выделением

При добавлении взаимодействий рисования (Draw, Select) важно учитывать конфликт жестов.

Типичная конфигурация:

  • DragPan отключается при рисовании
  • Select перехватывает click-события
  • DoubleClickZoom отключается при редактировании объектов

Система разрешения конфликтов строится на:

  • порядке добавления взаимодействий
  • приоритетах
  • условиях condition

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

При интенсивном взаимодействии критичны:

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

Особенно нагруженными являются:

  • PinchZoom (много вычислений геометрии)
  • DragPan с инерцией
  • кастомные обработчики pointermove

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