Композиция анимаций в Velocity.js опирается на последовательное и
параллельное выполнение анимационных вызовов с управляемым состоянием
очереди и возможностью синхронизации через промисы. Каждый вызов
Velocity может быть частью цепочки, где результат
предыдущей анимации становится триггером следующей.
Ключевая особенность подхода заключается в том, что анимации рассматриваются как поток операций над DOM-элементами, а не как изолированные эффекты.
Основные механизмы композиции:
Одним из базовых способов композиции является последовательное применение анимаций к одному или нескольким элементам через цепочки вызовов.
Velocity(element, { opacity: 1 }, 300)
.then(() => Velocity(element, { translateY: 100 }, 400))
.then(() => Velocity(element, { rotateZ: 180 }, 500));
Каждый следующий шаг запускается только после завершения предыдущего. Это формирует линейную структуру анимационного сценария.
Особенность цепочек Velocity.js заключается в том, что
then возвращает промисоподобный объект, позволяющий строить
сложные последовательности без ручного управления таймингами.
Velocity.js использует внутреннюю очередь для каждого элемента. Все анимации, назначенные на один и тот же DOM-узел, по умолчанию выполняются последовательно.
Velocity(element, { width: 300 }, { duration: 300 });
Velocity(element, { height: 300 }, { duration: 300 });
Velocity(element, { opacity: 0.5 }, { duration: 300 });
Такой подход позволяет формировать сценарии без явного связывания вызовов.
Для управления поведением очереди используется параметр
queue.
Velocity(element, { x: 100 }, { duration: 400, queue: false });
Отключение очереди переводит анимацию в параллельный режим относительно других анимаций этого элемента.
Параллельное выполнение достигается либо отключением очереди, либо запуском нескольких независимых вызовов.
Velocity(element, { scale: 1.2 }, { duration: 500, queue: false });
Velocity(element, { opacity: 0.3 }, { duration: 500, queue: false });
Velocity(element, { rotateZ: 45 }, { duration: 500, queue: false });
В этом случае все трансформации выполняются одновременно, формируя единый визуальный эффект.
Для более сложной параллельной композиции часто используется
Promise.all:
Promise.all([
Velocity(el1, { x: 100 }, { duration: 400 }),
Velocity(el2, { y: 100 }, { duration: 400 }),
Velocity(el3, { opacity: 0 }, { duration: 400 })
]).then(() => {
Velocity(wrapper, { scale: 0.9 }, { duration: 300 });
});
При усложнении логики анимации линейные цепочки перестают быть удобными. Используется сценарный подход, где анимации разбиваются на логические блоки.
const fadeIn = (el) =>
Velocity(el, { opacity: 1 }, { duration: 300 });
const move = (el) =>
Velocity(el, { translateX: 200 }, { duration: 500 });
const rotate = (el) =>
Velocity(el, { rotateZ: 90 }, { duration: 400 });
fadeIn(element)
.then(() => move(element))
.then(() => rotate(element));
Такое разбиение позволяет формировать переиспользуемые анимационные примитивы, которые легко комбинируются.
Velocity.js поддерживает работу с коллекциями DOM-элементов, что позволяет строить композиции на уровне групп.
const items = document.querySelectorAll('.item');
Velocity(items, { opacity: 1 }, { duration: 300 });
При этом поведение можно модифицировать через stagger,
создавая эффект каскадного появления.
Velocity(items, { translateY: 0 }, {
duration: 400,
stagger: 100
});
Stagger-композиция превращает одновременный запуск в последовательный поток с фиксированным интервалом между элементами.
Stagger может использоваться не только для простых последовательностей, но и для создания волнообразных анимаций.
Velocity(items, { scale: 1 }, {
duration: 600,
stagger: (index) => index * 80
});
Функциональный stagger позволяет формировать нелинейные
паттерны запуска, что важно для сложных визуальных систем.
При построении многоуровневых сценариев важно управление моментом завершения каждой фазы.
Velocity(element, { opacity: 1 }, { duration: 300 })
.then(() => {
return Velocity(element, { translateY: 150 }, { duration: 500 });
})
.then(() => {
return Velocity(element, { rotateZ: 360 }, { duration: 700 });
});
Каждый уровень композиции становится атомарной фазой, которую можно изолировать и переиспользовать.
Сложные интерфейсы требуют синхронного запуска нескольких независимых сценариев.
const a = Velocity(elA, { x: 100 }, { duration: 400 });
const b = Velocity(elB, { y: 100 }, { duration: 400 });
const c = Velocity(elC, { opacity: 0 }, { duration: 400 });
Promise.all([a, b, c]).then(() => {
Velocity(container, { scale: 0.8 }, { duration: 300 });
});
Такой подход обеспечивает точку синхронизации, после которой продолжается единый поток анимации.
Задержки позволяют внедрять временные разрывы в сценарий без изменения структуры цепочки.
Velocity(element, { opacity: 1 }, { duration: 300 })
.then(() => Velocity(element, { translateX: 200 }, { duration: 400, delay: 200 }))
.then(() => Velocity(element, { scale: 1.5 }, { duration: 300 }));
Задержка становится инструментом пространственного и временного разделения фаз анимации.
Композиция может включать обратное выполнение анимаций, формируя циклические сценарии.
const sequence = (el) => {
return Velocity(el, { x: 200 }, { duration: 400 })
.then(() => Velocity(el, { x: 0 }, { duration: 400 }));
};
Такой подход используется для создания возвратных интерфейсных переходов и повторяющихся эффектов.
Velocity.js позволяет объединять несколько свойств в одном вызове, формируя атомарные композиции.
Velocity(element, {
translateX: 150,
translateY: 50,
rotateZ: 45,
opacity: 0.8
}, {
duration: 600
});
Объединение свойств уменьшает количество фаз и обеспечивает синхронность трансформаций внутри одного шага.
Для расширения сценариев применяются промежуточные функции, управляющие логикой переходов.
const step = (el, props, duration) =>
Velocity(el, props, { duration });
step(element, { opacity: 1 }, 300)
.then(() => step(element, { translateY: 100 }, 400))
.then(() => step(element, { scale: 1.2 }, 300));
Абстракция на уровне функций позволяет строить декларативные анимационные схемы.
Анимации могут прерываться, что влияет на дальнейшее выполнение цепочки.
Velocity(element, "stop", true);
Прерывание текущей анимации влияет на состояние очереди и может изменить дальнейший сценарий, особенно при сложных композициях с несколькими слоями анимации.
Состояние элемента может использоваться как основа для последующих анимаций, создавая зависимые цепочки.
Velocity(element, { opacity: 1 }, { duration: 300 })
.then(() => Velocity(element, { scale: 1.1 }, { duration: 300 }))
.then(() => Velocity(element, { scale: 1 }, { duration: 300 }));
Такая структура формирует эффект «дыхания» интерфейсного элемента, где каждая фаза опирается на предыдущую.
Сложные интерфейсные сценарии формируются через объединение нескольких уровней:
const sceneIn = () => {
return Velocity(items, { opacity: 1 }, { stagger: 80, duration: 300 })
.then(() => Velocity(container, { scale: 1 }, { duration: 400 }));
};
const sceneOut = () => {
return Velocity(items, { opacity: 0 }, { stagger: 50, duration: 300 })
.then(() => Velocity(container, { scale: 0.9 }, { duration: 400 }));
};
Такая структура позволяет строить полноценные анимационные сцены, где каждая фаза управляется независимо, но участвует в общей композиции.