Создание сложных последовательностей

Сложные анимационные сценарии в Motion One строятся на комбинации последовательного и параллельного исполнения. Базовая функция animate() позволяет запускать отдельные анимации, однако при масштабировании интерфейсов требуется управление целыми группами эффектов, их синхронизация и точное позиционирование во времени.

Последовательность возникает тогда, когда одна анимация должна стартовать после завершения другой. Параллельность — когда несколько анимаций выполняются одновременно, но с возможностью независимого контроля длительности и задержек.

Ключевая особенность Motion One заключается в том, что управление временем вынесено в отдельный слой — таймлайн.


Таймлайн как основа композиции

Таймлайн в Motion One представляет собой контейнер, управляющий набором анимаций как единым процессом. Каждая добавленная анимация получает относительное положение во времени.

import { timeline } from "motion";

timeline([
  ["div", { opacity: 1 }, { duration: 0.3 }],
  ["div", { x: 100 }, { duration: 0.5 }]
]);

Каждый элемент массива описывает отдельный шаг. Первый параметр — цель, второй — свойства, третий — опции.

Важный принцип: временная структура определяется порядком элементов, а не вложенностью кода.


Относительное позиционирование анимаций

Для создания сложных сценариев недостаточно линейной последовательности. Motion One поддерживает смещение во времени с помощью параметра at.

timeline([
  ["h1", { opacity: 1 }, { duration: 0.4 }],
  ["h1", { y: -20 }, { at: 0.2, duration: 0.5 }],
  ["p", { opacity: 1 }, { at: "<", duration: 0.3 }]
]);

Значения at позволяют управлять моментом старта относительно:

  • абсолютного времени таймлайна
  • предыдущей анимации ("<")
  • конца предыдущей анимации (">")

Это позволяет строить многослойные сцены без необходимости ручного расчета задержек.


Синхронизация групп элементов

При работе с коллекциями DOM-элементов важно управлять их поведением как единой системой. Motion One предоставляет механизм stagger через вспомогательную функцию stagger.

import { animate, stagger } from "motion";

animate(
  ".card",
  { opacity: 1, transform: "translateY(0px)" },
  { delay: stagger(0.1) }
);

Каждый элемент получает смещённую задержку, формируя каскадный эффект.

При использовании внутри таймлайна stagger усиливает контроль над группами:

timeline([
  [
    ".item",
    { scale: 1 },
    { delay: stagger(0.05), duration: 0.3 }
  ]
]);

Композиция вложенных сценариев

Сложные интерфейсы требуют объединения независимых анимационных блоков. Вместо монолитного таймлайна используется композиция функций, возвращающих части сценария.

const fadeIn = (target) => [
  [target, { opacity: 1 }, { duration: 0.3 }]
];

const slideUp = (target) => [
  [target, { y: 0 }, { duration: 0.5 }]
];

timeline([
  ...fadeIn("header"),
  ...slideUp("header"),
  ...fadeIn("section")
]);

Такой подход позволяет формировать библиотеку переиспользуемых анимационных блоков.


Управление перекрывающимися анимациями

При параллельном выполнении нескольких эффектов важно учитывать их взаимодействие по свойствам трансформации и композиции кадров.

timeline([
  ["box", { x: 200 }, { duration: 1 }],
  ["box", { rotate: 180 }, { at: 0, duration: 1 }]
]);

Обе анимации стартуют одновременно, создавая комбинированное движение. Motion One объединяет трансформации в единый CSS transform, минимизируя конфликт между свойствами.


Использование временных якорей

Якоря времени позволяют привязывать анимации к ключевым точкам сценария.

  • 0 — начало таймлайна
  • "<" — начало предыдущей анимации
  • ">" — конец предыдущей анимации
timeline([
  ["title", { opacity: 1 }, { duration: 0.4 }],
  ["subtitle", { opacity: 1 }, { at: "<", duration: 0.3 }],
  ["button", { scale: 1 }, { at: ">", duration: 0.2 }]
]);

Такая модель исключает необходимость ручного вычисления временных смещений и делает структуру анимации декларативной.


Управление длительностью всей сцены

Таймлайн может быть растянут или сжат через изменение длительности отдельных шагов или добавление пауз.

timeline([
  ["overlay", { opacity: 1 }, { duration: 0.2 }],
  ["modal", { scale: 1 }, { duration: 0.6 }],
  ["overlay", { opacity: 0 }, { at: "+0.5", duration: 0.2 }]
]);

Параметр at с числовым значением позволяет вводить явные задержки, формируя паузы между блоками.


Вложенные таймлайны

Сложные интерфейсы часто требуют вложенных сценариев, где один таймлайн становится частью другого.

const modalTimeline = timeline([
  ["modal", { scale: 1 }, { duration: 0.4 }],
  ["content", { opacity: 1 }, { at: "<", duration: 0.3 }]
]);

timeline([
  modalTimeline,
  ["backdrop", { opacity: 1 }, { duration: 0.2 }]
]);

Вложенный таймлайн исполняется как единый шаг, но сохраняет внутреннюю структуру.


Динамическое построение последовательностей

Анимации могут формироваться на основе данных, что особенно важно при работе с динамическими интерфейсами.

const items = ["a", "b", "c"];

timeline(
  items.map((item, index) => [
    `#${item}`,
    { opacity: 1, y: 0 },
    { at: index * 0.1, duration: 0.4 }
  ])
);

Такой подход позволяет масштабировать последовательности без изменения структуры кода.


Контроль прерывания и повторного запуска

Сложные сценарии требуют возможности прерывания текущего выполнения и перезапуска с нового состояния.

const controls = timeline([
  ["panel", { x: 300 }, { duration: 0.6 }],
  ["panel", { opacity: 0 }, { duration: 0.3 }]
]);

controls.pause();
controls.play();
controls.stop();

Управляющий объект предоставляет доступ к состоянию анимации и позволяет синхронизировать её с пользовательскими событиями.


Координация нескольких таймлайнов

В крупных интерфейсах часто используются несколько независимых таймлайнов, работающих синхронно.

const intro = timeline([
  ["title", { opacity: 1 }, { duration: 0.4 }]
]);

const content = timeline([
  ["text", { y: 0 }, { duration: 0.5 }]
]);

intro.finished.then(() => {
  content.play();
});

Такой подход разделяет сценарии на логические блоки и упрощает управление сложной сценой.


Согласование анимаций через общие параметры

Единообразие поведения достигается через согласование параметров длительности, easing и задержек.

timeline([
  ["card", { scale: 1 }, { duration: 0.3, easing: "ease-out" }],
  ["icon", { rotate: 180 }, { at: "<", duration: 0.3, easing: "ease-out" }],
  ["text", { opacity: 1 }, { at: ">-0.1", duration: 0.2 }]
]);

Комбинации относительных смещений и одинаковых easing-функций создают визуально согласованные сцены.


Построение сложных сцен из атомарных анимаций

Атомарный подход предполагает создание минимальных анимационных функций, каждая из которых отвечает за одно свойство или эффект.

const fade = (target) => [target, { opacity: 1 }, { duration: 0.2 }];
const move = (target) => [target, { y: 0 }, { duration: 0.4 }];
const rotate = (target) => [target, { rotate: 90 }, { duration: 0.3 }];

Комбинация таких атомов формирует сложные сцены без дублирования логики.

timeline([
  fade("element"),
  move("element"),
  rotate("element")
]);

Масштабирование анимационных систем

При росте приложения анимации перестают быть локальной задачей и превращаются в отдельную систему управления состояниями интерфейса. Motion One позволяет структурировать такие системы через:

  • разделение таймлайнов по функциональным зонам
  • использование переиспользуемых анимационных блоков
  • синхронизацию через промисы завершения
  • относительное позиционирование вместо абсолютных таймингов

Такой подход снижает сложность сопровождения и делает поведение интерфейса предсказуемым даже при большом количестве одновременно активных эффектов