Параллельные очереди

Очередь анимаций в Velocity.js строится вокруг механизма, который определяет порядок исполнения эффектов для каждого отдельного элемента. По умолчанию все анимации, запущенные на одном DOM-элементе, складываются в последовательную очередь и выполняются одна за другой. Это поведение наследует подход, знакомый по jQuery, но в Velocity.js он расширен возможностью управления параллельным исполнением, независимыми очередями и тонкой настройкой конкурирующих анимационных потоков.

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

При стандартном запуске анимации:

Velocity(element, { opacity: 1 }, { duration: 300 });
Velocity(element, { translateX: 200 }, { duration: 300 });

второй вызов начнёт выполняться только после завершения первого. Такое поведение формирует линейную очередь. Внутри Velocity.js это реализуется через внутренний стек задач, где каждый новый эффект добавляется в конец очереди.

Ключевой аспект этой модели — последовательность гарантируется только при совпадении очереди. Если анимации принадлежат одной очереди, они никогда не пересекаются по времени выполнения.

Разделение потоков через именованные очереди

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

Velocity(element, { opacity: 0.5 }, { queue: "fx" });
Velocity(element, { translateX: 200 }, { queue: "transform" });

В этом случае обе анимации стартуют независимо, поскольку принадлежат разным очередям. Каждая очередь ведёт собственную последовательность команд, не блокируя другую.

Такая модель позволяет организовать параллельные изменения разных CSS-аспектов:

  • fx может отвечать за прозрачность и масштаб
  • transform может отвечать за перемещения
  • rotation может управлять вращением

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

Параллельное выполнение без очереди

Самый прямой способ параллелизма — отключение очереди:

Velocity(element, { opacity: 0.2 }, { queue: false });
Velocity(element, { translateX: 300 }, { queue: false });

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

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

Поведение при множественных элементах

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

Velocity([el1, el2, el3], { opacity: 0 }, { duration: 400 });

Если затем добавить следующую анимацию:

Velocity([el1, el2, el3], { translateY: 100 }, { duration: 400 });

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

Конкуренция очередей и приоритеты

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

Если одновременно выполняются:

Velocity(element, { left: "100px" }, { queue: "a" });
Velocity(element, { left: "200px" }, { queue: "b" });

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

Связка параллельных и последовательных очередей

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

Velocity(element, { opacity: 1 }, { queue: "fx" });
Velocity(element, { translateX: 100 }, { queue: "transform" });
Velocity(element, { rotateZ: 45 }, { queue: "transform" });
Velocity(element, { scale: 1.2 }, { queue: "fx" });

Здесь формируются две независимые линии:

  • fx — управляет визуальной выразительностью
  • transform — управляет геометрией

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

Очистка и управление очередями

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

Velocity(element, "stop", "transform");

или остановить все очереди сразу:

Velocity(element, "stop", true);

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

Завершение и синхронизация параллельных потоков

При использовании параллельных очередей возникает задача координации завершения. Velocity.js предоставляет Promise-подобное завершение каждой анимации:

Velocity(element, { opacity: 0 }).then(() => {
    // выполнение после завершения конкретной анимации
});

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

Внутренний механизм обработки

Внутри Velocity.js каждая очередь представляет собой структуру задач, связанных с конкретным DOM-элементом. При добавлении анимации происходит:

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

При использовании queue: false этап регистрации в очереди пропускается, и анимация становится автономной единицей исполнения.

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

Практическое значение параллельных очередей

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

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