Обработка кликов и перемещений мыши

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

Основной объект, через который проходит вся интерактивность, — экземпляр ol/Map. Он наследует EventTarget-подобную модель и генерирует события разного уровня:

  • события мыши (click, dblclick, singleclick)
  • pointer-события (pointermove, pointerdrag)
  • события состояния карты (moveend, movestart, postrender)

Базовая регистрация обработчика:

map.on('click', function (event) {
  console.log(event.coordinate);
});

Объект event содержит важные данные:

  • coordinate — координаты в системе карты
  • pixel — пиксель на холсте
  • map — ссылка на карту
  • originalEvent — исходное DOM-событие

Разница между click и singleclick

В OpenLayers событие click может конфликтовать с drag-операциями, поэтому чаще используется singleclick.

  • click — срабатывает сразу при нажатии/отпускании
  • singleclick — срабатывает после определения, что это не drag и не double click
map.on('singleclick', function (event) {
  console.log('Одиночный клик:', event.coordinate);
});

Использование singleclick предпочтительно при работе с выбором объектов.

Получение объектов по координате клика

Одна из наиболее частых задач — определить, по каким объектам кликнул пользователь.

getFeaturesAtPixel

map.on('singleclick', function (event) {
  const features = map.getFeaturesAtPixel(event.pixel);

  features.forEach(f => {
    console.log(f.get('name'));
  });
});

Метод работает через рендер-слой и быстро возвращает все объекты в точке.

forEachFeatureAtPixel

Более гибкий вариант:

map.on('singleclick', function (event) {
  map.forEachFeatureAtPixel(event.pixel, function (feature, layer) {
    console.log('Feature:', feature);
    console.log('Layer:', layer);
  });
});

Позволяет:

  • фильтровать слои
  • управлять порядком обхода
  • оптимизировать hit detection

Определение координат клика

OpenLayers предоставляет удобные методы трансформации пикселей в координаты карты:

map.on('click', function (event) {
  const coord = event.coordinate;
  console.log(coord);
});

Также возможно ручное вычисление:

const coordinate = map.getCoordinateFromPixel(event.pixel);

Это полезно при работе вне событий карты.

Обработка движения мыши (pointermove)

Событие pointermove используется для:

  • hover-подсветки объектов
  • отображения координат
  • динамических тултипов
  • предварительного выбора объектов
map.on('pointermove', function (event) {
  const pixel = event.pixel;

  map.forEachFeatureAtPixel(pixel, function (feature) {
    feature.setStyle(highlightStyle);
  });
});

Оптимизация pointermove

Событие pointermove генерируется очень часто, поэтому требуется контроль частоты обработки.

Простейшая защита от перегрузки

let lastTime = 0;

map.on('pointermove', function (event) {
  const now = Date.now();
  if (now - lastTime < 50) return;
  lastTime = now;

  // обработка
});

Использование requestAnimationFrame

let pending = false;

map.on('pointermove', function (event) {
  if (pending) return;
  pending = true;

  requestAnimationFrame(() => {
    pending = false;

    map.forEachFeatureAtPixel(event.pixel, function (feature) {
      // обработка hover
    });
  });
});

Hover-подсветка объектов

Типовой сценарий — изменение стиля объекта при наведении.

let lastFeature = null;

map.on('pointermove', function (event) {
  if (lastFeature) {
    lastFeature.setStyle(undefined);
    lastFeature = null;
  }

  map.forEachFeatureAtPixel(event.pixel, function (feature) {
    feature.setStyle(highlightStyle);
    lastFeature = feature;
    return true;
  });
});

Важно сбрасывать стиль при уходе курсора с объекта, иначе состояние останется изменённым.

Работа с курсором

Изменение курсора при наведении на объект реализуется через DOM-стиль контейнера карты:

map.on('pointermove', function (event) {
  const hit = map.hasFeatureAtPixel(event.pixel);

  map.getTargetElement().style.cursor = hit ? 'pointer' : '';
});

Это создаёт привычный UX как в интерактивных веб-картах.

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

OpenLayers различает движение карты и движение указателя:

  • pointerdrag — пользователь тащит карту
  • pointermove — движение без перетаскивания
map.on('pointerdrag', function (event) {
  console.log('Карта перемещается');
});

Это важно для отключения hover-логики во время drag:

let isDragging = false;

map.on('pointerdrag', () => isDragging = true);
map.on('moveend', () => isDragging = false);

Select interaction вместо ручной обработки

Для выбора объектов часто используется встроенная интеракция:

import Select fr om 'ol/interaction/Select.js';

const select = new Select();
map.addInteraction(select);

select.on('select', function (event) {
  console.log(event.selected);
});

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

  • автоматический hit detection
  • поддержка multiple selection
  • интеграция с стилями

Отличие event.coordinate и pixel

Ключевое различие:

  • pixel — координаты на canvas (в пикселях экрана)
  • coordinate — координаты карты (в проекции слоя)

Пример преобразования:

map.on('click', function (event) {
  const pixel = event.pixel;
  const coordinate = event.coordinate;
});

Использование pixel критично для hit detection, coordinate — для геоданных.

Фильтрация слоёв при клике

Иногда необходимо реагировать только на определённые слои:

map.on('singleclick', function (event) {
  map.forEachFeatureAtPixel(event.pixel, function (feature, layer) {
    if (layer.get('name') === 'cities') {
      console.log(feature.getProperties());
    }
  });
});

Работа с overlay при клике

Частый сценарий — открытие всплывающего окна:

map.on('singleclick', function (event) {
  const coordinate = event.coordinate;

  overlay.setPosition(coordinate);
  popup.innerHTML = 'Информация об объекте';
});

Overlay привязывается к координатам карты, а не к пикселям.

Комбинирование click и pointermove

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

  • pointermove — hover-подсветка
  • singleclick — выбор объекта
  • dblclick — zoom или дополнительное действие
map.on('pointermove', hoverHandler);
map.on('singleclick', clickHandler);
map.on('dblclick', zoomHandler);

Разделение логики снижает конфликт событий и улучшает отзывчивость интерфейса.

Предотвращение ложных срабатываний

При сложных взаимодействиях важно учитывать:

  • задержки между drag и click
  • случайные движения мыши
  • многослойные объекты

Подход с проверкой пикселя:

map.on('singleclick', function (event) {
  const hit = map.hasFeatureAtPixel(event.pixel);
  if (!hit) return;
});

Использование hit tolerance

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

map.on('singleclick', function (event) {
  map.forEachFeatureAtPixel(event.pixel, function (feature) {
    console.log(feature);
  }, {
    hitTolerance: 5
  });
});

Это особенно полезно для точечных объектов.

События уровня карты при перемещении

Помимо мыши, движение карты также влияет на взаимодействие:

map.on('moveend', function () {
  console.log('Перемещение завершено');
});

Используется для:

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