Покадровая анимация

Покадровая анимация опирается на дискретное управление состоянием объекта на каждом кадре отрисовки. Вместо непрерывного математического интерполирования значения задаются явно для каждого шага времени или вычисляются вручную внутри функции, вызываемой на каждом кадре. В контексте Popmotion такой подход реализуется через низкоуровневые примитивы вроде frame, animate с ручным контролем, а также через комбинирование функций времени и состояния.

Любая покадровая анимация в JavaScript в конечном счёте опирается на механизм:

  • requestAnimationFrame
  • цикл обновления состояния
  • перерисовка DOM или canvas

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

Кадр в браузере обычно синхронизирован с частотой обновления экрана (60 FPS, иногда 120 FPS). Это означает, что между кадрами проходит ~16.6 мс. Покадровая анимация оперирует именно этим интервалом, а не временем как непрерывной величиной.


Примитив frame в Popmotion

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

import { frame } from "popmotion";

frame((info) => {
  console.log(info.timestamp);
});

info содержит данные о текущем кадре:

  • timestamp — текущее время
  • delta — разница между текущим и предыдущим кадром
  • elapsed — общее время с начала запуска

Покадровая анимация в этом случае строится вокруг накопления состояния:

import { frame } from "popmotion";

let position = 0;

frame((info) => {
  position += info.delta * 0.1;
  element.style.transform = `translateX(${position}px)`;
});

Здесь отсутствует заранее заданная кривая движения. Каждое значение вычисляется строго в момент кадра.


Управление состоянием вручную

Ключевая особенность покадровой модели — явное управление состоянием.

let x = 0;
let velocity = 2;

frame(({ delta }) => {
  x += velocity * delta;

  if (x > 300) {
    x = 300;
    velocity = -velocity;
  }

  if (x < 0) {
    x = 0;
    velocity = -velocity;
  }

  element.style.transform = `translateX(${x}px)`;
});

Здесь реализована простая физическая логика без использования встроенных интерполяций. Такой подход позволяет моделировать:

  • столкновения
  • инерцию
  • пружины (вручную)
  • сложные пользовательские правила

Отличие от интерполяционных анимаций

Popmotion также поддерживает декларативные анимации через animate, где движение задаётся от начального до конечного значения:

import { animate } from "popmotion";

animate({
  from: 0,
  to: 300,
  onUpdate: (v) => {
    element.style.transform = `translateX(${v}px)`;
  }
});

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

  • нет понятия “from/to”
  • нет встроенной кривой easing (если не добавить вручную)
  • состояние полностью управляется пользователем

Построение анимации через delta time

Главный принцип устойчивой покадровой анимации — использование delta, а не фиксированного шага.

frame(({ delta }) => {
  position += speed * (delta / 16.67);
});

Такой подход делает движение независимым от FPS:

  • при 30 FPS delta больше
  • при 120 FPS delta меньше

Popmotion отдаёт delta уже нормализованным относительно реального времени, что позволяет строить предсказуемые анимации.


Использование остановки анимации

Любая покадровая анимация должна иметь механизм остановки.

В Popmotion frame возвращает функцию отмены:

const stop = frame(() => {
  element.style.transform = "translateX(10px)";
});

stop();

Это критично для:

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

Реализация инерции

Покадровая модель особенно удобна для имитации физики.

let position = 0;
let velocity = 0;
let friction = 0.95;

frame(({ delta }) => {
  position += velocity * delta;
  velocity *= friction;

  if (Math.abs(velocity) < 0.01) {
    velocity = 0;
  }

  element.style.transform = `translateX(${position}px)`;
});

Здесь поведение полностью управляется состоянием, а не функцией времени.


Обработка пользовательского ввода

Популярный сценарий — связка покадровой анимации с drag-событиями.

let x = 0;
let isDragging = false;
let lastX = 0;

element.addEventListener("mousedown", () => {
  isDragging = true;
});

document.addEventListener("mousemove", (e) => {
  if (!isDragging) return;
  x = e.clientX;
});

document.addEventListener("mouseup", () => {
  isDragging = false;
});

frame(() => {
  element.style.transform = `translateX(${x}px)`;
});

Здесь кадры служат только для синхронизации DOM с состоянием, а логика обновления происходит вне анимационного цикла.


Гибридная модель: кадры + интерполяция

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

import { frame, linear } from "popmotion";

const interpolate = linear(0, 100, 500);

frame(({ elapsed }) => {
  const value = interpolate(elapsed);
  element.style.width = `${value}px`;
});

В этом случае:

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

Принципы стабильной покадровой анимации

Ключевые инженерные принципы:

1. Единственный источник истины Состояние хранится в переменных, а не в DOM.

2. Полная детерминированность кадра Каждый кадр вычисляется только из текущего состояния и времени.

3. Отсутствие скрытых побочных эффектов Обновление DOM происходит строго один раз за кадр.

4. Минимизация вычислений Код внутри frame должен быть максимально лёгким.


Работа с несколькими объектами

Покадровая анимация масштабируется на множество элементов через единый цикл:

const items = [
  { x: 0, v: 1 },
  { x: 50, v: 2 },
  { x: 100, v: 3 }
];

frame(({ delta }) => {
  for (let item of items) {
    item.x += item.v * delta;
  }

  render();
});

function render() {
  elements.forEach((el, i) => {
    el.style.transform = `translateX(${items[i].x}px)`;
  });
}

Такой подход предотвращает создание множества requestAnimationFrame циклов.


Управление сложными сценами

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

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

Popmotion в этом случае выступает как временной движок, а не как система анимации готовых переходов.


Ошибки при использовании покадрового подхода

Типичные проблемы:

Перегрузка логики внутри кадра Сложные вычисления приводят к падению FPS.

Отсутствие остановки Анимации продолжают работать после удаления элементов.

Игнорирование delta Приводит к нестабильной скорости движения.

Множественные независимые frame-циклы Создают конкуренцию за ресурсы и усложняют синхронизацию.


Связь с архитектурой Popmotion

Покадровая анимация — базовый слой, на котором строятся:

  • физические модели
  • жесты
  • timeline
  • spring-анимации

Все высокоуровневые API в конечном счёте сводятся к обработке кадров и времени.

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