В основе Velocity.js лежит система очередей анимации, построенная вокруг принципа последовательного исполнения операций над DOM-элементами. Каждая анимация, применённая к элементу, может быть помещена в очередь, которая определяет порядок её выполнения относительно других эффектов.
По умолчанию используется очередь "fx", аналогичная
jQuery, где анимации выполняются строго последовательно. Это поведение
позволяет строить цепочки трансформаций без необходимости вручную
управлять таймингами.
Ключевые особенности очередей:
queue: falseСинхронизация в Velocity.js начинается с понимания того, что каждая анимация возвращает управление немедленно, если не используется механизм ожидания завершения.
Последовательное выполнение на одном элементе:
Velocity(element, { opacity: 0.5 }, { duration: 300 });
Velocity(element, { translateX: 100 }, { duration: 300 });
Velocity(element, { rotateZ: 45 }, { duration: 300 });
Здесь каждая следующая анимация автоматически помещается в очередь
"fx" и стартует после завершения предыдущей.
Синхронное выполнение нескольких свойств в рамках одной операции:
Velocity(element, {
opacity: 0.5,
translateX: 100,
rotateZ: 45
}, {
duration: 300
});
В этом случае все свойства применяются одновременно, так как они принадлежат одному шагу анимации.
Основная сложность синхронизации возникает при работе с несколькими DOM-элементами. Каждый элемент имеет собственную очередь, что приводит к рассинхронизации при последовательных вызовах.
Типичный пример рассинхронизации:
Velocity(el1, { translateX: 100 }, { duration: 500 });
Velocity(el2, { translateX: 100 }, { duration: 200 });
Velocity(el3, { translateX: 100 }, { duration: 800 });
Несмотря на одинаковую цель, завершение произойдёт в разное время.
Для принудительной параллелизации используется отключение очереди:
Velocity(el1, { translateX: 100 }, { queue: false, duration: 500 });
Velocity(el2, { translateX: 100 }, { queue: false, duration: 500 });
Velocity(el3, { translateX: 100 }, { queue: false, duration: 500 });
Это обеспечивает старт одновременно, но не гарантирует координацию завершения.
Velocity.js поддерживает Promise-интерфейс, что позволяет строить надёжную синхронизацию между независимыми анимациями.
Базовая схема ожидания нескольких анимаций:
Promise.all([
Velocity(el1, { opacity: 1 }, { duration: 400 }),
Velocity(el2, { opacity: 1 }, { duration: 700 }),
Velocity(el3, { opacity: 1 }, { duration: 300 })
]).then(() => {
Velocity([el1, el2, el3], { scale: 1.2 }, { duration: 300 });
});
Promise здесь выступает как механизм барьерной синхронизации: последующая анимация стартует только после завершения всех предыдущих.
Более сложный случай — каскадная синхронизация:
Velocity(el1, { x: 100 }, { duration: 300 }).then(() =>
Velocity(el2, { x: 100 }, { duration: 300 })
).then(() =>
Velocity(el3, { x: 100 }, { duration: 300 })
);
Такой подход формирует строго линейную зависимость между анимациями разных элементов.
Velocity предоставляет callbacks, которые позволяют синхронизировать очереди без использования Promise.
Основные хуки:
begin — вызывается перед стартом анимацииcomplete — вызывается после завершенияprogress — вызывается во время выполненияСинхронизация через complete:
let counter = 0;
function checkDone() {
counter++;
if (counter === 3) {
Velocity(group, { opacity: 1 }, { duration: 400 });
}
}
Velocity(el1, { opacity: 0 }, { duration: 300, complete: checkDone });
Velocity(el2, { opacity: 0 }, { duration: 300, complete: checkDone });
Velocity(el3, { opacity: 0 }, { duration: 300, complete: checkDone });
Механизм счётчика позволяет реализовать барьер синхронизации без внешних библиотек.
Velocity позволяет задавать именованные очереди, что используется для группировки анимаций.
Velocity(el1, { x: 100 }, { queue: "group1", duration: 400 });
Velocity(el2, { x: 100 }, { queue: "group1", duration: 400 });
Velocity(el3, { x: 100 }, { queue: "group1", duration: 400 });
Однако важно учитывать, что именованная очередь действует на уровне каждого элемента, а не глобально. Поэтому синхронизация через очередь требует дополнительного управления стартом всех элементов.
Для контроля синхронности часто используется приостановка очереди и её принудительное завершение.
Velocity(el, "stop", true);
Velocity(el, { x: 0 }, { duration: 0 });
Остановка очереди позволяет сбросить состояние перед новой синхронной фазой.
Также используется finish, который мгновенно завершает
все текущие анимации:
Velocity(el, "finish");
Это важно при переходах между синхронными сценами интерфейса, где новая группа анимаций должна стартовать строго одновременно.
Сложные интерфейсы часто строятся как набор сцен, каждая из которых содержит собственный набор анимаций.
Барьерная синхронизация строится через Promise.all или
счётчики завершения.
function animateSceneA() {
return Promise.all([
Velocity(el1, { x: 50 }, { duration: 300 }),
Velocity(el2, { x: 50 }, { duration: 500 }),
Velocity(el3, { x: 50 }, { duration: 700 })
]);
}
function animateSceneB() {
return Promise.all([
Velocity(el1, { rotateZ: 90 }, { duration: 400 }),
Velocity(el2, { rotateZ: 90 }, { duration: 400 }),
Velocity(el3, { rotateZ: 90 }, { duration: 400 })
]);
}
animateSceneA().then(animateSceneB);
Каждая сцена выступает как синхронизированный блок, внутри которого допустима асинхронность элементов, но между сценами действует строгий порядок.
При построении сложных анимационных систем вводится концепция общего временного якоря. Он задаёт стартовую точку для всех элементов.
const start = performance.now();
Velocity(el1, { x: 100 }, { duration: 500 });
Velocity(el2, { x: 100 }, { duration: 500 });
Velocity(el3, { x: 100 }, { duration: 500 });
Сами анимации стартуют одновременно, но их внутренняя
продолжительность может корректироваться через динамические параметры
delay.
Velocity(el2, { x: 100 }, { delay: 100, duration: 500 });
Velocity(el3, { x: 100 }, { delay: 200, duration: 500 });
Такой подход используется для создания фазированной синхронизации, где элементы движутся как единая система, но с временным сдвигом.
При работе с зависимыми анимациями часто возникает необходимость связывать завершение одной очереди с запуском другой.
Velocity(el1, { x: 100 }, {
duration: 400,
complete: function() {
Velocity(el2, { y: 100 }, { duration: 400 });
}
});
В этом случае создаётся явная зависимость между очередями, где завершение первой автоматически инициирует вторую.
Для масштабирования такой логики используется композиция промисов и группировок, позволяющая строить граф зависимостей анимаций вместо линейной цепочки.
При работе с большими наборами элементов применяется групповая синхронизация через массивы.
Velocity([el1, el2, el3, el4], { opacity: 0 }, { duration: 300 });
Все элементы получают одинаковую анимацию и стартуют синхронно, но внутренние очереди каждого элемента остаются независимыми.
Для точного контроля синхронизации часто комбинируются:
queue: false)