Pitch и rotate события

Геометрия отображения карты: pitch и bearing

В системе WebGL-картографии MapLibre GL JS трёхмерное представление карты строится вокруг двух ключевых параметров камеры:

Pitch (наклон) — угол наклона камеры относительно поверхности земли.

  • Значение соответствует строго вертикальному виду сверху
  • Увеличение pitch создаёт перспективу с «горизонтом»
  • Типичные значения: от до 60°

Bearing (поворот) — азимутальный угол вращения карты вокруг вертикальной оси.

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

Эти параметры являются частью состояния камеры и напрямую влияют на матрицу трансформации WebGL-сцены.


Модель событий pitch и rotate

Система событий в MapLibre GL JS построена на реактивной модели: любое изменение состояния камеры может сопровождаться событиями жизненного цикла.

Для pitch и rotate предусмотрены следующие события:

  • pitchstart
  • pitch
  • pitchend
  • rotatestart
  • rotate
  • rotateend

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


Фазы событий pitch

pitchstart

Срабатывает в момент начала изменения наклона карты.

Типичные источники:

  • перетаскивание правой кнопкой мыши (или с Alt/Shift модификатором)
  • жесты на тач-устройствах
  • программное изменение setPitch

Событие сигнализирует о входе в режим трансформации камеры.


pitch

Срабатывает многократно в процессе изменения наклона.

Особенности:

  • генерируется на каждом кадре анимации
  • отражает промежуточное состояние pitch
  • может вызываться десятки раз в секунду

Используется для:

  • синхронизации UI-индикаторов
  • динамических визуальных эффектов
  • вычислений, завязанных на текущий угол наклона

pitchend

Фиксирует завершение изменения pitch.

Момент окончания определяется:

  • завершением drag-интеракции
  • окончанием анимации камеры
  • завершением программного transition

Фазы событий rotate

rotatestart

Срабатывает при начале вращения карты.

Источники:

  • жест поворота на тачпадах
  • удержание правой кнопки мыши и движение
  • вызов setBearing

Событие фиксирует переход камеры в режим вращения.


rotate

Промежуточное событие вращения.

Характеристики:

  • вызывается непрерывно во время изменения bearing
  • отражает текущее значение угла поворота
  • тесно связано с render-циклом WebGL

rotateend

Срабатывает при завершении вращения.

Фиксирует:

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

Подписка на события камеры

События pitch и rotate подписываются через единый интерфейс map.on.

Базовый синтаксис

map.on('pitchstart', (e) => {
    console.log('Начало наклона');
});

map.on('pitch', (e) => {
    console.log('Текущий pitch:', map.getPitch());
});

map.on('pitchend', (e) => {
    console.log('Наклон завершён');
});

Аналогично для вращения:

map.on('rotatestart', () => {
    console.log('Начало вращения');
});

map.on('rotate', () => {
    console.log('Bearing:', map.getBearing());
});

map.on('rotateend', () => {
    console.log('Вращение завершено');
});

Объект события

Каждое событие камеры передаёт объект event, содержащий контекст взаимодействия.

Типичная структура:

  • type — имя события
  • target — экземпляр карты
  • originalEvent — DOM-событие (если применимо)

Пример обработки:

map.on('rotate', (e) => {
    if (e.originalEvent) {
        console.log('Пользовательское вращение');
    } else {
        console.log('Программное изменение камеры');
    }
});

Различение источника события критично при построении сложных интерфейсов, где программные и пользовательские изменения должны обрабатываться по-разному.


Программное управление pitch и rotate

MapLibre GL JS предоставляет методы управления камерой:

setPitch

map.setPitch(45);

Изменяет наклон камеры. При наличии transition вызовет полный цикл событий:

  • pitchstart
  • pitch (серия)
  • pitchend

setBearing

map.setBearing(120);

Устанавливает угол поворота карты.


Параллельные изменения

При одновременном изменении pitch и bearing события могут интерливаться:

map.easeTo({
    pitch: 60,
    bearing: 180,
    duration: 2000
});

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


Практика обработки событий камеры

Синхронизация интерфейса

Часто события используются для обновления UI-компонентов:

map.on('pitch', () => {
    const value = map.getPitch();
    ui.pitchLabel.textContent = value.toFixed(1);
});

Ограничение вычислений

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

Подход:

  • минимизация логики внутри pitch/rotate
  • использование throttle/debounce
  • перенос вычислений в pitchend / rotateend

Детектирование пользовательского взаимодействия

let userInteracting = false;

map.on('rotatestart', () => {
    userInteracting = true;
});

map.on('rotateend', () => {
    userInteracting = false;
});

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


Различия между pitch и rotate в событийной модели

Характеристика pitch rotate
Ось изменения X/Z перспектива вертикальная ось
Влияние наклон камеры поворот карты
Тип восприятия 3D глубина ориентация
Частота событий высокая высокая

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


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

При одновременной работе pitch и rotate возникают сложные состояния камеры, характерные для 3D-навигации:

  • наклон + поворот создают свободную камеру
  • события могут пересекаться по времени
  • порядок вызовов не гарантируется

Пример наблюдения состояния:

function logCameraState() {
    console.log({
        pitch: map.getPitch(),
        bearing: map.getBearing()
    });
}

map.on('pitch', logCameraState);
map.on('rotate', logCameraState);

Особенности жизненного цикла событий

События pitch и rotate интегрированы в render-loop MapLibre:

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

Это означает, что фактическое количество событий зависит от FPS и сложности сцены.


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

  • выполнение тяжёлой логики внутри rotate или pitch
  • отсутствие фильтрации originalEvent, что приводит к путанице между программными и пользовательскими изменениями
  • использование событий вместо moveend для финальной фиксации состояния камеры
  • игнорирование высокой частоты вызовов событий