В Mapbox GL JS анимация карты строится вокруг непрерывного изменения
состояния map через функции обновления кадра. Основной
механизм основан на цикле рендеринга, который синхронизирован с
requestAnimationFrame. Это позволяет изменять параметры
камеры, источников данных и слоёв с высокой частотой без ручного
управления перерисовкой.
Ключевым элементом является метод:
map.triggerRepaint()
Он принудительно инициирует перерисовку кадра, если анимация управляется внешним таймером или асинхронной логикой.
Автовоспроизведение в контексте Mapbox GL JS означает непрерывное выполнение серии изменений состояния карты: перемещение камеры, обновление данных или последовательное переключение визуальных состояний.
easeToНаиболее распространённый способ реализации автовоспроизведения — циклическое изменение параметров камеры:
map.easeTo({
center: [37.618423, 55.751244],
zoom: 10,
duration: 4000,
essential: true
});
Метод easeTo использует плавную интерполяцию между
текущим и целевым состоянием камеры. При последовательном вызове
формируется эффект непрерывного движения.
flyTo для сценариев перелётаМетод flyTo создаёт более сложную траекторию движения
камеры, включая имитацию полёта с ускорением и замедлением:
map.flyTo({
center: [30.31413, 59.93863],
zoom: 12,
speed: 0.8,
curve: 1.42
});
Автовоспроизведение на базе flyTo часто применяется для
последовательного обхода точек интереса.
Для циклического воспроизведения используется массив координат и индекс состояния:
const points = [
[37.62, 55.75],
[30.31, 59.93],
[82.92, 55.03]
];
let index = 0;
function playSequence() {
map.flyTo({
center: points[index],
zoom: 10,
speed: 0.9
});
index = (index + 1) % points.length;
setTimeout(playSequence, 5000);
}
playSequence();
Такой подход создаёт базовую систему автовоспроизведения без внешних библиотек.
Mapbox GL JS поддерживает обновление GeoJSON-источников в реальном времени. Это позволяет реализовать воспроизведение событий на карте.
map.getSource('points').setData(updatedGeojson);
При последовательном обновлении данных можно воспроизводить временные изменения.
Один из распространённых методов — фильтрация по временной метке:
map.setFilter('events-layer', [
'<=',
['get', 'timestamp'],
currentTime
]);
Изменяя currentTime в цикле, создаётся эффект
проигрывания событий.
let t = 0;
function animateTime() {
t += 1000;
map.setFilter('events-layer', [
'<=',
['get', 'time'],
t
]);
map.triggerRepaint();
requestAnimationFrame(animateTime);
}
animateTime();
Слои можно использовать как кадры анимации, переключая их видимость:
map.setLayoutProperty('layer-1', 'visibility', 'visible');
map.setLayoutProperty('layer-2', 'visibility', 'none');
При последовательной смене создаётся эффект слайд-шоу.
const layers = ['step-1', 'step-2', 'step-3'];
let frame = 0;
function playLayers() {
layers.forEach((layer, i) => {
map.setLayoutProperty(layer, 'visibility', i === frame ? 'visible' : 'none');
});
frame = (frame + 1) % layers.length;
setTimeout(playLayers, 2000);
}
playLayers();
Для более точного контроля используется
requestAnimationFrame, который синхронизирует обновления с
частотой обновления экрана:
function loop() {
map.rotateTo((map.getBearing() + 0.5) % 360, { duration: 0 });
requestAnimationFrame(loop);
}
loop();
Этот подход позволяет создавать непрерывные анимации без задержек таймера.
При изменении источников вне встроенных анимационных методов требуется принудительная перерисовка:
function update() {
// изменение данных или состояния
map.triggerRepaint();
requestAnimationFrame(update);
}
update();
Маршруты реализуются через GeoJSON LineString и интерполяцию координат.
const route = {
type: 'Feature',
geometry: {
type: 'LineString',
coordinates: [
[37.62, 55.75],
[38.00, 56.00]
]
}
};
Для движения используется индексирование точек или интерполяция.
function interpolate(a, b, t) {
return [
a[0] + (b[0] - a[0]) * t,
a[1] + (b[1] - a[1]) * t
];
}
let progress = 0;
function animateMarker() {
const start = route.geometry.coordinates[0];
const end = route.geometry.coordinates[1];
const pos = interpolate(start, end, progress);
map.getSource('point').setData({
type: 'Feature',
geometry: {
type: 'Point',
coordinates: pos
}
});
progress += 0.01;
if (progress > 1) progress = 0;
requestAnimationFrame(animateMarker);
}
animateMarker();
В сложных сценариях автовоспроизведение включает несколько параллельных процессов:
function tick() {
map.easeTo({
bearing: map.getBearing() + 0.2,
duration: 0
});
updateDataFrame();
updateLayersFrame();
requestAnimationFrame(tick);
}
tick();
Для предотвращения накопления вызовов используется флаг состояния:
let playing = true;
function loop() {
if (!playing) return;
map.rotateTo(map.getBearing() + 0.3, { duration: 0 });
requestAnimationFrame(loop);
}
loop();
Чрезмерное обновление источников приводит к нагрузке на GPU. Используется троттлинг:
let last = 0;
function update(ts) {
if (ts - last > 50) {
map.triggerRepaint();
last = ts;
}
requestAnimationFrame(update);
}
update();
Слои с выражениями (expressions) следует проектировать
так, чтобы они зависели от одного параметра, а не множества динамических
значений, иначе каждая итерация вызывает перерасчёт всего стека
рендеринга.
map.on('load', () => {
startPlayback();
});
map.on('moveend', () => {
nextStep();
});
Такой подход позволяет строить цепочки воспроизведения без таймеров.
Сцена представляет собой фиксированное состояние карты: центр, масштаб, наклон, набор видимых слоёв.
const scenes = [
{ center: [37, 55], zoom: 9 },
{ center: [30, 59], zoom: 10 }
];
let s = 0;
function playScenes() {
const scene = scenes[s];
map.easeTo(scene);
s = (s + 1) % scenes.length;
setTimeout(playScenes, 6000);
}
playScenes();
Для flyTo и easeTo ключевым параметром
является speed или duration, влияющий на
восприятие темпа воспроизведения.
map.flyTo({
center: [10, 50],
speed: 1.2
});
let speed = 0.5;
function animate() {
speed += 0.01;
map.rotateTo(map.getBearing() + speed, { duration: 0 });
requestAnimationFrame(animate);
}
animate();
clearTimeout(timerId);
cancelAnimationFrame(frameId);
При переключении режимов воспроизведения необходимо останавливать
предыдущие циклы, иначе одновременно активные
requestAnimationFrame создают дублирующие обновления
состояния карты.
При взаимодействии пользователя с картой автоматическое воспроизведение должно временно приостанавливаться:
map.on('mousedown', () => playing = false);
map.on('mouseup', () => playing = true);
function resume() {
if (!playing) return;
requestAnimationFrame(loop);
}