Базовые принципы анимации

Анимация в deck.gl строится вокруг непрерывного пересчёта состояния сцены и повторного рендеринга WebGL-контекста, при котором изменения параметров слоёв, камеры и атрибутов геометрии интерпретируются как временные переходы между состояниями. Основой служит модель декларативного описания сцены: визуализация задаётся как функция от props, а анимация становится результатом изменения этих props во времени.

В основе работы deck.gl лежит цикл, синхронизированный с requestAnimationFrame. Каждый кадр включает:

  • получение актуального состояния viewState
  • пересчёт свойств слоёв (layers)
  • обновление атрибутов WebGL при необходимости
  • рендер сцены

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

import {Deck} from '@deck.gl/core';
import {ScatterplotLayer} from '@deck.gl/layers';

const deckgl = new Deck({
  initialViewState: {
    longitude: 0,
    latitude: 0,
    zoom: 2
  },
  controller: true,
  layers: []
});

Рендер-цикл активируется автоматически при изменении props или интерактивных событий камеры. Это создаёт основу для анимации без необходимости явного управления кадрами.

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

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

setInterval(() => {
  deckgl.setProps({
    layers: [
      new ScatterplotLayer({
        id: 'points',
        data: [{position: [Math.random() * 360 - 180, 0]}],
        getPosition: d => d.position,
        getRadius: 100000
      })
    ]
  });
}, 1000);

При каждом обновлении setProps происходит пересборка слоя и оценка необходимости анимационного перехода. Важно, что сам механизм не зависит от таймера — он зависит от изменения состояния.

Интерполяция свойств слоёв

Плавность анимации достигается через интерполяцию значений атрибутов. В deck.gl это реализовано на уровне слоя через transitions.

new ScatterplotLayer({
  id: 'animated-points',
  data,
  getPosition: d => d.position,
  getRadius: d => d.radius,
  transitions: {
    getPosition: 500,
    getRadius: 500
  }
});

Каждое свойство, указанное в transitions, становится объектом временной интерполяции. При изменении входных данных система:

  1. Сохраняет начальное значение
  2. Фиксирует целевое значение
  3. Интерполирует их на каждом кадре

Математически процесс можно представить как линейную интерполяцию:

[ v(t) = v_0 + (v_1 - v_0) ]

где () — нормализованное время перехода.

Управление временем переходов

Система переходов управляется внутренним таймером, синхронизированным с рендер-циклом. Каждое изменение props инициирует новый transition state.

Основные характеристики:

  • длительность задаётся в миллисекундах
  • поддерживается отмена и перезапуск перехода
  • возможно параллельное выполнение нескольких переходов

Пример изменения нескольких свойств одновременно:

new PolygonLayer({
  id: 'polygons',
  data,
  getPolygon: d => d.coordinates,
  getFillColor: d => d.color,
  transitions: {
    getPolygon: 800,
    getFillColor: 1200
  }
});

Разные длительности создают асинхронную динамику визуальных изменений.

Камера как источник анимации

Анимация в deck.gl не ограничивается слоями. Изменение viewState также является источником временной динамики. Камера описывается как набор параметров:

  • longitude
  • latitude
  • zoom
  • pitch
  • bearing

Изменение этих параметров может быть интерполировано аналогично атрибутам слоёв.

deckgl.setProps({
  viewState: {
    longitude: 30,
    latitude: 50,
    zoom: 5,
    transitionDuration: 1000,
    transitionInterpolator: new FlyToInterpolator()
  }
});

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

Атрибутная модель GPU и анимация

Каждый слой в deck.gl компилируется в набор WebGL атрибутов. Анимация на низком уровне сводится к обновлению буферов GPU.

Процесс включает:

  • хранение старого vertex buffer
  • создание нового buffer при изменении данных
  • частичное обновление через diffing
  • постепенную интерполяцию в шейдере или CPU

В высокопроизводительных сценариях используется attribute updater, минимизирующий пересоздание буферов.

Временные функции и непрерывная анимация

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

function animate() {
  const time = Date.now() * 0.001;

  deckgl.setProps({
    layers: [
      new ScatterplotLayer({
        id: 'wave',
        data,
        getPosition: d => [
          d.base[0],
          d.base[1] + Math.sin(time + d.phase) * 5
        ],
        getRadius: 50000
      })
    ]
  });

  requestAnimationFrame(animate);
}

animate();

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

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

В сложных сценах одновременно активны:

  • transitions слоёв
  • анимация камеры
  • внешние requestAnimationFrame обновления
  • пользовательские события

deck.gl объединяет эти источники в единый render pass. Приоритет обновления определяется изменениями props, а не источником их происхождения.

Зависимость анимации от diff-механизма слоёв

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

Это создаёт эффект:

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

Нелинейные переходы и easing-функции

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

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

  • linear
  • easeIn
  • easeOut
  • cubic easing

Применение:

transitions: {
  getPosition: {
    duration: 1000,
    easing: t => t * t * (3 - 2 * t)
  }
}

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

Анимация больших наборов данных

При работе с тысячами и миллионами объектов ключевым становится перенос вычислений в GPU. В deck.gl анимация часто реализуется через:

  • attribute interpolation in shaders
  • instanced rendering
  • precomputed animation frames

Shader-based подход позволяет выполнять интерполяцию параллельно для всех вершин:

float t = uniformProgress;
vec3 position = mix(positionStart, positionEnd, t);

Это обеспечивает масштабируемость без значительного роста CPU-нагрузки.

Состояние слоя как функция времени

Фундаментальная модель может быть выражена как:

[ Layer(t) = f(Data(t), ViewState(t))]

где каждый кадр является вычислением функции от текущего времени. deck.gl не хранит «анимацию» как объект, а вычисляет её как производную состояния сцены.

Связь между реактивностью и анимацией

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

  • изменение состояния → diff
  • diff → transition
  • transition → render loop

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