В MapLibre GL JS обработка событий на уровне слоёв строится вокруг привязки интерактивных обработчиков не к карте в целом, а к конкретным слоям, что позволяет работать с объектами данных (features) напрямую, минуя ручной рейтрейсинг или сложную логику определения попадания курсора.
Ключевой механизм выглядит через перегрузку метода
map.on, где вторым аргументом передаётся идентификатор
слоя:
map.on('click', 'cities-layer', (e) => {
console.log(e.features);
});
Такой подход позволяет библиотеке автоматически выполнять выборку объектов слоя под курсором и передавать их в обработчик.
MapLibre GL JS поддерживает набор событий, которые могут быть привязаны к конкретному слою. Они делятся на несколько групп: указательные, сенсорные и вспомогательные.
Указательные события:
clickdblclickmousedownmouseupmousemoveСобытия наведения:
mouseentermouseleavemouseover (в некоторых сценариях эквивалентен
mouseenter)mouseoutСенсорные события:
touchstarttouchendtouchcancelЭти события работают только для векторных слоёв, содержащих
интерактивные features (например, fill, line,
circle, symbol).
При срабатывании layer-event обработчику передаётся объект события, расширенный по сравнению с обычными DOM-событиями карты.
Основные поля:
features — массив объектов GeoJSON, попавших под
курсорpoint — координаты курсора в пикселях относительно
контейнера картыlngLat — географические координаты точки событияoriginalEvent — нативное DOM-событие мыши или
touchtype — тип событияПример доступа к данным:
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();
});
Это предотвращает срабатывание обработчиков на более низких слоях или глобальных событиях карты.
Особенно важно при наличии:
MapLibre GL JS разделяет два уровня событий:
События карты:
map.on('click', (e) => {});
События слоя:
map.on('click', 'layer-id', (e) => {});
Разница заключается в механизме выборки:
Layer events более специализированы и эффективны при работе с векторными данными.
Событие mousemove на слоях может генерироваться с
высокой частотой, что приводит к нагрузке на основной поток
JavaScript.
Для оптимизации применяются:
requestAnimationFrameПример:
let lastTime = 0;
map.on('mousemove', 'roads-layer', (e) => {
const now = performance.now();
if (now - lastTime < 16) return;
lastTime = now;
// логика обработки
});
В некоторых слоях под курсор попадает сразу несколько объектов. Это типично для:
Обработчик получает массив features, и порядок элементов
зависит от глубины и порядка рендеринга:
map.on('click', 'zones-layer', (e) => {
e.features.forEach((f) => {
console.log(f.id, f.properties);
});
});
Для слоёв типа symbol поведение событий может
отличаться:
Пример обработки кластеров:
map.on('click', 'clusters', (e) => {
const clusterId = e.features[0].properties.cluster_id;
});
Иногда события слоёв недостаточны, особенно при кастомной логике взаимодействия. В таких случаях применяется прямой запрос:
map.on('click', (e) => {
const features = map.queryRenderedFeatures(e.point, {
layers: ['cities-layer']
});
});
Это позволяет:
В реальных приложениях события слоёв часто комбинируются:
Такая модель формирует реактивную систему взаимодействия, где слой выступает единицей интерактивности, а не только визуализации.