Цикл отрисовки в MapLibre GL JS построен вокруг механизма кадровой
синхронизации браузера и функции requestAnimationFrame,
которая задаёт базовый ритм обновления сцены карты. Этот подход
обеспечивает согласованность визуального состояния с частотой обновления
экрана и позволяет минимизировать лишние вычисления при отсутствии
изменений.
requestAnimationFrame (rAF) вызывает переданную функцию
непосредственно перед следующим перерисовыванием страницы. В контексте
WebGL-рендеринга это означает, что MapLibre получает возможность
обновить матрицы, пересчитать тайлы и отрисовать слой строго в момент,
когда браузер готов вывести новый кадр.
Внутренний цикл рендеринга карты опирается на идею «кадра как единицы состояния»:
Любое изменение этих параметров маркирует карту как требующую перерисовки.
В MapLibre GL JS отсутствует классический бесконечный цикл, активно работающий в каждый момент времени. Вместо этого используется условная схема:
requestAnimationFrame.Это позволяет экономить ресурсы CPU/GPU.
Упрощённая модель выглядит следующим образом:
function frame() {
if (map._needsRender) {
map._render();
}
if (map._needsRender) {
requestAnimationFrame(frame);
}
}
Флаг _needsRender (внутренний механизм) выставляется
при:
map.setCenter, map.setZoom,
map.setBearing, map.setPitch;setStyle;Одним из ключевых этапов каждого кадра является пересчёт матриц:
Эти матрицы формируют цепочку преобразования:
географические координаты → мировые координаты → экранные координаты.
requestAnimationFrame обеспечивает, что эти вычисления
происходят синхронно с визуальным обновлением экрана, исключая
рассинхронизацию между логикой и отрисовкой.
MapLibre использует WebGL как низкоуровневый слой рендеринга. В каждом кадре:
Ключевой момент — WebGL вызовы выполняются строго внутри rAF-цикла, что гарантирует минимальные задержки между расчётом и отображением.
Система оптимизации рендеринга строится на событии «dirty state». Карта помечается как требующая обновления только при фактических изменениях.
Типовые триггеры:
source.setData);setFilter);transition).Если ни один триггер не активен, rAF-цепочка останавливается, и браузер возвращается к idle-состоянию.
Это принципиально отличает MapLibre от постоянных игровых циклов, где кадры генерируются непрерывно.
Анимации камеры и свойств слоёв реализуются через интерполяцию между
начальным и конечным состоянием, где каждый кадр rAF предоставляет
временную метку timestamp.
На основе этой метки вычисляется прогресс:
Пример логики:
function animate(timestamp) {
const t = (timestamp - startTime) / duration;
const eased = easeInOutCubic(t);
map.setCenter(interpolate(start, end, eased));
if (t < 1) {
requestAnimationFrame(animate);
}
}
Custom layer API также привязан к rAF-циклу. Каждый пользовательский слой получает вызовы:
onAdd(map, gl) — инициализация;render(gl, matrix) — отрисовка в каждом кадре;onRemove() — очистка ресурсов.Метод render вызывается строго в момент кадра, синхронно
с основным pipeline MapLibre. Это позволяет интегрировать собственные
WebGL-шейдеры без нарушения общей синхронизации.
События render и idle тесно связаны с
rAF:
render — вызывается после каждого кадра, где произошла
отрисовка;idle — вызывается, когда rAF-цепочка завершилась и
карта не требует обновлений.Таким образом, requestAnimationFrame становится
центральным механизмом перехода между активным и пассивным состоянием
карты.
В случаях, когда внешняя система изменяет состояние, но не вызывает стандартные методы API, используется принудительный запуск цикла:
Псевдомеханизм:
map.triggerRepaint = function () {
this._needsRender = true;
if (!this._frameRequested) {
this._frameRequested = true;
requestAnimationFrame(this._renderFrame);
}
};
Несмотря на привязку к rAF, реальная частота отрисовки зависит от:
MapLibre автоматически пропускает лишние шаги, если вычислительная нагрузка превышает допустимый порог, сохраняя стабильность кадра.
Загрузка тайлов и ресурсов происходит вне rAF-цикла, но их интеграция в сцену синхронизируется при следующем кадре. Это предотвращает «разрывы» состояния, когда новые данные могли бы появиться между рендерами.
Каждый загруженный тайл:
Главная задача rAF-архитектуры — обеспечение атомарности кадра:
Это критично для картографических систем, где несогласованность приводит к визуальным артефактам при панорамировании и зуме.