Покадровая анимация опирается на дискретное управление состоянием
объекта на каждом кадре отрисовки. Вместо непрерывного математического
интерполирования значения задаются явно для каждого шага времени или
вычисляются вручную внутри функции, вызываемой на каждом кадре. В
контексте Popmotion такой подход реализуется через низкоуровневые
примитивы вроде frame, animate с ручным
контролем, а также через комбинирование функций времени и состояния.
Любая покадровая анимация в JavaScript в конечном счёте опирается на механизм:
requestAnimationFramePopmotion предоставляет абстракции, которые позволяют не работать
напрямую с 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)`;
}
});
Покадровая модель принципиально отличается:
Главный принцип устойчивой покадровой анимации — использование
delta, а не фиксированного шага.
frame(({ delta }) => {
position += speed * (delta / 16.67);
});
Такой подход делает движение независимым от FPS:
delta больше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-циклы Создают конкуренцию за ресурсы и усложняют синхронизацию.
Покадровая анимация — базовый слой, на котором строятся:
Все высокоуровневые API в конечном счёте сводятся к обработке кадров и времени.
Popmotion не скрывает этот слой полностью, а делает его доступным
через frame, позволяя управлять анимацией на уровне
механики времени и состояния.