Изменение свойств во времени

Работа с динамическими изменениями состояния карты в Mapbox GL JS строится вокруг нескольких механизмов: переходов (transitions), анимаций камеры, выражений (expressions), обновления стилей и источников данных, а также ручного управления через цикл анимации. Совокупность этих инструментов позволяет создавать плавные визуальные эффекты, синхронизированные с временем, пользовательским вводом или внешними данными.


Переходы свойств (Transition API)

Каждое визуальное свойство слоя в Mapbox GL JS поддерживает встроенную анимацию перехода. Это относится к paint-свойствам, таким как цвет, прозрачность, ширина линий, радиус кругов и другие параметры визуализации.

Ключевой механизм — объект transition, задающий длительность и задержку изменения.

map.setPaintProperty('cities-layer', 'circle-radius', 8);

map.setPaintProperty('cities-layer', 'circle-radius-transition', {
    duration: 1200,
    delay: 0
});

map.setPaintProperty('cities-layer', 'circle-radius', 20);

В этом примере изменение радиуса круга происходит плавно, а не мгновенно.

Основные параметры transition

  • duration — длительность перехода в миллисекундах
  • delay — задержка перед началом анимации

Важно, что transition применяется только к изменяющимся значениям. Повторное присвоение того же значения не вызывает анимацию.


Плавное изменение стилей через setPaintProperty

Метод setPaintProperty используется для динамического обновления визуальных параметров слоя во времени. Он часто применяется в сочетании с таймерами или анимационными циклами.

let opacity = 1;

setInterval(() => {
    opacity -= 0.05;

    if (opacity < 0) opacity = 1;

    map.setPaintProperty('water-layer', 'fill-opacity', opacity);
}, 100);

Подобный подход позволяет создавать эффекты пульсации, мигания или затухания.


Использование requestAnimationFrame для синхронизированных анимаций

Для более точного управления временем применяется requestAnimationFrame, который синхронизируется с частотой обновления экрана.

let start = performance.now();

function animate(timestamp) {
    const progress = (timestamp - start) / 2000; // 2 секунды цикл

    const value = Math.sin(progress * Math.PI * 2) * 0.5 + 0.5;

    map.setPaintProperty('pulse-layer', 'circle-opacity', value);

    requestAnimationFrame(animate);
}

requestAnimationFrame(animate);

Этот подход позволяет создавать непрерывные анимации без рывков и лишней нагрузки.


Камерные анимации во времени

Изменение свойств карты включает не только стили, но и параметры камеры: центр, масштаб, наклон и поворот. Для этого используются методы easeTo, flyTo, jumpTo.

map.easeTo({
    center: [30.3, 59.9],
    zoom: 10,
    duration: 3000
});

Особенности временных переходов камеры

  • easeTo — плавное движение с контролем скорости
  • flyTo — имитация полёта с дуговой траекторией
  • jumpTo — мгновенное изменение без анимации

Комбинация камерных анимаций и изменений слоёв позволяет синхронизировать визуальные эффекты во времени.


Динамические выражения и интерполяция

Expressions позволяют изменять свойства в зависимости от данных или масштаба, включая временные зависимости.

Интерполяция цвета

map.setPaintProperty('temperature-layer', 'circle-color', [
    'interpolate',
    ['linear'],
    ['get', 'temp'],
    0, '#2c7bb6',
    10, '#abd9e9',
    20, '#ffffbf',
    30, '#fdae61',
    40, '#d7191c'
]);

Хотя здесь нет явного времени, такие выражения часто комбинируются с обновлением данных во времени, создавая динамическую карту.


Обновление источников данных во времени

Изменение свойств часто связано с обновлением GeoJSON-источников.

setInterval(() => {
    const source = map.getSource('points');

    const updatedData = generateNewGeoJSON();

    source.setData(updatedData);
}, 5000);

Каждое обновление источника автоматически приводит к перерасчёту слоёв, что создаёт эффект движения объектов во времени.


Анимация объектов через координаты

Изменение геометрии — один из наиболее выразительных способов временной анимации.

let t = 0;

function animate() {
    t += 0.01;

    const lng = 30 + Math.cos(t) * 0.5;
    const lat = 50 + Math.sin(t) * 0.5;

    const source = map.getSource('moving-point');

    source.setData({
        type: 'Feature',
        geometry: {
            type: 'Point',
            coordinates: [lng, lat]
        }
    });

    requestAnimationFrame(animate);
}

animate();

Такой подход используется для отображения движущихся объектов: транспорта, потоков, сигналов.


Временные эффекты через heatmap

Тепловые карты часто используются для визуализации изменений плотности во времени.

map.setPaintProperty('heatmap-layer', 'heatmap-intensity', [
    'interpolate',
    ['linear'],
    ['zoom'],
    0, 1,
    9, 3
]);

При обновлении данных по временным срезам создаётся эффект «пульсации» плотности.


Синхронизация с внешним временем

При интеграции с таймлайнами или временными шкалами значение времени становится управляющим параметром всей сцены.

function updateByTime(hour) {
    map.setPaintProperty('night-layer', 'fill-opacity',
        hour >= 20 || hour <= 6 ? 0.8 : 0
    );
}

Подобные конструкции позволяют моделировать суточные циклы, сезонные изменения и другие временные процессы.


Переходы между стилями

Изменение полного стиля карты также может быть анимировано, хотя это более тяжёлая операция.

map.setStyle('mapbox://styles/mapbox/dark-v11');

map.once('styledata', () => {
    map.setPaintProperty('water', 'fill-color', '#001f3f');
});

Переключение стиля фактически пересоздаёт слои, поэтому временная логика часто переносится в события загрузки стиля.


Композиция временных эффектов

На практике изменение свойств во времени редко ограничивается одним методом. Обычно используется комбинация:

  • requestAnimationFrame для точного контроля
  • setData для обновления геометрии
  • setPaintProperty для визуальных параметров
  • easeTo для камеры
  • expressions для адаптивного поведения

Так формируется единая временная сцена, где каждый слой реагирует на общий временной параметр.


Производительность при частых обновлениях

Частые изменения свойств требуют учёта нагрузки на рендеринг:

  • минимизация вызовов setPaintProperty
  • предпочтение expressions вместо постоянных обновлений
  • группировка изменений в одном кадре
  • ограничение частоты setData

Особенно критичны обновления источников данных, так как они инициируют перерасчёт геометрии и перерисовку слоёв.


Использование временного параметра как глобального состояния

Распространённый подход — введение единой переменной времени, управляющей всей сценой:

let time = 0;

function tick() {
    time += 0.016;

    map.setPaintProperty('layer-1', 'opacity', Math.sin(time));

    map.setPaintProperty('layer-2', 'circle-radius', 5 + Math.cos(time) * 3);

    requestAnimationFrame(tick);
}

tick();

Такое решение позволяет синхронизировать визуальные эффекты, создавая единый временной контекст карты.