Компонентный подход в контексте работы с библиотекой Popmotion предполагает разбиение сложной анимационной логики на независимые, переиспользуемые и изолированные части. Каждая часть отвечает за конкретное поведение: движение, изменение прозрачности, реакцию на ввод или синхронизацию состояний.
Вместо монолитных анимационных цепочек формируются небольшие модули, которые можно комбинировать, заменять или тестировать отдельно. Это особенно важно при разработке интерфейсов с высокой интерактивностью.
Ключевые свойства компонентного подхода:
Popmotion предоставляет несколько примитивов, которые становятся «компонентами» анимационной системы:
tweenspringdecayКаждая из них представляет собой поток значений во времени.
Хранят текущее состояние анимации и позволяют подписываться на изменения:
import { value } from "popmotion";
const x = value(0);
x.subscribe(v => console.log(v));
x.update(100);
parallel — одновременное выполнениеchain — последовательное выполнениеstagger — выполнение с задержкойКомпонент в Popmotion — это функция, которая возвращает готовую анимацию или управляет состоянием.
import { tween } from "popmotion";
const moveX = (from, to, duration = 300) => {
return tween({
from,
to,
duration
});
};
Теперь moveX можно использовать в разных частях
приложения.
Компонент может содержать собственное состояние через
value.
import { value, tween } from "popmotion";
const animatedValue = () => {
const val = value(0);
const animation = tween({
from: 0,
to: 100
}).start(val);
return {
value: val,
stop: () => animation.stop()
};
};
Такой компонент:
Компоненты легко комбинируются друг с другом.
import { parallel } from "popmotion";
const fadeAndMove = (element) => {
return parallel(
moveX(0, 300),
tween({ from: 0, to: 1 })
).start({
update: ([x, opacity]) => {
element.style.transform = `translateX(${x}px)`;
element.style.opacity = opacity;
}
});
};
import { chain } from "popmotion";
const sequence = chain(
moveX(0, 100),
moveX(100, 0)
);
Каждый компонент должен решать одну задачу:
moveXfadescaleconst fade = (from, to) =>
tween({ from, to });
const scale = (from, to) =>
tween({ from, to });
Комбинирование:
parallel(
fade(0, 1),
scale(0.5, 1)
);
Компоненты должны быть гибкими через параметры.
const springMove = ({ from, to, stiffness = 100 }) => {
return spring({
from,
to,
stiffness
});
};
Это позволяет:
Компоненты можно переиспользовать в разных UI-элементах.
const buttonHover = (element) => {
const scaleUp = tween({ from: 1, to: 1.1 });
const scaleDown = tween({ from: 1.1, to: 1 });
element.addEventListener("mouseenter", () => {
scaleUp.start(v => {
element.style.transform = `scale(${v})`;
});
});
element.addEventListener("mouseleave", () => {
scaleDown.start(v => {
element.style.transform = `scale(${v})`;
});
});
};
Popmotion хорошо сочетается с реактивным подходом: значения могут зависеть друг от друга.
import { value } from "popmotion";
const x = value(0);
const y = value(0);
x.subscribe(v => {
y.update(v * 2);
});
Создается зависимость:
x автоматически влияет на yКомпоненты часто управляют DOM напрямую.
const createMover = (element) => {
return moveX(0, 200).start(v => {
element.style.transform = `translateX(${v}px)`;
});
};
Для повышения гибкости:
Каждый компонент должен уметь:
const createAnimation = () => {
let control;
return {
start() {
control = moveX(0, 100).start(console.log);
},
stop() {
control && control.stop();
}
};
};
Компоненты позволяют описывать анимацию декларативно:
const animation = parallel(
moveX(0, 300),
fade(0, 1)
);
Вместо пошагового управления:
Компоненты можно оборачивать для добавления функциональности.
const withLogging = (animationFactory) => (...args) => {
const animation = animationFactory(...args);
return {
start: (update) => {
return animation.start(v => {
console.log(v);
update(v);
});
}
};
};
Использование:
const loggedMove = withLogging(moveX);
На основе базовых компонентов строятся более сложные.
const modalAnimation = (element) => {
return parallel(
moveX(-100, 0),
fade(0, 1)
).start(([x, opacity]) => {
element.style.transform = `translateX(${x}px)`;
element.style.opacity = opacity;
});
};
Такой компонент:
Компоненты могут запускать друг друга:
const first = moveX(0, 100);
const second = moveX(100, 200);
first.start({
complete: () => second.start(console.log)
});
Или через chain:
chain(first, second);
Из-за изоляции компоненты легко тестировать:
const anim = moveX(0, 100);
anim.start({
update: v => {
// проверка значений
}
});
Можно:
Компонентный подход в Popmotion постепенно приводит к архитектуре, где:
Такая структура делает код: