В системах анимации Popmotion ключевым элементом становится контроль времени исполнения. Любая анимация рассматривается как функция, зависящая от прогресса, где время перестаёт быть линейным и становится управляемым параметром.
Каждая анимация возвращает управляющий объект, через который происходит вмешательство в поток выполнения: приостановка, возобновление, перемотка к произвольной точке, изменение скорости воспроизведения.
Основная идея заключается в разделении:
Такое разделение позволяет манипулировать состоянием без пересоздания анимации.
После запуска анимации создаётся контроллер, инкапсулирующий текущее состояние:
Контроллер предоставляет набор методов, позволяющих вмешиваться в ход выполнения без разрушения экземпляра анимации.
Типичный набор операций:
pause() — замораживание текущего состоянияplay() — продолжение с сохранённого моментаstop() — полная остановка и сбросseek(value) — установка позиции времениreverse() — инверсия направления воспроизведенияupdate() — внутреннее обновление состоянияЭти методы работают поверх общего таймлайна, не изменяя саму функцию интерполяции.
Механизм паузы реализуется через фиксацию временной метки, с которой была остановлена анимация.
При вызове приостановки фиксируются:
Далее цикл обновления продолжает выполняться, но вычисление прогресса блокируется.
import { tween } from 'popmotion';
const animation = tween({
from: 0,
to: 100,
duration: 2000
}).start({
update: v => console.log(v)
});
setTimeout(() => {
animation.pause();
}, 500);
setTimeout(() => {
animation.play();
}, 1500);
В момент возобновления происходит перерасчёт начальной точки отсчёта, чтобы исключить скачок времени. Анимация продолжается так, как будто пауза была частью общего таймлайна.
Перемотка реализует концепцию «random access timeline». Вместо линейного воспроизведения появляется возможность установки любого положения внутри диапазона анимации.
Метод перемотки принимает:
При изменении позиции вычисление интерполяции пересчитывается немедленно.
const controls = tween({
from: 0,
to: 1000,
duration: 4000
}).start({
update: v => console.log(v)
});
setTimeout(() => {
controls.seek(2000);
}, 1000);
Внутри механизма происходит преобразование:
При этом все easing-функции применяются повторно, как если бы анимация «всегда находилась» в этой точке.
Режим реверса строится на инверсии временной оси. Вместо увеличения прогресса от 0 до 1 используется движение от 1 к 0.
Внутренне это реализуется через коэффициент скорости:
const animation = tween({
from: 0,
to: 500,
duration: 3000
}).start();
setTimeout(() => {
animation.reverse();
}, 1500);
При изменении направления сохраняется текущая позиция, а пересчёт времени происходит относительно неё.
Важно, что реверс не требует пересоздания анимации — меняется только интерпретация времени.
Скорость анимации является множителем временной шкалы. Она влияет на то, как быстро происходит переход между состояниями.
1 — нормальная скорость0.5 — замедление2 — ускорение-1 — обратное воспроизведениеИзменение скорости эквивалентно масштабированию времени, а не пересчёту значений.
const controls = tween({
from: 0,
to: 300,
duration: 3000
}).start();
setTimeout(() => {
controls.speed = -1;
}, 1000);
Такой подход позволяет комбинировать ускорение и перемотку без дополнительных вычислений интерполяции.
При работе с несколькими анимациями управление временем становится централизованным. Таймлайн объединяет несколько независимых анимаций в единый поток управления.
Каждая анимация:
Перемотка таймлайна влияет сразу на все вложенные анимации.
import { timeline } from 'popmotion';
const sequence = timeline([
tween({ from: 0, to: 100, duration: 1000 }),
tween({ from: 100, to: 200, duration: 1000 })
]).start();
setTimeout(() => {
sequence.seek(500);
}, 800);
Перемещение по таймлайну автоматически пересчитывает состояние каждого шага.
В композициях анимаций пауза распространяется рекурсивно. Приостановка родительского контроллера блокирует все дочерние элементы.
При этом сохраняется локальное состояние каждого сегмента:
После возобновления все элементы продолжают выполнение синхронно, без десинхронизации.
В системах с физикой (spring, inertia) перемотка работает иначе, чем в tween-анимациях. Вместо прямого задания времени происходит восстановление состояния системы:
При seek() происходит пересчёт состояния системы на
основе модели движения.
Это означает, что перемотка не просто устанавливает значение, а пересчитывает динамику системы с заданной точки времени.
Все операции управления временем объединяются в один цикл обновления:
Пауза отключает шаг delta-time, перемотка переопределяет базовое значение времени, а скорость масштабирует его.
Так формируется единая модель управления временем, в которой отсутствует разделение между «воспроизведением» и «изменением состояния» — есть только трансформация временной координаты.