Управление состоянием

Базовая модель состояния анимации

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

С точки зрения внутренней модели можно выделить следующие состояния:

  • Инициализация — анимация создана, но ещё не запущена
  • Выполнение — активное изменение свойств элемента
  • Пауза — текущее значение зафиксировано, таймер остановлен
  • Завершение — анимация достигла конечного состояния
  • Принудительное завершение — состояние установлено мгновенно без проигрывания

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


Хранение состояния вне анимации

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

Типовой подход:

const animationState = {
  open: false,
  progress: 0,
  direction: 1,
  isAnimating: false
};

Такое разделение позволяет синхронизировать UI и анимацию без привязки к внутренним механизмам библиотеки.


Синхронизация состояния с событиями Velocity.js

Velocity.js предоставляет набор callback-функций, которые позволяют отслеживать жизненный цикл анимации:

  • begin — запуск
  • complete — завершение
  • progress — прогресс выполнения
  • interrupt — прерывание

Пример синхронизации:

Velocity(element, { opacity: 1 }, {
  duration: 400,

  begin() {
    animationState.isAnimating = true;
  },

  progress(elements, complete, remaining, start, tweenValue) {
    animationState.progress = complete;
  },

  complete() {
    animationState.isAnimating = false;
    animationState.progress = 1;
  }
});

Ключевой момент заключается в том, что состояние приложения обновляется строго через события, а не через опрос таймера.


Управление направлением анимации

Одним из важных аспектов является возможность изменения направления выполнения анимации. Velocity.js поддерживает методы reverse и повторный запуск с инвертированными параметрами.

Состояние направления удобно хранить явно:

animationState.direction = animationState.open ? -1 : 1;

Реализация переключения:

if (animationState.open) {
  Velocity(element, "reverse");
  animationState.open = false;
} else {
  Velocity(element, { height: "100px" }, { duration: 300 });
  animationState.open = true;
}

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


Пауза и возобновление как часть состояния

Velocity.js предоставляет методы pause и resume, которые позволяют временно замораживать выполнение анимации без сброса прогресса.

Управление этим состоянием требует отдельного флага:

animationState.paused = false;

Пример:

if (!animationState.paused) {
  Velocity(element, "pause");
  animationState.paused = true;
} else {
  Velocity(element, "resume");
  animationState.paused = false;
}

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


Принудительное завершение и сброс состояния

В ряде сценариев требуется мгновенно перевести анимацию в конечное состояние. Для этого используется метод finish.

Velocity(element, "finish");

После такого вызова состояние должно быть синхронизировано вручную:

animationState.isAnimating = false;
animationState.progress = 1;

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


Очереди анимаций и их влияние на состояние

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

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

С точки зрения состояния:

  • очередь представляет собой стек задач
  • каждая задача может модифицировать общее состояние элемента
  • завершение очереди означает переход в стабильное состояние

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


Состояние прогресса и его интерпретация

Progress является наиболее важным числовым показателем состояния анимации. Он нормализован в диапазоне от 0 до 1.

progress: 0.0 → 1.0

Использование прогресса вне Velocity.js позволяет:

  • синхронизировать UI элементы (например, индикаторы загрузки)
  • управлять зависимыми анимациями
  • строить сложные переходы между состояниями

Пример:

progress(elements, complete) {
  animationState.progress = complete;
  uiSlider.value = complete * 100;
}

Комбинированные состояния и сложные сценарии

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

const animationState = {
  phase: "opening",
  isAnimating: true,
  isPaused: false,
  progress: 0.35,
  direction: 1
};

Такая модель позволяет описывать:

  • раскрытие модальных окон
  • анимацию списков
  • переходы между страницами
  • взаимодействие нескольких элементов

Согласование состояния с DOM

DOM-узел часто используется как источник частичного состояния через атрибуты:

element.dataset.state = "open";

Однако более стабильный подход — хранить состояние вне DOM и использовать его только для отражения:

if (animationState.open) {
  element.classList.add("is-open");
} else {
  element.classList.remove("is-open");
}

Такое разделение исключает конфликт между CSS и JavaScript логикой.


Управление конкурирующими анимациями

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

Velocity.js по умолчанию прерывает текущую анимацию, если запускается новая. Это может привести к неконсистентному состоянию.

Решение заключается в явном контроле:

if (animationState.isAnimating) {
  Velocity(element, "stop", true);
}

После остановки необходимо пересчитать состояние:

animationState.isAnimating = false;
animationState.progress = 0;

Состояние в цепочках промисов

Velocity.js возвращает promise-подобный объект, что позволяет строить цепочки:

Velocity(element, { opacity: 0 })
  .then(() => Velocity(element, { translateY: 100 }))
  .then(() => {
    animationState.phase = "finished";
  });

Состояние в этом случае становится линейной последовательностью переходов, где каждый .then фиксирует новый этап.


Масштабируемая модель управления состоянием

При увеличении сложности интерфейса используется централизованная модель состояния:

  • отдельный store для анимаций
  • идентификаторы анимаций
  • реестр активных процессов

Пример структуры:

const animations = new Map();

animations.set("modal", {
  isAnimating: true,
  progress: 0.6,
  direction: 1
});

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