Анимация rotation

Поворот (rotation) в OpenLayers относится к базовым трансформациям вида карты и объектов, позволяя изменять ориентацию отображаемой сцены вокруг заданной точки. Внутренне библиотека оперирует углами в радианах, а анимация вращения реализуется через механизм изменения состояния View во времени.

Ключевым объектом для управления вращением является ol/View, где параметр rotation определяет текущий угол поворота карты.


Базовый принцип вращения

В OpenLayers вращение карты задаётся значением угла в радианах:

  • 0 — север направлен вверх
  • положительные значения — вращение по часовой стрелке
  • отрицательные значения — против часовой стрелки

Основной метод установки:

view.setRotation(angle);

Также значение можно получить:

const current = view.getRotation();

Однако статическая установка не даёт эффекта анимации — она просто мгновенно меняет ориентацию. Для плавного вращения используется встроенный механизм анимаций View.animate.


Анимация вращения через View.animate

Метод animate у ol/View позволяет интерполировать значение rotation во времени.

view.animate({
  rotation: Math.PI,
  duration: 1000
});

В данном случае карта совершит поворот на 180 градусов за 1 секунду.

Параметры анимации

Основные параметры объекта анимации:

  • rotation — целевой угол в радианах
  • duration — длительность анимации в миллисекундах
  • easing — функция сглаживания

Пример с функцией плавности:

import { easeIn, easeOut } from 'ol/easing';

view.animate({
  rotation: Math.PI * 2,
  duration: 1500,
  easing: easeIn
});

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

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

Если обозначить:

  • r0 — начальный угол
  • r1 — конечный угол
  • t — прогресс от 0 до 1

то текущее значение вычисляется как:

r(t) = r0 + (r1 - r0) * easing(t)

Такой подход обеспечивает плавность вращения и позволяет комбинировать rotation с другими параметрами вида.


Комбинирование rotation с центром и zoom

Часто вращение используется не изолированно, а совместно с изменением центра и масштаба:

view.animate({
  center: [0, 0],
  zoom: 4,
  rotation: Math.PI / 2,
  duration: 2000
});

В этом случае происходит комплексная трансформация сцены: карта одновременно поворачивается, масштабируется и перемещается.


Поворот вокруг конкретной точки

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

Используется параметр anchor в более сложных сценариях через последовательные трансформации:

const center = view.getCenter();

view.animate({
  rotation: Math.PI / 4,
  anchor: center,
  duration: 1200
});

Хотя anchor не всегда явно поддерживается в высокоуровневом API, концептуально вращение всегда вычисляется относительно центра проекции.


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

В случаях, когда требуется полный контроль над вращением, используется ручная интерполяция:

const start = view.getRotation();
const end = start + Math.PI;

const startTime = performance.now();

function rotateFrame(time) {
  const t = (time - startTime) / 1000;
  const progress = Math.min(t, 1);

  const angle = start + (end - start) * progress;

  view.setRotation(angle);

  if (progress < 1) {
    requestAnimationFrame(rotateFrame);
  }
}

requestAnimationFrame(rotateFrame);

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


Обработка переполнения угла

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

function normalizeAngle(angle) {
  return ((angle + Math.PI) % (2 * Math.PI)) - Math.PI;
}

Это помогает избежать резких скачков при переходе через границу полного оборота.


Интеграция с интерактивными событиями

Вращение часто привязывается к пользовательскому вводу:

Вращение колесом мыши

map.getView().on('change:rotation', () => {
  const rotation = view.getRotation();
  console.log(rotation);
});

Управление через клавиши

window.addEventListener('keydown', (e) => {
  const step = Math.PI / 18;

  if (e.key === 'ArrowLeft') {
    view.setRotation(view.getRotation() - step);
  }

  if (e.key === 'ArrowRight') {
    view.setRotation(view.getRotation() + step);
  }
});

Использование easing-функций

Функции сглаживания играют ключевую роль в восприятии вращения. OpenLayers предоставляет набор стандартных easing-функций:

  • easeIn
  • easeOut
  • easeInOut
  • linear

Пример:

import { easeInOut } from 'ol/easing';

view.animate({
  rotation: Math.PI,
  duration: 2000,
  easing: easeInOut
});

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


Связь rotation с разрешением проекции

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

OpenLayers применяет rotation как часть трансформации:

screen = translate(center) × rotate(angle) × scale(resolution)

Это означает, что:

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

Производительность анимации

Анимация rotation относится к GPU-ускоряемым операциям в современных браузерах. Однако при высокой нагрузке могут возникать ограничения:

  • большое количество векторных объектов
  • сложные стили с тенями и фильтрами
  • параллельные анимации нескольких view

Оптимизация достигается за счёт:

  • уменьшения количества перерисовок
  • ограничения FPS через easing
  • использования WebGL-рендерера для векторных слоёв

Синхронизация нескольких карт

При необходимости синхронного вращения нескольких экземпляров карты используется общий обработчик:

view1.on('change:rotation', () => {
  view2.setRotation(view1.getRotation());
});

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


Типичные ошибки при работе с rotation

  • использование градусов вместо радиан
  • отсутствие нормализации угла при циклических вращениях
  • попытка анимации через setRotation без интерполяции
  • одновременное управление rotation и center без учёта порядка трансформаций

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


Особенности поведения при инерции

При включённой инерции взаимодействий (drag/rotate interaction) rotation может продолжаться после отпускания мыши. Это реализуется через momentum-интерполяцию, где угол продолжает изменяться по убывающей скорости.

Такая логика накладывается поверх View.animate, создавая комбинированный эффект плавного затухания вращения.