Композиция анимаций

Базовый принцип построения композиции

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

Ключевая особенность подхода заключается в том, что анимации рассматриваются как поток операций над DOM-элементами, а не как изолированные эффекты.

Основные механизмы композиции:

  • цепочки вызовов (chaining)
  • очередь анимаций (queue)
  • промисы (then/catch)
  • параллельные группы анимаций
  • последовательные сценарии выполнения
  • синхронизация через задержки и коллбэки

Цепочки вызовов и последовательное выполнение

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

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 }));
};

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