В Mapbox GL JS анимация почти всегда опирается на два фундаментальных механизма: изменение состояния камеры и обновление данных слоёв. Камера определяет, что именно пользователь видит на карте, а слои определяют, как эти данные визуализируются. В результате анимация может быть либо «географической» (перемещение, масштабирование, вращение), либо «данной» (изменение геометрии, свойств и визуальных параметров объектов).
Камера реализуется через три основные операции:
jumpTo — мгновенное изменение состояния без
интерполяцииeaseTo — плавное изменение с заданной функцией
сглаживанияflyTo — физически-ориентированное перемещение с
эффектом полётаmap.easeTo({
center: [55.751244, 37.618423],
zoom: 10,
duration: 2000,
essential: true
});
Параметр duration задаёт длительность перехода, а
встроенная кривая плавности формирует естественное ускорение и
замедление движения. Внутри Mapbox GL JS используется непрерывная
интерполяция состояния камеры, где каждое значение (центр, масштаб, угол
наклона) вычисляется на каждом кадре.
Анимации в Mapbox GL JS строятся на интерполяции — вычислении промежуточных значений между начальным и конечным состоянием. Этот подход позволяет избегать дискретных скачков при обновлении карты.
Пример интерполяции угла поворота:
map.rotateTo(180, {
duration: 3000,
easing: (t) => t * (2 - t)
});
Функция easing определяет характер движения. Линейная
интерполяция создаёт механическое движение, тогда как нелинейные функции
формируют естественные траектории.
Наиболее гибкий способ создания динамики — регулярное обновление источников данных. В Mapbox GL JS GeoJSON-источник может изменяться в реальном времени, что приводит к перерисовке слоя.
const source = map.getSource('points');
setInterval(() => {
const updated = generateRandomPoints();
source.setData({
type: 'FeatureCollection',
features: updated
});
}, 1000);
Такой подход используется для:
Однако частое обновление GeoJSON требует контроля производительности, поскольку каждое обновление инициирует перерасчёт рендеринга.
Для более тонкого управления временем применяется цикл
requestAnimationFrame. Он синхронизирован с частотой
обновления экрана и обеспечивает стабильную анимацию без лишней
нагрузки.
let start;
function animate(timestamp) {
if (!start) start = timestamp;
const progress = (timestamp - start) / 2000;
const lng = 30 + progress * 10;
const lat = 50 + progress * 5;
map.setCenter([lng, lat]);
if (progress < 1) {
requestAnimationFrame(animate);
}
}
requestAnimationFrame(animate);
Такой подход позволяет создавать полностью кастомные анимации камеры и данных, выходящие за рамки стандартных методов.
Слои в Mapbox GL JS поддерживают изменение визуальных свойств через transition-параметры. Это касается цвета, прозрачности, толщины линий и других визуальных характеристик.
map.setPaintProperty('route', 'line-opacity-transition', {
duration: 1500,
delay: 0
});
map.setPaintProperty('route', 'line-opacity', 1);
Transition позволяет задать плавное изменение между состояниями без ручной интерполяции.
Стильовая система основана на выражениях, которые поддерживают встроенную интерполяцию значений.
'circle-radius': [
'interpolate',
['linear'],
['zoom'],
5, 2,
10, 10,
15, 20
]
Такой подход делает масштабно-зависимую анимацию частью декларативного стиля. В отличие от ручных обновлений, вычисление происходит внутри рендерера.
Одним из распространённых визуальных эффектов является движение
пунктирной линии. Оно достигается изменением line-dasharray
во времени.
let step = 0;
function animateDash() {
step += 0.5;
map.setPaintProperty('route', 'line-dasharray', [
0, step, 1, step + 1
]);
requestAnimationFrame(animateDash);
}
animateDash();
Этот метод создаёт иллюзию движения вдоль линии без изменения геометрии.
Символьные слои позволяют изменять положение и свойства объектов в реальном времени. Например, перемещение маркера вдоль маршрута.
let i = 0;
function moveMarker() {
const coord = route.coordinates[i];
map.getSource('point').setData({
type: 'Feature',
geometry: {
type: 'Point',
coordinates: coord
}
});
i = (i + 1) % route.coordinates.length;
requestAnimationFrame(moveMarker);
}
Подобные методы часто используются для визуализации транспорта и трекинга объектов.
При работе с временными рядами важно синхронизировать данные и визуализацию. Часто используется концепция временного окна, в рамках которого отображаются только актуальные данные.
function filterByTime(features, time) {
return features.filter(f => f.properties.timestamp <= time);
}
Такой подход позволяет строить анимации изменения состояния системы во времени.
Тепловые карты поддерживают изменение интенсивности в зависимости от времени. При обновлении данных перерасчитывается плотность распределения.
map.setPaintProperty('heat', 'heatmap-intensity', [
'interpolate',
['linear'],
['zoom'],
0, 1,
9, 3
]);
При комбинировании с обновляемыми источниками создаётся эффект «пульсации» плотности событий.
В Mapbox GL JS все анимации завязаны на WebGL рендеринг, что требует аккуратного управления нагрузкой. Основные ограничения связаны с:
setDataОптимизация часто сводится к снижению частоты обновлений и переносу логики интерполяции в GPU через expressions.
На практике анимации редко ограничиваются одним типом. Камера, данные и стили часто изменяются одновременно, формируя сложные визуальные сцены.
Пример комбинированного подхода:
function tick(t) {
const progress = (t % 5000) / 5000;
map.easeTo({
bearing: progress * 360,
duration: 0
});
updateData(progress);
requestAnimationFrame(tick);
}
requestAnimationFrame(tick);
Такой подход объединяет непрерывное вращение камеры и синхронное обновление данных, создавая цельную динамическую модель отображения.