Система событий в Mapbox GL JS построена вокруг механизма подписки на события объектов карты, источников данных, маркеров, всплывающих окон и других компонентов. События позволяют реагировать на действия пользователя, изменения состояния карты, загрузку ресурсов и взаимодействие со слоями.
Основным методом регистрации обработчиков является
on():
map.on('click', () => {
console.log('Карта была нажата');
});
Для удаления обработчика используется метод off():
function handleClick() {
console.log('Клик');
}
map.on('click', handleClick);
map.off('click', handleClick);
Для однократного выполнения применяется метод
once():
map.once('load', () => {
console.log('Карта загружена');
});
Такой обработчик автоматически удаляется после первого вызова.
Одной из наиболее важных групп событий являются события жизненного цикла карты.
Срабатывает после полной инициализации карты и загрузки базового стиля.
map.on('load', () => {
console.log('Карта готова к работе');
});
Большинство операций по добавлению слоев и источников рекомендуется
выполнять именно внутри обработчика load.
map.on('load', () => {
map.addSource('cities', {
type: 'geojson',
data: '/data/cities.geojson'
});
map.addLayer({
id: 'cities-layer',
type: 'circle',
source: 'cities'
});
});
Срабатывает, когда карта полностью завершила все операции рендеринга и загрузки данных.
map.on('idle', () => {
console.log('Все данные загружены');
});
Событие полезно для выполнения тяжелых вычислений после окончания работы движка визуализации.
Возникает при удалении карты из DOM.
map.on('remove', () => {
console.log('Карта уничтожена');
});
Mapbox GL JS предоставляет полный набор событий для работы с мышью.
Срабатывает при нажатии кнопки мыши.
map.on('click', (event) => {
console.log(event.lngLat);
});
Объект события содержит координаты точки клика.
map.on('click', (event) => {
console.log(event.lngLat.lng);
console.log(event.lngLat.lat);
});
Срабатывает при двойном клике.
map.on('dblclick', (event) => {
console.log('Двойной клик');
});
Используются для отслеживания нажатия и отпускания кнопки мыши.
map.on('mousedown', () => {
console.log('Нажатие');
});
map.on('mouseup', () => {
console.log('Отпускание');
});
Вызывается при каждом перемещении указателя.
map.on('mousemove', (event) => {
console.log(event.lngLat);
});
Частое срабатывание требует осторожности при выполнении ресурсоемких операций.
Используются для отслеживания входа и выхода курсора из слоя.
map.on('mouseenter', 'cities-layer', () => {
map.getCanvas().style.cursor = 'pointer';
});
map.on('mouseleave', 'cities-layer', () => {
map.getCanvas().style.cursor = '';
});
Для мобильных устройств доступны специальные события.
map.on('touchstart', (event) => {
console.log('Касание началось');
});
map.on('touchmove', (event) => {
console.log('Палец перемещается');
});
map.on('touchend', (event) => {
console.log('Касание завершено');
});
Эти события позволяют реализовывать собственную логику работы с жестами.
Навигация по карте сопровождается большим количеством событий.
Срабатывает в начале перемещения.
map.on('movestart', () => {
console.log('Начало перемещения');
});
Вызывается во время движения карты.
map.on('move', () => {
console.log(map.getCenter());
});
Срабатывает после завершения перемещения.
map.on('moveend', () => {
console.log('Перемещение завершено');
});
Практический пример:
map.on('moveend', () => {
const center = map.getCenter();
loadObjects(
center.lng,
center.lat
);
});
Такой подход часто используется для подгрузки данных в зависимости от текущей области просмотра.
map.on('zoomstart', () => {
console.log('Начало масштабирования');
});
map.on('zoom', () => {
console.log(map.getZoom());
});
map.on('zoomend', () => {
console.log('Масштабирование завершено');
});
Пример отображения текущего масштаба:
map.on('zoom', () => {
document.getElementById('zoom').textContent =
map.getZoom().toFixed(2);
});
map.on('rotatestart', () => {
console.log('Начало вращения');
});
map.on('rotate', () => {
console.log(map.getBearing());
});
map.on('rotateend', () => {
console.log('Вращение завершено');
});
Получение текущего угла поворота:
map.on('rotate', () => {
const bearing = map.getBearing();
console.log(bearing);
});
map.on('pitchstart', () => {
console.log('Начало изменения наклона');
});
map.on('pitch', () => {
console.log(map.getPitch());
});
map.on('pitchend', () => {
console.log('Наклон изменен');
});
Если размеры контейнера изменяются, карта генерирует соответствующее событие.
map.on('resize', () => {
console.log('Размер карты изменился');
});
Пример адаптации интерфейса:
map.on('resize', () => {
updateSidebarLayout();
});
Большинство обработчиков получают объект события.
Пример структуры:
map.on('click', (event) => {
console.log(event.point);
console.log(event.lngLat);
console.log(event.originalEvent);
});
Позиция курсора в пикселях.
map.on('click', (event) => {
console.log(event.point.x);
console.log(event.point.y);
});
Географические координаты.
map.on('click', (event) => {
console.log(event.lngLat.lng);
console.log(event.lngLat.lat);
});
Оригинальное DOM-событие браузера.
map.on('click', (event) => {
console.log(event.originalEvent);
});
Mapbox GL JS позволяет подписываться на события конкретного слоя.
map.on('click', 'cities-layer', (event) => {
console.log(event.features);
});
В этом случае обработчик срабатывает только при взаимодействии с объектами указанного слоя.
map.on('click', 'cities-layer', (event) => {
const feature = event.features[0];
console.log(feature.properties.name);
});
События часто используются вместе с Popup.
map.on('click', 'cities-layer', (event) => {
const feature = event.features[0];
new mapboxgl.Popup()
.setLngLat(event.lngLat)
.setHTML(`
<h3>${feature.properties.name}</h3>
`)
.addTo(map);
});
Один из самых распространенных сценариев.
map.on('mouseenter', 'cities-layer', () => {
map.getCanvas().style.cursor = 'pointer';
});
map.on('mouseleave', 'cities-layer', () => {
map.getCanvas().style.cursor = '';
});
Подсветка выбранного объекта:
map.on('mousemove', 'cities-layer', (event) => {
const featureId = event.features[0].id;
map.setFeatureState(
{
source: 'cities',
id: featureId
},
{
hover: true
}
);
});
Срабатывает при изменении данных источника.
map.on('sourcedata', (event) => {
console.log(event.sourceId);
});
Универсальное событие работы с данными.
map.on('data', (event) => {
console.log(event.dataType);
});
Вызывается после изменения стиля.
map.on('styledata', () => {
console.log('Стиль обновлен');
});
map.on('sourcedata', (event) => {
if (
event.sourceId === 'cities' &&
event.isSourceLoaded
) {
console.log('GeoJSON загружен');
}
});
Такой подход полезен при последовательной загрузке нескольких наборов данных.
Для обработки ошибок предусмотрено событие error.
map.on('error', (event) => {
console.error(event.error);
});
Пример централизованного логирования:
map.on('error', ({ error }) => {
sendErrorToServer(error);
});
Нередко требуется отслеживать несколько состояний одновременно.
let isDragging = false;
map.on('movestart', () => {
isDragging = true;
});
map.on('moveend', () => {
isDragging = false;
});
Другой пример:
map.on('zoomend', updateData);
map.on('moveend', updateData);
function updateData() {
console.log('Обновление данных');
}
Регистрация обработчиков может происходить во время выполнения приложения.
function enableTracking() {
map.on('mousemove', trackCursor);
}
function disableTracking() {
map.off('mousemove', trackCursor);
}
function trackCursor(event) {
console.log(event.lngLat);
}
Такой подход позволяет уменьшать нагрузку на приложение.
Вместо создания отдельных обработчиков для каждого слоя часто используется единая точка обработки.
map.on('click', (event) => {
const features = map.queryRenderedFeatures(
event.point
);
if (!features.length) {
return;
}
const feature = features[0];
switch (feature.layer.id) {
case 'cities-layer':
showCity(feature);
break;
case 'roads-layer':
showRoad(feature);
break;
}
});
Преимущества подхода:
Событие move может вызываться десятки раз в секунду.
Неэффективный вариант:
map.on('move', loadData);
Предпочтительный вариант:
map.on('moveend', loadData);
При удалении компонентов интерфейса необходимо снимать подписки.
map.off('click', handleClick);
Это предотвращает утечки памяти.
Нежелательно выполнять сетевые запросы внутри событий:
map.on('mousemove', () => {
fetch('/api/data');
});
Лучше использовать ограничение частоты вызовов:
const throttledUpdate = throttle(updateData, 300);
map.on('mousemove', throttledUpdate);
Вместо анонимных обработчиков:
map.on('click', function(event) {
processFeature(event);
});
Предпочтительно:
function handleMapClick(event) {
processFeature(event);
}
map.on('click', handleMapClick);
Такой код легче тестировать, переиспользовать и удалять через
off().