Popmotion предоставляет набор примитивов для управления анимацией как потоками значений во времени. Синхронизация независимых анимаций в этой системе строится не вокруг единого «timeline-движка» в классическом понимании, а вокруг композиции источников времени, комбинирования трансформаций и согласования состояний через общие значения и управляющие действия.
Каждая анимация в Popmotion является автономным источником: spring, tween, keyframes или physics генерируют поток значений, не зависящий от других потоков. Такая изоляция упрощает повторное использование, но усложняет координацию визуальных сценариев, где требуется:
Синхронизация достигается не глобальным временем сцены, а локальными стратегиями координации.
Базовая форма синхронизации — одновременный старт нескольких анимаций через общий управляющий триггер.
import { tween, spring, action } from 'popmotion';
const fade = tween({
from: 0,
to: 1,
duration: 600
});
const move = spring({
from: 0,
to: 300,
stiffness: 120
});
const start = action(({ complete }) => {
fade.start();
move.start();
complete();
});
start.start();
Несмотря на одновременный запуск, потоки остаются независимыми: различие в типах интерполяции приводит к рассинхронизации по фазе. Это создает необходимость дополнительного уровня координации.
Более строгая синхронизация достигается введением общего нормализованного прогресса от 0 до 1. Вместо запуска нескольких анимаций создается один источник времени.
import { tween, transform } from 'popmotion';
const { interpolate } = transform;
const progress = tween({
from: 0,
to: 1,
duration: 1000
});
progress.start(v => {
const opacity = interpolate([0, 1], [0, 1])(v);
const x = interpolate([0, 1], [0, 500])(v);
element.style.opacity = opacity;
element.style.transform = `translateX(${x}px)`;
});
Такой подход устраняет рассинхронизацию, поскольку все параметры зависят от одного временного источника.
Синхронизация независимых потоков часто реализуется через вычисление производных значений одного потока для управления другим.
import { tween } from 'popmotion';
const timeline = tween({
from: 0,
to: 1,
duration: 1200
});
timeline.start(v => {
const scale = 1 + v * 0.5;
const rotation = v * 180;
const opacity = v < 0.5 ? v * 2 : 1;
element.style.transform = `
scale(${scale})
rotate(${rotation}deg)
`;
element.style.opacity = opacity;
});
Здесь каждая визуальная характеристика является независимой функцией одного прогресса, что обеспечивает полную синхронность при различной динамике изменений.
Часто требуется сохранить общую структуру анимации, но сместить отдельные элементы во времени. В Popmotion это реализуется через трансформацию входного значения.
const delay = 0.2;
timeline.start(v => {
const lead = v;
const follow = Math.max(0, (v - delay) / (1 - delay));
leadElement.style.opacity = lead;
followElement.style.opacity = follow;
});
Такой подход позволяет формировать каскадные эффекты без разрыва общей временной оси.
В архитектуре Popmotion поток значений можно трактовать как функцию времени. Несколько потоков можно объединять через вычисление композиционного состояния.
import { combineLatest, tween } from 'popmotion';
const a = tween({ from: 0, to: 100, duration: 800 });
const b = tween({ from: 0, to: 1, duration: 1200 });
combineLatest(a, b).start(([x, opacity]) => {
box.style.transform = `translateX(${x}px)`;
box.style.opacity = opacity;
});
Согласование достигается через объединение последних значений потоков. Разная длительность анимаций приводит к сложным динамическим соотношениям без необходимости ручной синхронизации таймингов.
При масштабировании системы анимаций важным становится централизованное состояние, из которого выводятся все визуальные эффекты.
import { tween } from 'popmotion';
const state = {
progress: 0
};
tween({
from: 0,
to: 1,
duration: 1000
}).start(v => {
state.progress = v;
render();
});
function render() {
const p = state.progress;
circle.style.transform = `scale(${1 + p})`;
line.style.strokeDashoffset = 100 * (1 - p);
text.style.opacity = p;
}
Здесь синхронизация переносится в слой рендеринга: все элементы обновляются из одного источника состояния.
Композиция анимаций часто требует строгого порядка выполнения. В Popmotion это достигается через последовательные вызовы и управление завершением потоков.
import { tween, action } from 'popmotion';
const step1 = tween({ from: 0, to: 1, duration: 500 });
const step2 = tween({ from: 1, to: 0, duration: 500 });
step1.start({
complete: () => step2.start()
});
Синхронизация здесь строится на событии завершения, а не на общем времени.
Более сложные цепочки формируются через вложенные управляющие действия:
const sequence = action(({ complete }) => {
step1.start({
complete: () => {
step2.start({
complete
});
}
});
});
sequence.start();
Особую сложность представляет синхронизация физических анимаций. Springs не имеют фиксированной длительности, поэтому согласование происходит через привязку к общему событию или значению.
import { spring } from 'popmotion';
const sharedTarget = { x: 300 };
const springA = spring({
from: 0,
to: sharedTarget.x
});
const springB = spring({
from: 0,
to: sharedTarget.x
});
springA.start(v => boxA.style.transform = `translateX(${v}px)`);
springB.start(v => boxB.style.transform = `translateX(${v}px)`);
Согласование достигается не временем, а общим целевым состоянием.
Transform-слой позволяет строить зависимости между анимациями без прямого управления их запуском.
import { tween, transform } from 'popmotion';
const { pipe, clamp, interpolate } = transform;
const base = tween({ from: 0, to: 1, duration: 1000 });
const processed = (v) =>
pipe(
clamp(0, 1),
interpolate([0, 1], [0, 200])
)(v);
base.start(v => {
const x = processed(v);
element.style.transform = `translateX(${x}px)`;
});
Синхронизация достигается за счет общей функции преобразования, а не через координацию нескольких анимаций.
Анимации могут синхронизироваться с пользовательскими или системными событиями, превращая внешние сигналы в общий временной триггер.
import { tween } from 'popmotion';
const anim = tween({ from: 0, to: 1, duration: 700 });
button.addEventListener('click', () => {
anim.start(v => {
panel.style.opacity = v;
});
});
При таком подходе синхронизация определяется не внутренней логикой анимаций, а моментом активации внешнего события.
В сложных интерфейсах синхронизация строится на нескольких слоях:
Такая архитектура позволяет избегать жёсткой зависимости анимаций друг от друга, сохраняя при этом визуальную согласованность.
Композиционный подход делает каждый поток независимым, но управляемым через общие принципы: время, состояние и преобразование значений.