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

Модель рендеринга и влияние анимаций на кадры

MapLibre GL JS использует WebGL-пайплайн, в котором каждый кадр формируется на основе текущего состояния карты: положения камеры, стиля, источников данных и параметров слоёв. Любая анимация в этой модели означает непрерывное изменение состояния, что приводит к постоянному пересчёту матрицы камеры и повторному рендерингу сцены.

Ключевой особенностью является то, что движок автоматически включает непрерывный цикл отрисовки, когда обнаруживает изменения состояния. Это означает, что даже небольшие анимации удерживают активный рендер-цикл, увеличивая нагрузку на CPU и GPU.

Основные источники затрат при анимациях:

  • пересчёт матриц проекции и вида;
  • перерисовка всех видимых тайлов;
  • повторная отрисовка слоёв с динамическими стилями;
  • пересчёт подписей (symbol placement);
  • обновление буферов WebGL при изменении источников данных.

Типы анимаций и их стоимость

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

Камера — наиболее часто анимируемый объект. Методы flyTo, easeTo, jumpTo и rotateTo используют интерполяцию параметров камеры.

  • jumpTo — мгновенное изменение, не создаёт анимационного цикла;
  • easeTo — интерполяция с easing-функцией;
  • flyTo — сложная траектория с зумом и смещением перспективы.

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

Пример факторов нагрузки:

  • изменение zoom увеличивает количество перерисовываемых тайлов;
  • наклон (pitch) активирует перерасчёт перспективной проекции;
  • вращение (bearing) требует пересчёта ориентации всех векторных данных.

Анимации данных (data-driven animations)

Изменение источников GeoJSON или vector tile sources в реальном времени создаёт дополнительную нагрузку:

  • полная перерасчётка геометрии при setData;
  • обновление буферов вершин;
  • повторная триангуляция полигонов;
  • переразмещение символов.

Частое обновление данных приводит к деградации FPS даже при стабильной камере.

Оптимизационно критичным является различие между:

  • setData — полная замена источника;
  • атрибутными обновлениями через setFeatureState — частичное обновление без полной пересборки геометрии.

Анимации стилей

Изменения свойств слоёв (paint/layout) могут запускать частичный или полный repaint:

  • изменение opacity;
  • изменение цвета;
  • динамические выражения (expressions);
  • фильтры слоёв.

Наиболее затратны выражения, зависящие от zoom и feature-state, поскольку они пересчитываются на каждом кадре.


Цикл рендеринга и непрерывные кадры

MapLibre GL JS активирует continuous rendering режим, когда выполняется хотя бы одно из условий:

  • идёт анимация камеры;
  • изменяются источники данных;
  • активны transition-свойства;
  • происходит загрузка тайлов.

В этом режиме вызывается повторный рендер до тех пор, пока карта не перейдёт в состояние idle.

Состояние idle означает:

  • отсутствуют активные анимации;
  • завершена загрузка тайлов;
  • нет pending repaint операций.

Оптимизация заключается в минимизации времени пребывания в non-idle состоянии.


Интерполяция и easing-функции

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

  • линейная интерполяция координат;
  • easing-функции для сглаживания ускорения;
  • логарифмическая интерполяция zoom.

Логарифмическая природа zoom создаёт неравномерное распределение нагрузки: на высоких zoom уровнях стоимость каждого изменения выше из-за увеличения количества тайлов.

Типовые easing-функции:

  • easeInOutQuad;
  • easeInOutCubic;
  • custom bezier curves.

Чем сложнее функция, тем выше CPU overhead на расчёт каждого кадра.


Влияние WebGL и GPU-пайплайна

В MapLibre GL JS большая часть рендеринга выполняется на GPU, однако CPU остаётся узким местом при анимациях:

  • подготовка данных для шейдеров;
  • обновление uniform-переменных;
  • пересборка буферов при изменении источников.

GPU эффективно обрабатывает:

  • растеризацию тайлов;
  • отрисовку линий и полигонов;
  • символы через texture atlas.

Однако при частых изменениях состояния возникает bottleneck на этапе передачи данных CPU → GPU.


Символы и текстовые слои

Symbol layers являются одними из самых дорогих при анимациях:

  • требуется вычисление collision detection;
  • пересчёт позиции текста при каждом изменении камеры;
  • динамическое размещение и скрытие объектов.

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

Оптимизации:

  • уменьшение количества символов;
  • использование text-optional свойств;
  • снижение text-size динамическими ограничениями;
  • агрегация данных на уровне источника.

Частота кадров и throttling

По умолчанию рендеринг стремится к 60 FPS. Однако фактическая частота зависит от:

  • сложности сцены;
  • количества активных слоёв;
  • плотности векторных тайлов;
  • частоты обновления источников.

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

Дополнительные механизмы контроля:

  • ограничение интенсивности обновлений данных;
  • ручное управление обновлением через map.triggerRepaint();
  • использование requestAnimationFrame вне внутреннего цикла карты только при необходимости синхронизации внешних анимаций.

Динамическое обновление данных и batching

Частые вызовы setData или setFeatureState приводят к fragmentation рендер-пайплайна.

Эффективный подход основан на батчинге:

  • группировка обновлений за один кадр;
  • накопление изменений в буфере;
  • применение обновлений в одном цикле рендера.

Это снижает:

  • количество перерасчётов геометрии;
  • число WebGL buffer uploads;
  • нагрузку на garbage collector.

Оптимизация переходов (transition)

Слои в MapLibre поддерживают transition-параметры:

  • duration;
  • delay.

Каждое изменение paint-параметра может запускать анимационный переход. При большом количестве слоёв это создаёт каскадное увеличение нагрузки.

Критический фактор — количество одновременно анимируемых свойств. Чем их больше, тем выше вероятность постоянного repaint.


Геометрические изменения и их стоимость

Изменение геометрии источника имеет разные уровни стоимости:

  • точечные данные — минимальная нагрузка;
  • линии — средняя нагрузка;
  • полигоны — высокая нагрузка из-за триангуляции.

При анимациях перемещения объектов предпочтительно:

  • использовать point sources вместо геометрических пересборок;
  • обновлять координаты через минимальные delta-изменения;
  • избегать полной пересборки GeoJSON.

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

При интеграции с внешними анимационными системами (DOM, canvas, Three.js) возникает необходимость синхронизации циклов рендеринга.

Основные проблемы:

  • дублирование requestAnimationFrame циклов;
  • несогласованность кадров;
  • избыточные repaint вызовы карты.

Оптимальный подход — привязка внешнего цикла к состоянию карты:

  • запуск внешней анимации только при map.isMoving();
  • остановка при idle;
  • синхронизация обновлений через единый RAF-цикл.

Уменьшение нагрузки при сложных сценах

Ключевые стратегии оптимизации:

  • сокращение количества активных слоёв;
  • отключение невидимых слоёв при zoom-уровнях;
  • использование растровых тайлов вместо векторных при статичных сценах;
  • минимизация выражений в style specification;
  • агрегация данных на серверной стороне.

Профилирование анимаций

Для анализа производительности используются:

  • FPS мониторинг;
  • инспекция repaint событий;
  • анализ WebGL draw calls;
  • профилирование CPU usage в DevTools.

Основной показатель эффективности — стабильность кадра (frame consistency), а не только средний FPS.


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

При перегрузке система демонстрирует характерные эффекты:

  • пропуск кадров анимации;
  • снижение плавности easing-функций;
  • задержка обновления тайлов;
  • временная фиксация камеры.

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