Архитектура событий в MapLibre GL JS строится вокруг централизованной
модели, где большинство объектов наследуют поведение от внутреннего
класса Evented. Это обеспечивает единый механизм подписки,
удаления подписок и одноразового срабатывания обработчиков.
Ключевая особенность модели заключается в разделении событий по источнику:
Каждое событие представляет собой объект с предсказуемой структурой, содержащий тип события, координаты, контекст источника и дополнительные метаданные (в зависимости от типа события).
Основные методы управления событиями остаются стабильным ядром API:
on(type, listener) — регистрация обработчикаoff(type, listener) — удаление обработчикаonce(type, listener) — одноразовая подпискаСобытия могут быть привязаны как к карте, так и к конкретным слоям через префиксирование идентификатора слоя:
map.on('click', (e) => {
console.log('click anywhere on map');
});
map.on('click', 'cities-layer', (e) => {
console.log('click on feature in cities-layer');
});
Механизм делегирования событий слоям работает через внутренний hit-testing рендерера: при клике вычисляются пересечения с геометрией в текущем viewport.
Одним из ключевых направлений эволюции MapLibre GL JS стало перераспределение ответственности между UI-потоком и worker-потоками. Это напрямую повлияло на поведение событий.
Ранее часть событий (особенно связанных с данными и стилем) формировалась в основном потоке. В современных версиях логика переработана:
data и dataloading стали более
асинхронными и предсказуемыми.Это привело к изменению временных характеристик событий: порядок их срабатывания стал менее зависим от UI-потока.
События жизненного цикла карты претерпели уточнение семантики:
load теперь строго означает завершение загрузки стиля и
базовых ресурсов;idle стал более строгим индикатором отсутствия активных
задач рендера;render может вызываться чаще из-за оптимизаций
частичного обновления кадров.В результате поведение idle стало менее «шумным» и более
пригодным для триггеров автоматических действий (например, экспорт
изображения карты или запуск аналитики состояния).
События, связанные с источниками данных, получили более детализированную структуру.
Событие data теперь содержит расширенные поля:
dataType (source, style, tiles, glyphs и т.д.)sourceIdtileId (при наличии)isSourceLoadedЭто позволило точнее отслеживать, какой именно сегмент данных вызвал обновление.
Событие загрузки данных стало более частым, но менее «тяжёлым» с точки зрения блокировки потока. Оно больше не гарантирует немедленного завершения загрузки, а лишь сигнализирует о начале асинхронного процесса.
Одним из значимых изменений стала переработка определения попадания курсора в геометрические объекты.
Ранее hit-testing мог выполняться синхронно в UI-потоке, что приводило к лагам на сложных сценах. В новых версиях:
Это изменило поведение событий:
mousemove стал менее «тяжёлым»;mouseenter и mouseleave стали более
стабильными при высокой плотности объектов;MapLibre GL JS постепенно унифицирует взаимодействие через pointer-события:
pointerdownpointeruppointermovepointerenterpointerleaveПри этом сохраняется совместимость с классическими mouse
и touch событиями, но внутренняя реализация опирается на
pointer abstraction layer.
Это изменение упростило обработку мульти-тач жестов и унифицировало поведение на разных устройствах.
Событие render теперь теснее связано с внутренним циклом
WebGL:
postrender (если используется в кастомных сборках или
расширениях) стал важен для синхронизации внешних WebGL-слоёв.
В новых версиях наблюдается рост частоты render-событий
из-за оптимизаций частичного перерисовывания слоёв. Это изменило
практику их использования:
Событие styledata стало более детализированным:
Это позволило строить более точные системы динамического изменения стиля без необходимости полной перезагрузки карты.
Введение промежуточных состояний загрузки стиля изменило поведение цепочек событий:
Событие error стало более структурированным:
Это позволило унифицировать обработку ошибок на уровне приложения:
Внутренний event loop был переработан:
Это привело к следующим последствиям:
data → render → idle;Многие события теперь привязаны к циклу
requestAnimationFrame, что влияет на:
move и zoom события;render цикл;Это делает поведение более согласованным с браузерным рендерингом, но увеличивает задержку между действием пользователя и фактическим событием.
События камеры стали более «агрегированными»:
movestart вызывается реже, с более строгими условиями
начала движения;move может быть пропущен при минимальных
изменениях;moveend теперь зависит от стабилизации рендера, а не
только от завершения жеста.События масштабирования и вращения получили более точную синхронизацию:
zoom теперь чаще сопровождается
render;rotate учитывает инерционные анимации;MapLibre GL JS сохранил возможность генерации пользовательских
событий через fire, однако поведение изменилось:
map.fire('custom-event', { payload: { id: 1 } });
В новых версиях такие события интегрируются в общий event queue, что повышает стабильность, но снижает предсказуемость точного момента вызова.
Механизм off стал более строгим:
Особенно важно при частом создании/удалении карт:
remove().При работе с vector tiles изменилось поведение событий
клика:
click,
hover и mousemove.Это устранило ряд проблем, когда один и тот же пиксель мог давать разные feature при быстром зуме.
Оптимизации событийной системы напрямую связаны с производительностью WebGL-рендеринга:
Следствием стало: