Метрики времени рендеринга в Mapbox GL JS опираются на несколько уровней: жизненный цикл карты, события библиотеки, браузерные API измерения производительности и низкоуровневые особенности WebGL. Корректная оценка времени рендеринга требует разделения понятий «первый визуальный кадр», «полная загрузка стиля», «стабилизация слоя данных» и «устойчивый FPS в интерактиве».
При создании экземпляра карты Mapbox GL JS проходит последовательность состояний, каждое из которых можно использовать как точку отсчёта:
С точки зрения измерений важно различать:
Первый визуальный рендер (first paint карты) — момент, когда карта впервые становится видимой. Полная загрузка (fully loaded) — когда все ресурсы стиля и источников загружены. Состояние idle — когда нет активных загрузок и перерисовок.
Mapbox GL JS предоставляет набор событий, которые используются как якоря для измерения времени.
loadСрабатывает, когда стиль загружен и карта готова к дальнейшей работе.
renderВызывается при каждом кадре рендеринга. Используется для измерения частоты и длительности отрисовки.
idleКлючевое событие для определения завершения всех асинхронных операций:
dataСрабатывает при загрузке или изменении данных.
Эти события формируют базовую шкалу измерений:
const start = performance.now();
map.on('load', () => {
const loadTime = performance.now() - start;
console.log('Load time:', loadTime);
});
Для точных измерений предпочтительно использовать
performance.mark и performance.measure, так
как они позволяют структурировать таймлайны.
performance.mark('map-start');
map.on('load', () => {
performance.mark('map-load');
performance.measure('map-load-time', 'map-start', 'map-load');
const measure = performance.getEntriesByName('map-load-time')[0];
console.log(measure.duration);
});
Преимущество такого подхода заключается в интеграции с DevTools Performance Timeline, где можно визуально анализировать узкие места.
Первый кадр — критическая метрика для UX, особенно при тяжёлых стилях.
Mapbox GL JS не предоставляет прямого события «first render
complete», поэтому используется комбинация render и
requestAnimationFrame.
let firstFrame = false;
const start = performance.now();
map.on('render', () => {
if (!firstFrame) {
firstFrame = true;
requestAnimationFrame(() => {
const firstPaintTime = performance.now() - start;
console.log('First render:', firstPaintTime);
});
}
});
Этот подход фиксирует момент, когда WebGL действительно отрисовал первый кадр после начала рендера.
Состояние idle часто используется как более строгий
критерий завершённой загрузки, чем load.
const start = performance.now();
map.on('idle', () => {
const idleTime = performance.now() - start;
console.log('Map fully idle:', idleTime);
});
Важно учитывать, что idle может срабатывать неоднократно
при динамическом изменении источников.
Для однократного измерения используют флаг:
let done = false;
map.on('idle', () => {
if (done) return;
done = true;
const t = performance.now() - start;
console.log('Final idle:', t);
});
Для оценки плавности интерактивного рендеринга используется
requestAnimationFrame.
let last = performance.now();
let frames = 0;
let fpsSum = 0;
function loop() {
const now = performance.now();
const delta = now - last;
last = now;
const fps = 1000 / delta;
fpsSum += fps;
frames++;
requestAnimationFrame(loop);
}
loop();
setTimeout(() => {
console.log('Average FPS:', fpsSum / frames);
}, 5000);
Такой метод позволяет оценить:
Mapbox GL JS использует WebGL, поэтому значительная часть времени уходит в GPU-пайплайн. Это создаёт ограничения:
performance.now() фиксирует только CPU-стадиюДля более точной диагностики можно использовать расширения:
EXT_disjoint_timer_query_webgl2 (если доступно)WEBGL_debug_renderer_info (для диагностики
устройства)Однако поддержка ограничена, поэтому чаще применяют косвенные метрики.
Для аналитики полезно разбивать рендеринг на этапы:
Инициализация карты
Загрузка ресурсов
Построение сцены
GPU рендер
Каждый этап можно частично отследить через события:
map.on('data', (e) => {
console.log('Data event:', e.type);
});
Для реального профилирования обычно комбинируются несколько методов:
const metrics = {
start: performance.now(),
load: null,
firstRender: null,
idle: null
};
map.on('load', () => {
metrics.load = performance.now();
});
map.on('render', () => {
if (!metrics.firstRender) {
metrics.firstRender = performance.now();
}
});
map.on('idle', () => {
if (!metrics.idle) {
metrics.idle = performance.now();
console.log({
load: metrics.load - metrics.start,
firstRender: metrics.firstRender - metrics.start,
idle: metrics.idle - metrics.start
});
}
});
Такая структура позволяет сравнивать:
При анализе в Chrome DevTools полезно совмещать ручные метки с профилированием:
Mapbox GL JS хорошо отображается в профилировщике как серия WebGL draw calls и script execution blocks, что позволяет сопоставить измерения из кода с визуальным таймлайном браузера.
При использовании flyTo, easeTo и других
анимационных методов метрики становятся непрерывными:
map.flyTo({
center: [10, 20],
zoom: 5
});
В таких сценариях:
render вызывается на каждом кадре анимацииidle наступает только после завершения переходаДля оценки стабильности используют скользящее окно FPS и время между кадрами, а не одиночные измерения.
При работе с Mapbox GL JS важно учитывать системные ограничения:
performance.now()Поэтому корректные замеры всегда требуют: