Анимация камеры

Камера в MapLibre GL JS представляет собой абстракцию состояния отображения карты, включающую центр, масштаб, угол поворота и наклон. Управление камерой сводится к изменению этих параметров с возможностью анимации, интерполяции и прерывания переходов. Все методы камеры опираются на единый принцип: переход от текущего состояния к целевому с заданными параметрами времени и кривой интерполяции.

Состояние камеры определяется набором свойств:

  • center — географические координаты центра карты
  • zoom — уровень масштабирования
  • bearing — угол поворота карты относительно севера
  • pitch — угол наклона камеры

Любое изменение этих параметров может быть выполнено мгновенно либо с анимацией. Анимационные переходы реализуются через встроенный tweening-механизм.

Ключевые параметры анимации:

  • duration — длительность перехода в миллисекундах
  • easing — функция плавности изменения (easing function)
  • essential — флаг, указывающий на критичность анимации
  • animate — включение/отключение анимации
  • offset — смещение центра в пикселях

Мгновенное изменение состояния камеры

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

map.jumpTo({
  center: [30.31413, 59.93863],
  zoom: 10,
  bearing: 0,
  pitch: 0
});

Изменения применяются сразу, без промежуточных кадров, что делает метод полезным для синхронизации состояния карты с внешними событиями.

Плавная анимация с easeTo

Метод easeTo реализует плавное изменение камеры с контролируемой кривой ускорения и замедления.

map.easeTo({
  center: [30.31413, 59.93863],
  zoom: 12,
  duration: 2000,
  easing: (t) => t * (2 - t)
});

Параметр easing принимает функцию, нормализованную на диапазон от 0 до 1, где значение определяет прогресс анимации. Использование нелинейных функций позволяет создавать эффект ускорения, замедления или комбинированного движения.

Типовая сигнатура easing-функции:

function easing(t) {
  return t;
}

Для более сложных траекторий применяются квадратичные, кубические и синусоидальные функции.

Полёт камеры flyTo

Метод flyTo реализует анимацию с эффектом «полёта» к целевой точке. В отличие от easeTo, движение включает дугообразную траекторию и динамическое изменение масштаба.

map.flyTo({
  center: [37.6173, 55.7558],
  zoom: 13,
  speed: 1.2,
  curve: 1.5,
  essential: true
});

Параметры:

  • speed — относительная скорость анимации
  • curve — степень кривизны траектории
  • essential — обозначение важности анимации для UX

Алгоритм строит интерполяцию не по линейной прямой, а по геодезической дуге, что создаёт визуальный эффект «перелёта» над картой.

Остановка анимации

Любая активная анимация камеры может быть прервана методом stop.

map.stop();

После вызова текущее состояние фиксируется, а дальнейшие переходы требуют нового вызова методов управления камерой. Это важно при обработке пользовательского взаимодействия, чтобы исключить конфликт автоматической и ручной навигации.

Изменение угла и наклона

Отдельные параметры камеры могут изменяться независимо.

map.rotateTo(90, { duration: 1000 });

map.pitchTo(45, {
  duration: 1500,
  easing: (t) => Math.pow(t, 2)
});

rotateTo управляет вращением вокруг центра, а pitchTo изменяет угол обзора. Оба метода поддерживают анимацию и easing-функции.

Ограничение области видимости через fitBounds

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

map.fitBounds(
  [[30.0, 59.8], [30.5, 60.1]],
  {
    padding: 40,
    duration: 2000
  }
);

Алгоритм вычисляет оптимальный zoom и center, учитывая пропорции контейнера карты.

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

Все анимации камеры в MapLibre GL JS основаны на временной интерполяции. Для каждого кадра вычисляется прогресс t в диапазоне [0, 1], после чего применяются функции интерполяции:

  • линейная интерполяция координат
  • логарифмическая интерполяция масштаба
  • сферическая интерполяция для координат

Обобщённая модель:

state = start + (end - start) * easing(t);

Для zoom применяется логарифмическое преобразование, обеспечивающее равномерное восприятие масштаба.

Прерывание и конкуренция анимаций

При запуске новой анимации предыдущая может быть автоматически прервана. Поведение зависит от метода:

  • easeTo и flyTo прерывают текущую анимацию
  • jumpTo мгновенно заменяет состояние
  • stop фиксирует текущее состояние без перехода

Конкуренция анимаций становится критичной при быстром поступлении событий, например при скролле или обновлении данных в реальном времени.

Работа с событиями камеры

Изменения камеры сопровождаются событиями:

  • movestart
  • move
  • moveend
  • zoomstart
  • zoom
  • zoomend
  • rotatestart
  • rotate
  • rotateend

Пример последовательности:

map.on('movestart', () => {
  console.log('движение началось');
});

map.on('moveend', () => {
  console.log('движение завершено');
});

Эти события позволяют синхронизировать внешние компоненты с состоянием камеры.

Динамическое следование за объектом

Камера может быть связана с координатами объекта, обновляемыми в реальном времени. В таком режиме используется непрерывное обновление center.

function updatePosition(lng, lat) {
  map.easeTo({
    center: [lng, lat],
    duration: 500
  });
}

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

Орбитальная анимация

Вращение камеры вокруг точки реализуется через изменение bearing во времени.

function spinCamera(timestamp) {
  const rotation = timestamp / 100;
  map.rotateTo(rotation % 360, { duration: 0 });
  requestAnimationFrame(spinCamera);
}

requestAnimationFrame(spinCamera);

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

Комбинированные переходы

Сложные сценарии камеры часто включают одновременное изменение нескольких параметров:

map.flyTo({
  center: [37.6173, 55.7558],
  zoom: 14,
  pitch: 60,
  bearing: -30,
  duration: 3000
});

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

Управление производительностью анимации

Производительность зависит от частоты кадров и сложности сцены. Основные факторы:

  • количество слоёв
  • использование 3D-объектов
  • частота пересчёта стиля
  • интенсивность событий камеры

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

Практика композиции анимаций

Последовательные переходы реализуются через цепочку вызовов:

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

map.once('moveend', () => {
  map.easeTo({
    zoom: 14,
    duration: 1500
  });
});

Такая модель позволяет строить сценарии навигации с детерминированной последовательностью кадров без внешнего таймера.