В сложных интерфейсах отдельные анимации редко существуют изолированно. Движение элементов формируется как результат наложения нескольких независимых эффектов: появления, трансформации, реакций на скролл, состояния наведения и переходов между экранами. В Motion One композиция таких эффектов строится на принципах приоритизации, синхронизации и повторного использования анимационных примитивов.
Ключевая особенность подхода заключается в том, что каждая анимация рассматривается не как конечный сценарий, а как функция, которая может быть включена в более крупную систему поведения интерфейса.
Композиция начинается с разделения эффектов по уровням ответственности:
Motion One опирается на единый API animate, который
позволяет запускать независимые анимационные задачи, не блокирующие друг
друга.
import { animate } from "motion";
animate(".card", { opacity: [0, 1], y: [20, 0] });
animate(".badge", { scale: [0.8, 1] });
Каждая анимация существует как отдельная единица, но визуально формируется единая сцена. Композиция достигается не объединением кода, а согласованием временных параметров и свойств.
При усложнении интерфейса появляется необходимость управлять порядком запуска. Базовый механизм — управление задержками и длительностью, но в композиции сложных эффектов этого недостаточно.
animate(".title", { opacity: [0, 1], y: [10, 0] }, { duration: 0.4 });
animate(".subtitle", { opacity: [0, 1], y: [10, 0] }, { delay: 0.2, duration: 0.4 });
Такой подход формирует линейную последовательность, однако остаётся хрупким при масштабировании. Изменение одного тайминга требует пересчёта всех зависимых эффектов.
Более устойчивый способ композиции заключается в использовании
временной оси через timeline. В этом случае анимации
описываются как элементы общей структуры времени.
import { timeline } from "motion";
timeline([
[".title", { opacity: [0, 1], y: [10, 0] }, { duration: 0.4 }],
[".subtitle", { opacity: [0, 1], y: [10, 0] }, { at: 0.2 }],
[".button", { scale: [0.9, 1] }, { at: 0.4 }]
]);
Параметр at становится ключевым инструментом композиции,
позволяя выстраивать перекрывающиеся и параллельные анимации без жесткой
зависимости от предыдущих таймингов.
Одним из устойчивых паттернов становится представление анимации как функции, возвращающей эффект.
const fadeInUp = (selector) =>
animate(selector, { opacity: [0, 1], y: [20, 0] }, { duration: 0.5 });
const scaleIn = (selector) =>
animate(selector, { scale: [0.8, 1] }, { duration: 0.4 });
Далее композиция формируется через последовательный вызов:
fadeInUp(".title");
scaleIn(".icon");
fadeInUp(".subtitle");
При необходимости согласования времени добавляется слой координации:
const introSequence = () => {
fadeInUp(".title");
scaleIn(".icon");
fadeInUp(".subtitle");
};
Такой подход формирует модульную систему эффектов, где каждая анимация становится переиспользуемым строительным блоком.
В сложных интерфейсах часто возникает ситуация одновременного воздействия нескольких анимационных потоков на один элемент. Это требует контроля приоритетов трансформаций.
animate(".panel", { x: [0, 100] });
animate(".panel", { opacity: [1, 0.5] });
Обе анимации применяются к одному узлу, но затрагивают разные свойства. Композиция становится безопасной при соблюдении правила разделения трансформационных и визуальных каналов.
Проблема возникает при конфликте трансформаций:
animate(".box", { x: 100 });
animate(".box", { x: -50 });
В таких случаях последняя анимация перезаписывает предыдущую, поэтому композиция требует явного объединения значений.
Для предотвращения конфликтов используется объединение трансформаций в единый объект.
animate(".box", {
x: 100,
y: 50,
rotate: 15
});
Композиция смещений теперь происходит внутри одного анимационного контекста, где свойства интерпретируются как части единого трансформационного пространства.
Сложные интерфейсы редко опираются только на запуск анимаций. Важную роль играет реакция на состояние: hover, focus, in-view, active.
import { animate, inView } from "motion";
inView(".card", ({ target }) => {
animate(target, { opacity: [0, 1], y: [30, 0] }, { duration: 0.6 });
});
Композиция формируется как цепочка условий, где каждый триггер запускает собственный набор эффектов.
Особую категорию составляют эффекты, завязанные на прокрутку. Они часто пересекаются с локальными переходами.
import { scroll, animate } from "motion";
scroll(({ y }) => {
animate(".header", { scale: 1 - y.progress * 0.2 });
});
В композиции важно разделение:
Их наложение требует отсутствия пересечения по одному и тому же свойству в одинаковом временном масштабе.
Сложный эффект редко проектируется как монолит. Он разбивается на слои:
const enter = (el) =>
animate(el, { opacity: [0, 1], y: [20, 0] });
const exit = (el) =>
animate(el, { opacity: [1, 0], y: [0, -20] });
const hover = (el) =>
animate(el, { scale: 1.05 });
Композиция формируется через переключение между этими слоями в зависимости от состояния системы.
При наложении эффектов возникает необходимость управления жизненным циклом анимаций. Одна из ключевых проблем — конкуренция за управление одним свойством.
Motion One позволяет завершать или переопределять текущие анимации
через повторный вызов animate, что формирует модель
«последний вызов имеет приоритет».
let current;
const open = () => {
current = animate(".modal", { opacity: [0, 1], scale: [0.9, 1] });
};
const close = () => {
current = animate(".modal", { opacity: [1, 0], scale: [1, 0.9] });
};
Композиция здесь строится через управление ссылкой на активный процесс, а не через объединение ключевых кадров.
Наиболее масштабируемый подход заключается в построении цепочек функций, каждая из которых модифицирует или расширяет эффект.
const withFade = (animation) => (selector) =>
animation(selector, { opacity: [0, 1] });
const withSlide = (animation) => (selector) =>
animation(selector, { y: [20, 0] });
const composed = withFade(withSlide(animate));
composed(".item");
Такая структура позволяет собирать поведение из независимых трансформаций, не привязывая их к конкретным элементам.
При построении сложных интерфейсных сцен ключевым становится управление глобальным временным сценарием. Timeline превращается в декларативное описание всей сцены, где каждый элемент занимает своё место в общей структуре.
timeline([
[".bg", { opacity: [0, 1] }, { duration: 1 }],
[".hero", { y: [40, 0], opacity: [0, 1] }, { at: 0.2 }],
[".cta", { scale: [0.8, 1] }, { at: 0.6 }]
]);
Композиция здесь перестаёт быть набором независимых вызовов и становится системой координат времени, где каждый эффект занимает строго определённую позицию.
По мере роста сложности интерфейса возникает необходимость в многоуровневой организации:
Motion One в такой архитектуре используется как низкоуровневый исполнитель, в то время как композиция формируется на уровне бизнес-логики и структуры интерфейса.