События слоев

В MapLibre GL JS обработка событий на уровне слоёв строится вокруг привязки интерактивных обработчиков не к карте в целом, а к конкретным слоям, что позволяет работать с объектами данных (features) напрямую, минуя ручной рейтрейсинг или сложную логику определения попадания курсора.

Ключевой механизм выглядит через перегрузку метода map.on, где вторым аргументом передаётся идентификатор слоя:

map.on('click', 'cities-layer', (e) => {
    console.log(e.features);
});

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


Типы событий слоёв

MapLibre GL JS поддерживает набор событий, которые могут быть привязаны к конкретному слою. Они делятся на несколько групп: указательные, сенсорные и вспомогательные.

Указательные события:

  • click
  • dblclick
  • mousedown
  • mouseup
  • mousemove

События наведения:

  • mouseenter
  • mouseleave
  • mouseover (в некоторых сценариях эквивалентен mouseenter)
  • mouseout

Сенсорные события:

  • touchstart
  • touchend
  • touchcancel

Эти события работают только для векторных слоёв, содержащих интерактивные features (например, fill, line, circle, symbol).


Структура объекта события

При срабатывании layer-event обработчику передаётся объект события, расширенный по сравнению с обычными DOM-событиями карты.

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

  • features — массив объектов GeoJSON, попавших под курсор
  • point — координаты курсора в пикселях относительно контейнера карты
  • lngLat — географические координаты точки события
  • originalEvent — нативное DOM-событие мыши или touch
  • type — тип события

Пример доступа к данным:

map.on('click', 'buildings-layer', (e) => {
    const feature = e.features[0];
    console.log(feature.properties);
});

Механизм выборки объектов

При использовании событий слоя MapLibre GL JS выполняет внутреннюю операцию queryRenderedFeatures, ограниченную конкретным слоем. Это означает:

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

Фактически слой становится фильтром для пространственного запроса по экранным координатам курсора.


Приоритет слоёв и порядок попадания

Если несколько слоёв перекрывают друг друга, порядок обработки зависит от порядка их отрисовки в стиле (style layers order).

Слой, расположенный выше в списке стиля, имеет приоритет при определении features под курсором.

Это поведение важно при работе с перекрывающимися полигонами:

  • верхний слой «перехватывает» событие
  • нижние слои могут не получить данные, если не выполнен отдельный запрос

Для обхода этой особенности используется ручной запрос:

map.queryRenderedFeatures(point, {
    layers: ['layer1', 'layer2']
});

Наведение и управление состоянием интерфейса

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

Типичный сценарий — изменение курсора и подсветка объекта:

map.on('mouseenter', 'cities-layer', () => {
    map.getCanvas().style.cursor = 'pointer';
});

map.on('mouseleave', 'cities-layer', () => {
    map.getCanvas().style.cursor = '';
});

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

map.setFeatureState(
    { source: 'cities', id: feature.id },
    { hover: true }
);

И далее в стиле:

'fill-opacity': [
    'case',
    ['boolean', ['feature-state', 'hover'], false],
    1,
    0.5
]

Прерывание всплытия событий

Layer events поддерживают механизм остановки распространения события, аналогичный DOM.

map.on('click', 'layer-1', (e) => {
    e.originalEvent.stopPropagation();
});

Это предотвращает срабатывание обработчиков на более низких слоях или глобальных событиях карты.

Особенно важно при наличии:

  • кластеров
  • вложенных слоёв
  • интерактивных зон поверх базовых объектов

Отличие layer events от map events

MapLibre GL JS разделяет два уровня событий:

События карты:

map.on('click', (e) => {});

События слоя:

map.on('click', 'layer-id', (e) => {});

Разница заключается в механизме выборки:

  • map events не знают о GeoJSON features
  • layer events автоматически выполняют hit-testing по данным слоя

Layer events более специализированы и эффективны при работе с векторными данными.


Производительность и частые события

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

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

  • throttling через requestAnimationFrame
  • временное отключение обработчиков
  • ограничение обновлений состояния

Пример:

let lastTime = 0;

map.on('mousemove', 'roads-layer', (e) => {
    const now = performance.now();
    if (now - lastTime < 16) return;
    lastTime = now;

    // логика обработки
});

Работа с несколькими feature одновременно

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

  • пересекающихся полигонов
  • символов с кластерами
  • многослойных линий

Обработчик получает массив features, и порядок элементов зависит от глубины и порядка рендеринга:

map.on('click', 'zones-layer', (e) => {
    e.features.forEach((f) => {
        console.log(f.id, f.properties);
    });
});

Особенности событий символов и кластеров

Для слоёв типа symbol поведение событий может отличаться:

  • один пиксель может содержать несколько символов
  • hit-test учитывает collision detection
  • при включённой кластеризации возвращается кластер как единый feature

Пример обработки кластеров:

map.on('click', 'clusters', (e) => {
    const clusterId = e.features[0].properties.cluster_id;
});

Использование queryRenderedFeatures вне событий

Иногда события слоёв недостаточны, особенно при кастомной логике взаимодействия. В таких случаях применяется прямой запрос:

map.on('click', (e) => {
    const features = map.queryRenderedFeatures(e.point, {
        layers: ['cities-layer']
    });
});

Это позволяет:

  • комбинировать несколько слоёв
  • изменять приоритет выборки
  • обрабатывать пользовательские сценарии выбора

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

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

  • hover → подсветка через feature-state
  • click → открытие панели информации
  • mousemove → динамическая подсказка
  • mouseleave → сброс состояния

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