Очистка очередей

Очереди анимаций и их управление

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

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

Типичный сценарий:

$el.velocity({ opacity: 0.5 }, { duration: 300 });
$el.velocity({ translateX: 100 }, { duration: 300 });
$el.velocity({ rotateZ: 45 }, { duration: 300 });

Каждый следующий вызов добавляется в очередь и запускается только после завершения предыдущего.


Механизм переполнения очереди

При частых событиях интерфейса — hover, scroll, mousemove — очередь может начать накапливать десятки анимаций. Это приводит к эффекту «запаздывания интерфейса», когда визуальное состояние элемента уже не соответствует текущему состоянию приложения.

Классический пример — наведение курсора:

$el.on("mouseenter", () => {
  $el.velocity({ scale: 1.2 }, { duration: 200 });
});

$el.on("mouseleave", () => {
  $el.velocity({ scale: 1.0 }, { duration: 200 });
});

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


Остановка текущих и будущих анимаций

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

$el.velocity("stop");

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

Расширенная форма:

$el.velocity("stop", true, true);

Параметры:

  • первый true — очистка очереди
  • второй true — завершение текущей анимации мгновенно (без плавного перехода к конечному состоянию)

Такой вызов полностью «замораживает» элемент в текущем состоянии и удаляет все запланированные шаги.


Полная очистка очереди

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

$el.velocity("stop", true);

В этом режиме:

  • текущая анимация прерывается
  • все последующие анимации удаляются
  • элемент остаётся в промежуточном состоянии, если не указано завершение

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


Принудительное завершение очереди

В Velocity UI Pack существует метод finish, который отличается от stop тем, что не просто прерывает анимации, а доводит их до конечного состояния.

$el.velocity("finish");

Поведение:

  • текущая анимация мгновенно завершается
  • все промежуточные значения применяются сразу
  • очередь очищается
  • последующие эффекты удаляются

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


Различие между stop и finish

Разница между двумя механизмами принципиальна:

  • stop(true, true) — жёсткое прерывание, возможен «обрыв» анимации
  • finish — доведение до конечного состояния без визуального несоответствия

Пример различия:

$el.velocity({ width: "500px" }, { duration: 1000 });

Если в середине анимации вызвать:

$el.velocity("stop", true, true);

элемент останется на промежуточной ширине.

Если вызвать:

$el.velocity("finish");

ширина станет 500px мгновенно.


Очистка очередей при переинициализации интерфейса

При повторной инициализации компонентов (например, при SPA-навигации) часто возникает ситуация, когда старые анимации продолжают выполняться на уже неактуальных DOM-узлах.

Типичный подход:

function destroyComponent($el) {
  $el.velocity("stop", true, true);
  $el.off();
}

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


Очереди и параллельные анимации

Velocity позволяет запускать анимации без добавления в очередь через параметр queue: false.

$el.velocity(
  { opacity: 0.5 },
  { duration: 300, queue: false }
);

В этом случае анимация выполняется параллельно с другими эффектами и не влияет на основной стек очереди.

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


Управление очередями через именованные очереди

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

$el.velocity({ translateX: 100 }, { queue: "fx" });
$el.velocity({ opacity: 0 }, { queue: "fx" });

Очистка конкретной очереди:

$el.velocity("stop", "fx");

Такой подход используется для сложных компонентов, где отдельно управляются:

  • движение
  • прозрачность
  • масштабирование

Типичные проблемы при отсутствии очистки очередей

При игнорировании управления очередями возникают следующие эффекты:

  • накопление задержек при частых событиях
  • «дрожание» интерфейса после прекращения взаимодействия
  • выполнение устаревших анимаций после смены состояния
  • утечки логики в уничтоженных компонентах

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


Сброс состояния элемента перед новой анимацией

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

$el.velocity("stop", true, true);

$el.velocity({
  translateX: 200,
  opacity: 1
}, {
  duration: 400
});

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


Очереди внутри циклических эффектов

При создании повторяющихся анимаций (pulse, bounce, loop) важно учитывать, что остановка должна полностью разрушать цикл, иначе оставшиеся итерации будут продолжать добавляться в очередь.

function pulse($el) {
  $el.velocity({ scale: 1.1 }, { duration: 200 })
     .velocity({ scale: 1.0 }, { duration: 200, complete: () => pulse($el) });
}

Остановка:

$el.velocity("stop", true, true);

Без полной очистки цикл может продолжить выполнение даже после попытки остановки.


Синхронизация очередей с логикой состояния

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

Решение заключается в обязательной очистке перед любым критическим переходом состояния:

  • скрытие компонента
  • удаление из DOM
  • переключение маршрута
  • деактивация слоя интерфейса
function hide($el) {
  $el.velocity("stop", true, true)
     .velocity({ opacity: 0 }, { duration: 200 });
}

Особенности поведения при быстром повторном вызове

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

Поэтому базовым паттерном становится:

  • остановка очереди
  • очистка
  • запуск новой анимации

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