В MapLibre GL JS временная ось анимации строится вокруг механизма
перерисовки кадра, синхронизированного с
requestAnimationFrame. Каждый визуальный переход — от
изменения камеры до интерполяции свойств слоёв — выражается через
дискретные кадры, где время выступает основным параметром
интерполяции.
Ключевая особенность архитектуры заключается в том, что библиотека не оперирует глобальной «игровой секундой» или фиксированным таймлайном. Вместо этого используется система локальных временных интервалов, привязанных к конкретным анимациям.
Внутренний цикл рендеринга MapLibre GL JS основан на следующей модели:
requestAnimationFrameКаждый цикл получает временную метку timestamp, которая
используется для интерполяции значений:
function frame(timestamp) {
map.triggerRepaint();
requestAnimationFrame(frame);
}
Хотя прямое управление этим циклом недоступно, поведение анимации зависит от параметров переходов и камерных методов.
Наиболее прямой способ управления временем анимации — параметры методов камеры.
Метод задаёт плавный переход к новой позиции карты:
map.easeTo({
center: [37.6173, 55.7558],
zoom: 10,
duration: 2000
});
Основные временные параметры:
duration — длительность анимации в миллисекундахeasing — функция сглаживанияanimate — включение/выключение анимацииФункция easing определяет распределение времени:
easing: (t) => t * (2 - t)
Здесь t — нормализованное время от 0 до 1.
Анимация полёта использует физически интерпретируемую модель движения камеры:
map.flyTo({
center: [30.3351, 59.9343],
zoom: 12,
speed: 1.2,
curve: 1.42
});
Временная динамика определяется комбинацией:
speed — относительная скорость движенияcurve — кривизна траекторииminZoom и maxDuration — ограничения
длительностиВ отличие от easeTo, здесь время не фиксировано
напрямую, а вычисляется из расстояния и параметров скорости.
Поворот и наклон камеры также зависят от временной интерполяции:
map.rotateTo(90, { duration: 1500 });
map.pitchTo(45, { duration: 1200 });
Внутренне используется линейная интерполяция углов с применением easing-функции.
MapLibre GL JS поддерживает анимацию свойств стиля через
transition:
map.setPaintProperty('water', 'fill-color', '#3bb2d0');
map.setPaintProperty('water', 'fill-color-transition', {
duration: 1000,
delay: 0
});
duration — длительность переходаdelay — задержка перед стартомeasing — функция интерполяцииКаждое изменение свойства превращается в временную функцию:
[ value(t) = value_{start} + (value_{end} - value_{start}) f(t)]
где t ∈ [0,1].
Easing-функции определяют распределение скорости во времени.
t => t
Равномерное изменение без ускорения.
t => t * t * (3 - 2 * t)
Используется для естественного замедления и ускорения.
t => Math.pow(t, 0.7)
Позволяют моделировать нелинейное восприятие времени.
При интеграции с внешними источниками данных (например, GPS или моделями движения) используется собственная временная шкала.
const start = performance.now();
function animate() {
const now = performance.now();
const t = (now - start) / 5000;
map.setFeatureState(
{ source: 'vehicle', id: 1 },
{
progress: t
}
);
if (t < 1) requestAnimationFrame(animate);
}
Здесь время нормализуется в диапазон [0,1], что
позволяет использовать его в выражениях стиля.
Для анимации линий и маршрутов часто применяется
line-progress:
map.addLayer({
id: 'route',
type: 'line',
source: 'route',
paint: {
'line-gradient': [
'interpolate',
['linear'],
['line-progress'],
0, 'blue',
1, 'red'
]
}
});
line-progress фактически становится временной
координатой вдоль геометрии.
Метод triggerRepaint() принудительно инициирует
перерисовку кадра.
map.triggerRepaint();
Он используется для:
requestAnimationFrameВременная модель при этом остаётся внешней, а MapLibre лишь реагирует на изменения состояния.
При создании полностью кастомной временной логики используется прямое управление кадрами:
function animate(timestamp) {
const progress = (timestamp % 2000) / 2000;
map.setBearing(progress * 360);
requestAnimationFrame(animate);
}
requestAnimationFrame(animate);
Здесь время задаётся как функция от системного таймстампа.
В условиях высокой нагрузки браузер может:
requestAnimationFrameMapLibre компенсирует это через использование абсолютного времени
(timestamp), а не количества кадров.
Это означает:
Явного API «pause/resume» для всех типов анимаций нет, но поведение можно моделировать через контроль времени:
let paused = false;
let pauseTime = 0;
function animate(timestamp) {
if (!paused) {
pauseTime = timestamp;
}
const t = (timestamp - pauseTime) / 3000;
map.setZoom(5 + t);
requestAnimationFrame(animate);
}
Такой подход фиксирует временную точку остановки и компенсирует её при продолжении.
При работе с flyTo и easeTo длительность
может быть ограничена внутренними механизмами:
Если пользователь взаимодействует с картой, анимация может быть прервана или переопределена.
Кастомные слои используют собственный цикл:
onAdd(map, gl) {
this.startTime = performance.now();
}
render(gl, matrix) {
const t = (performance.now() - this.startTime) / 1000;
gl.uniform1f(this.u_time, t);
this.map.triggerRepaint();
}
Здесь время становится uniform-параметром шейдера.
Большинство анимаций MapLibre сводятся к нормализации:
[ t = ]
Далее применяется ограничение:
[ t = clamp(t, 0, 1)]
и передача в easing-функцию.
При одновременном выполнении нескольких переходов применяется независимая временная шкала:
performance.nowЭто приводит к модели многоканального времени, где каждый визуальный компонент интерполируется отдельно, но синхронизируется через общий рендер-цикл.
При одинаковых входных параметрах:
анимация становится детерминированной, поскольку базируется на
монотонно возрастающем временном значении timestamp.
Это позволяет: