Параллельные анимации в Motion One основаны на принципе одновременного запуска нескольких независимых или частично связанных анимационных процессов. В отличие от последовательного исполнения, где каждый следующий эффект ожидает завершения предыдущего, параллельная модель позволяет синхронизировать изменения свойств разных элементов или одного элемента с несколькими параметрами одновременно.
Ключевой механизм, обеспечивающий параллельность, — независимость
анимационных вызовов animate(), которые могут стартовать в
одном и том же тикe без блокировки друг друга.
Самый прямолинейный способ организовать параллельную анимацию —
вызвать несколько animate() подряд.
import { animate } from "motion";
animate(".box-1", {
x: 300,
opacity: 1
});
animate(".box-2", {
y: 200,
scale: 1.2
});
Обе анимации запускаются одновременно, поскольку вызовы не зависят друг от друга. Каждая из них создаёт собственный анимационный процесс с отдельным контроллером времени.
Важно учитывать, что Motion One не блокирует поток выполнения: после
инициализации анимации управление сразу возвращается в код, а рендеринг
происходит через requestAnimationFrame.
Один элемент может участвовать сразу в нескольких анимационных потоках, если анимируются разные CSS-свойства или разные трансформации.
animate(".card", {
opacity: [0, 1]
});
animate(".card", {
scale: [0.8, 1]
});
animate(".card", {
rotate: [0, 10]
});
Каждый вызов создаёт отдельную анимацию, но итоговое визуальное состояние объединяется браузером через композицию трансформаций и свойств.
Однако при работе с transform следует учитывать, что несколько
анимаций одного и того же свойства могут конфликтовать. Например,
параллельное изменение x в разных анимациях приведёт к
перезаписи значений.
Motion One оптимизирует трансформации через единый слой
transform, объединяя x, y,
scale, rotate и другие параметры в одну
матрицу преобразования.
animate(".box", { x: 200 });
animate(".box", { scale: 1.5 });
animate(".box", { rotate: 45 });
В этом случае не происходит конфликтов, поскольку Motion One агрегирует transform-параметры в общий набор вычислений. Это позволяет безопасно запускать параллельные трансформации без ручного объединения значений.
Параллельность не означает независимость по времени. Разные анимации могут иметь одинаковую длительность, что создаёт визуально синхронный эффект.
animate(".left", {
x: 400
}, {
duration: 0.8
});
animate(".right", {
x: -400
}, {
duration: 0.8
});
Обе анимации стартуют одновременно и завершаются одновременно, создавая зеркальный эффект движения.
Если длительности различаются, синхронизация нарушается, но старт остаётся параллельным.
Параллельность может быть частично смещена по времени через
delay, создавая эффект каскадного запуска.
animate(".item-1", { opacity: 1 }, { delay: 0 });
animate(".item-2", { opacity: 1 }, { delay: 0.1 });
animate(".item-3", { opacity: 1 }, { delay: 0.2 });
Хотя технически это последовательное смещение, визуально оно воспринимается как единая параллельная система с фазовым сдвигом.
Более структурированный способ управления параллельностью —
использование timeline.
Timeline позволяет задавать пересекающиеся интервалы анимаций.
import { timeline } from "motion";
timeline([
[".box-1", { x: 200 }, { duration: 0.5 }],
[".box-2", { y: 200 }, { duration: 0.5 }],
[".box-3", { scale: 1.3 }, { duration: 0.5 }]
]);
По умолчанию элементы массива стартуют одновременно, если не указано смещение.
Параллельность в timeline становится более гибкой при использовании offset.
timeline([
[".box-1", { x: 300 }, { duration: 1 }],
[".box-2", { y: 300 }, { duration: 1, at: 0.2 }],
[".box-3", { scale: 1.5 }, { duration: 1, at: 0.4 }]
]);
Здесь вторая и третья анимации накладываются на первую, создавая частично пересекающиеся интервалы времени.
При работе с наборами элементов параллельные анимации часто применяются ко всем элементам сразу.
animate(".list-item", {
opacity: [0, 1],
x: [-20, 0]
}, {
duration: 0.4
});
Все элементы с селектором .list-item анимируются
одновременно, но с одинаковыми параметрами. Это базовая форма массовой
параллельной анимации.
При сложных интерфейсах часто требуется запуск нескольких анимационных систем, каждая из которых управляет своей частью UI.
const menu = animate(".menu", { x: 0 }, { duration: 0.3 });
const overlay = animate(".overlay", { opacity: 1 }, { duration: 0.3 });
const button = animate(".button", { scale: 1.1 }, { duration: 0.3 });
Каждый вызов возвращает контроллер анимации, что позволяет управлять ими отдельно: приостанавливать, ускорять или отменять.
Motion One возвращает animation controls, которые можно использовать для синхронизации состояния нескольких процессов.
const a1 = animate(".a", { x: 100 });
const a2 = animate(".b", { y: 100 });
Promise.all([a1.finished, a2.finished]).then(() => {
animate(".c", { opacity: 1 });
});
Такой подход позволяет запускать параллельные анимации и реагировать на их завершение как на единое событие.
Даже при одинаковом времени выполнения анимации могут иметь разные кривые движения.
animate(".box-1", { x: 300 }, { easing: "ease-out" });
animate(".box-2", { x: 300 }, { easing: "linear" });
animate(".box-3", { x: 300 }, { easing: "ease-in-out" });
Визуально это создаёт сложную композицию движения, где каждый элемент развивается в своём темпе, несмотря на общий старт.
При одновременном изменении одного и того же свойства последняя запущенная анимация имеет приоритет.
animate(".box", { x: 100 });
animate(".box", { x: 300 });
Итоговое значение будет определяться второй анимацией, поскольку она перезапишет первую.
Для избежания конфликтов используется либо объединение свойств в один
animate, либо работа через timeline.
При большом количестве одновременных анимаций важно учитывать
стоимость компоновки кадров. Motion One использует
requestAnimationFrame и оптимизацию transform-полей, однако
перегрузка DOM всё равно возможна при тысячах элементов.
Практическая стратегия — группировать элементы и избегать избыточного числа независимых анимационных циклов.
animate(".particle", {
x: () => Math.random() * 500,
y: () => Math.random() * 500
}, {
duration: 1
});
Такой подход снижает количество отдельных анимационных экземпляров, сохраняя параллельность через один управляющий процесс.
В браузере все параллельные анимации проходят через единый pipeline отрисовки. Основные узкие места возникают при:
width, height,
top, left)Motion One минимизирует влияние этих факторов за счёт приоритета transform и opacity, которые не вызывают layout recalculation.
Параллельные анимации часто комбинируются с последовательными внутри одного сценария.
const sequence = async () => {
await animate(".title", { opacity: 1 }).finished;
await Promise.all([
animate(".left", { x: 200 }).finished,
animate(".right", { x: -200 }).finished
]);
animate(".footer", { opacity: 1 });
};
Так создаётся гибридная модель, где последовательные блоки содержат внутренние параллельные группы.