Событийная модель в Mapbox GL JS основана на паттерне наблюдателя, где карта выступает центральным источником событий, а различные компоненты интерфейса и бизнес-логика подписываются на изменения состояния. Все взаимодействие с картой — от загрузки стиля до кликов по слоям — представлено через унифицированную систему событий.
Ключевым объектом является экземпляр Map,
предоставляющий методы регистрации обработчиков и управления их
жизненным циклом. События делятся на системные, пользовательские и
событийные, связанные с источниками данных и слоями визуализации.
Внутри Mapbox GL JS события строятся вокруг внутреннего event emitter. Каждый экземпляр карты поддерживает независимый список подписчиков, привязанных к конкретным типам событий.
События могут возникать из нескольких подсистем:
Каждое событие содержит объект event, который передаёт
контекст: координаты, целевые слои, пиксельные позиции, состояние карты
и дополнительные метаданные.
Основной механизм подписки реализован через методы:
map.on('load', () => {});
map.on('click', (e) => {});
map.on('mousemove', 'layer-id', (e) => {});
Метод on поддерживает перегрузку:
Подписка на слой позволяет ограничить область реакции только теми объектами, которые визуально принадлежат указанному слою.
Удаление обработчиков выполняется через:
map.off('click', handler);
map.off('mousemove', 'layer-id', handler);
Важно учитывать, что ссылка на функцию должна совпадать, иначе удаление не произойдёт.
Одноразовые обработчики реализуются через:
map.once('load', () => {});
После первого вызова подписка автоматически удаляется.
Системные события отражают этапы инициализации и обновления состояния:
load — завершена загрузка стиля и ресурсовidle — карта завершила все рендер-операцииrender — произошёл новый кадр WebGLremove — карта уничтожаетсяerror — произошла ошибка загрузки или выполненияСобытие load критично для выполнения логики, зависящей
от наличия стиля, слоёв и источников.
Событие idle используется для синхронизации состояния
интерфейса с завершением рендеринга, особенно при динамическом
обновлении данных.
Интерактивные события включают обработку ввода пользователя:
clickdblclickmousedown / mouseupmousemovemouseenter / mouseleavetouchstart / touchend /
touchmoveКаждое событие содержит координаты:
event.lngLat — географические координатыevent.point — пиксельные координаты относительно
контейнера картыevent.originalEvent — нативное DOM-событиеПример структуры обработчика:
map.on('click', (e) => {
console.log(e.lngLat);
console.log(e.point);
});
Особенностью событийной модели является возможность привязки к конкретному слою:
map.on('click', 'cities-layer', (e) => {
console.log(e.features);
});
В этом случае выполняется spatial query по отрисованным объектам
слоя. Результат доступен через features, содержащий массив
геометрий, попавших под курсор.
Внутренне используется механизм hit-testing по WebGL-буферу, где каждый отрисованный объект имеет идентификатор, позволяющий сопоставить пиксельное попадание с геометрией источника данных.
События, связанные с источниками (source), позволяют
отслеживать процесс загрузки и обновления данных:
sourcedata — обновление данных источникаdata — общее событие изменения данныхstyledata — изменение стиля картыdataloading — начало загрузкиdataloaded — завершение загрузкиЭти события особенно важны при работе с динамическими GeoJSON или векторными тайлами.
Объект события содержит метаинформацию, позволяющую точно определить источник действия.
Основные поля:
type — тип событияtarget — экземпляр картыfeatures — выбранные геометрииlngLat — координаты событияpoint — экранные координатыoriginalEvent — DOM-событиеВ случае событий рендеринга также доступны поля:
timeStamprenderedFeaturestilesСобытия обрабатываются в порядке регистрации обработчиков. Однако при слоевой привязке сначала выполняется spatial filtering, после чего вызываются только релевантные обработчики.
При совпадении нескольких слоёв порядок определяется порядком их добавления в стиль. Верхние слои получают приоритет при hit-testing.
Распространение событий не полностью аналогично DOM event bubbling. В Mapbox GL JS отсутствует классическая фаза всплытия, вместо этого используется централизованная диспетчеризация.
Сценарии с частым обновлением состояния требуют управления жизненным циклом обработчиков.
Пример паттерна:
function enableInteraction(map) {
const handler = (e) => {
map.getCanvas().style.cursor = 'pointer';
};
map.on('mousemove', 'layer-id', handler);
return () => {
map.off('mousemove', 'layer-id', handler);
};
}
Возврат функции деактивации позволяет управлять подписками в модульных системах.
При большом количестве слоёв и источников критическим становится количество hit-test операций. Оптимизация достигается через:
queryRenderedFeaturesmousemoveСобытие mousemove является наиболее затратным, так как
может вызываться десятки раз в секунду при движении курсора.
Метод queryRenderedFeatures часто используется внутри
обработчиков событий:
map.on('click', (e) => {
const features = map.queryRenderedFeatures(e.point, {
layers: ['cities-layer']
});
});
Это позволяет дополнительно фильтровать объекты без привязки к встроенному layer-handler механизму.
Система поддерживает генерацию кастомных событий через
fire:
map.fire('custom-event', { value: 42 });
Такие события интегрируются в общий event loop и могут обрабатываться
через стандартный on.
Это используется для синхронизации внешних модулей с состоянием карты без прямой зависимости от внутренних механизмов.
Событие error агрегирует ошибки разных подсистем:
Объект ошибки содержит:
error.messageerror.statuserror.sourceIdЦентрализованная обработка позволяет строить устойчивые интерфейсы поверх нестабильных сетевых условий.
Каждое событие render связано с циклом WebGL. Карта
перерисовывается только при изменении состояния или анимации.
События рендера используются для:
При этом чрезмерное использование render может привести
к снижению производительности, поскольку вызывается на каждом кадре.
Отдельную категорию составляют события камеры:
movezoomrotatepitchmovestartmoveendОни отражают изменения состояния визуализации карты и позволяют синхронизировать интерфейсы:
map.on('move', () => {
const center = map.getCenter();
});
Эти события часто используются для построения аналитических панелей и синхронизации нескольких карт.
Внутренняя модель Mapbox связывает слои и источники через граф зависимостей. События изменения данных распространяются по этому графу, вызывая перерендер только затронутых частей сцены.
При обновлении GeoJSON источника инициируется цепочка:
Такая структура минимизирует количество полных перерисовок сцены.
Большинство событий работает в асинхронном контексте. Это означает, что состояние карты может измениться между моментом регистрации события и его вызовом.
Особенно это важно при работе с сетевыми источниками и динамическими
стилями, где задержка между dataloading и
dataloaded может быть значительной.
Событийная система Mapbox GL JS используется как основа для:
События выступают связующим слоем между WebGL-рендерингом и бизнес-логикой приложения, обеспечивая изоляцию визуального слоя от прикладной логики.