Reverse-анимации

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

Ключевой принцип: любая анимация представляет собой функцию перехода между двумя наборами свойств, и «reverse» фактически означает обмен начального и конечного состояния либо повторное воспроизведение ранее сохранённого состояния в обратном направлении.


Базовый паттерн инверсии свойств

Самый прямолинейный способ реализации обратной анимации заключается в ручной инверсии параметров.

Если исходная анимация:

Velocity(element, { opacity: 1, translateY: 0 }, { duration: 300 });

то обратная логика формируется явно:

Velocity(element, { opacity: 0, translateY: 20 }, { duration: 300 });

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

Более устойчивый вариант — считывание текущих вычисленных значений перед запуском обратного перехода:

const currentOpacity = parseFloat(getComputedStyle(element).opacity);

Velocity(element, { opacity: currentOpacity < 0.5 ? 1 : 0 }, { duration: 300 });

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


Сохранение состояния для обратного перехода

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

const state = {
  opacity: getComputedStyle(element).opacity,
  transform: element.style.transform
};

Анимация вперёд:

Velocity(element, {
  opacity: 0,
  translateY: -30
}, {
  duration: 400
});

Обратный переход:

Velocity(element, {
  opacity: state.opacity,
  translateY: 0
}, {
  duration: 400
});

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


Использование очередей Velocity.js

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

Прямая цепочка:

Velocity(element, { translateX: 100 }, { duration: 200 })
  .velocity({ translateY: 50 }, { duration: 200 })
  .velocity({ opacity: 0 }, { duration: 200 });

Обратная цепочка должна учитывать порядок выполнения:

Velocity(element, { opacity: 1 }, { duration: 200 })
  .velocity({ translateY: 0 }, { duration: 200 })
  .velocity({ translateX: 0 }, { duration: 200 });

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


Прерывание и разворот текущей анимации

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

Velocity(element, "stop");

Velocity(element, {
  opacity: 0,
  translateY: 30
}, {
  duration: 300
});

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


Обратная анимация через UI Pack

Velocity UI Pack добавляет набор готовых переходов и поддерживает концепцию reverse для определённых эффектов.

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

Velocity(element, "transition.slideUpIn", { duration: 300 });
Velocity(element, "reverse");

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

Особенность заключается в том, что reverse работает только для анимаций, созданных через UI Pack transitions, поскольку они сохраняют внутреннюю мета-информацию о начальных значениях.


Инверсия через повторное выполнение tween

Velocity.js позволяет повторно запускать анимацию с изменением направления логики без использования UI Pack.

Пример универсального tween-паттерна:

function animateIn(el) {
  Velocity(el, {
    opacity: 1,
    scale: 1
  }, { duration: 250 });
}

function animateOut(el) {
  Velocity(el, {
    opacity: 0,
    scale: 0.8
  }, { duration: 250 });
}

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


Reverse в условиях transform-графики

Сложности возникают при комбинировании нескольких трансформаций:

  • translateX
  • translateY
  • scale
  • rotate

Velocity.js хранит трансформации как строку transform, что делает частичную инверсию нетривиальной задачей.

Пример корректного хранения:

const initial = element.style.transform;

Velocity(element, {
  translateX: 200,
  rotateZ: 45
}, { duration: 400 });

Обратное восстановление:

element.style.transform = initial;

Такой подход часто надёжнее, чем попытка математически инвертировать каждый параметр отдельно.


Обратные анимации в последовательностях (stagger)

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

Прямая последовательность:

Velocity(elements, { opacity: 1, translateY: 0 }, {
  stagger: 80,
  duration: 200
});

Обратная:

Velocity(elements.reverse(), { opacity: 0, translateY: 20 }, {
  stagger: 80,
  duration: 200
});

Инверсия массива элементов играет ключевую роль, иначе визуальный эффект не будет зеркальным.


Управление состояниями через флаги

В интерфейсных компонентах reverse-анимация часто связана с логическим состоянием:

let isOpen = false;

function toggle() {
  if (isOpen) {
    Velocity(panel, { height: 0, opacity: 0 }, { duration: 300 });
  } else {
    Velocity(panel, { height: 300, opacity: 1 }, { duration: 300 });
  }
  isOpen = !isOpen;
}

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


Перезапуск анимации в обратном направлении

Velocity.js не всегда требует явного reverse-метода: достаточно прервать текущую анимацию и запустить новую с инвертированными параметрами.

Velocity(element, "stop", true);

Velocity(element, {
  translateY: 0,
  opacity: 1
}, {
  duration: 300
});

Флаг true очищает очередь анимаций, предотвращая наложение переходов и артефакты состояния.


Обратные анимации в сложных UI-паттернах

В интерфейсах с множеством вложенных компонентов reverse-анимации применяются в следующих сценариях:

  • раскрытие/сворачивание аккордеонов
  • модальные окна
  • боковые панели
  • динамические списки
  • карточные переходы

Типовой паттерн модального окна:

function openModal() {
  Velocity(modal, { opacity: 1, scale: 1 }, { duration: 250 });
}

function closeModal() {
  Velocity(modal, { opacity: 0, scale: 0.9 }, { duration: 200 });
}

Здесь reverse не требует симметрии параметров, поскольку закрытие использует упрощённую кривую движения для ускорения реакции интерфейса.


Особенности работы с easing при обратных переходах

Easing-функции не всегда симметричны при инверсии анимаций. Например:

  • ease-in при открытии
  • ease-out при закрытии
Velocity(el, { opacity: 1 }, {
  duration: 300,
  easing: "easeOutQuad"
});

Velocity(el, { opacity: 0 }, {
  duration: 200,
  easing: "easeInQuad"
});

Такой подход создаёт визуальную согласованность: появление воспринимается плавно, исчезновение — быстрее и более «решительно».


Комбинирование reverse с очередями и задержками

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

Velocity(element, { opacity: 0 }, { duration: 200 })
  .velocity({ translateY: 20 }, { duration: 200, delay: 50 })
  .velocity({ scale: 0.8 }, { duration: 200 });

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