Transition
Вакансии vuejobs.com
Vue предлагает два встроенных компонента, которые помогают работать с переходами и анимацией в ответ на изменение состояния:
<Transition>
для применения анимации элемента или компонента при входе и выходе из DOM. Об этом рассказывается на этой странице.<TransitionGroup>
для применения анимации при вставке, удалении или перемещении элемента или компонента в спискеv-for
. Этому посвящена следующая глава.
Помимо этих двух компонентов, в Vue можно применять анимацию и с помощью других приемов, таких как переключение CSS-классов или анимация, управляемая состоянием, с помощью привязки стилей. Эти дополнительные приемы рассматриваются в главе Техники анимации.
Компонент <Transition>
<Transition>
является встроенным компонентом: это означает, что он доступен в шаблоне любого компонента без необходимости его регистрации. Он может использоваться для применения анимации входа и выхода к элементам или компонентам, передаваемым ему через слот по умолчанию. Вход или выход может быть вызван одним из следующих действий:
- Условное отображение через
v-if
- Условное отображение с помощью
v-show
- Переключение динамических компонентов с помощью специального элемента
<component>
Это пример наиболее простого использования:
template
<button @click="show = !show">Toggle</button>
<Transition>
<p v-if="show">привет</p>
</Transition>
css
/* мы объясним, что делают эти классы дальше! */
.v-enter-active,
.v-leave-active {
transition: opacity 0.5s ease;
}
.v-enter-from,
.v-leave-to {
opacity: 0;
}
привет
Совет
<Transition>
поддерживает только один элемент или компонент в качестве содержимого своего слота. Если содержимое является компонентом, то компонент также должен иметь только один единственный корневой элемент.
Когда элемент в компоненте <Transition>
вставляется или удаляется, происходит следующее:
Vue автоматически определяет, есть ли в целевом элементе CSS-переходы или анимация. Если это так, то в соответствующие моменты времени будут добавлены/удалены несколько классов CSS-переходов.
Если есть слушатели для JavaScript хуков, то эти хуки будут вызываться в соответствующие моменты времени.
Если переходы/анимации CSS не обнаружены и хуки JavaScript не предоставлены, операции DOM по вставке и/или удалению будут выполняться в следующем кадре анимации браузера.
Переходы на основе CSS
Классы перехода
Для переходов входа/выхода применяются шесть классов.
v-enter-from
: Начало анимации появления элемента. Добавляется перед вставкой элемента, удаляется через один кадр после вставки элемента.v-enter-active
: Активное состояние появления элемента. Этот класс остаётся на элементе в течение всей анимации появления. Добавляется перед вставкой элемента, удаляется по завершении перехода/анимации. Этот класс можно использовать для установки длительности, задержки или функции плавности (easing curve) анимации появления.v-enter-to
: Завершение анимации появления элемента. Добавляется в следующем фрейме после вставки элемента (тогда же удаляетсяv-enter-from
), а удаляется после завершения перехода или анимации.v-leave-from
: Начало анимации исчезновения элемента. Добавляется сразу после вызова анимации исчезновения, а удаляется в следующем фрейме после этого.v-leave-active
: Активное состояние анимации исчезновения. Этот класс остаётся на элементе в течение всей анимации исчезновения. Он добавляется при вызове анимации исчезновения, а удаляется после завершения перехода или анимации. Этот класс можно использовать для установки длительности, задержки или функции плавности (easing curve) анимации исчезновения.v-leave-to
: Завершение анимации исчезновения элемента. Добавляется в следующем фрейме после вызова анимации исчезновения (тогда же удаляетсяv-leave-from
), а удаляется после завершения перехода или анимации.
Классы v-enter-active
и v-leave-active
позволяют устанавливать кривые плавности для переходов появления и исчезновения элемента. Пример использования рассмотрим ниже.
Именованные переходы
Переход может быть назван с помощью свойства name
:
template
<Transition name="fade">
...
</Transition>
Для именованного перехода его классы переходов будут иметь префикс с названием, а не v
. Например, применяемый класс для приведенного выше перехода будет fade-enter-active
, а не v-enter-active
. CSS для перехода fade должен выглядеть следующим образом:
css
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.5s ease;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
CSS-переходы
<Transition>
чаще всего используется в сочетании с собственными CSS-переходами, как показано в базовом примере выше. CSS-свойство transition
- это сокращение, позволяющее указать множество аспектов перехода, включая свойства, которые должны быть анимированы, длительность перехода и функции плавности.
Приведем более сложный пример, в котором переходы осуществляются по нескольким свойствам, с различными длительностями и функциями плавностями для входа и выхода:
template
<Transition name="slide-fade">
<p v-if="show">привет</p>
</Transition>
css
/*
Анимации появления и исчезновения могут иметь
различные продолжительности и функции плавности.
*/
.slide-fade-enter-active {
transition: all 0.3s ease-out;
}
.slide-fade-leave-active {
transition: all 0.8s cubic-bezier(1, 0.5, 0.8, 1);
}
.slide-fade-enter-from,
.slide-fade-leave-to {
transform: translateX(20px);
opacity: 0;
}
привет
CSS-анимации
Собственные CSS-анимации применяются таким же образом, что и CSS-переходы. Они отличаются лишь тем, что *-enter-from
удаляется не сразу после вставки элемента, а при наступлении события animationend
.
Для большинства CSS-анимаций мы можем просто объявить их в классах *-enter-active
и *-leave-active
. Вот пример:
template
<Transition name="bounce">
<p v-if="show" style="text-align: center;">
Привет, вот какой-то задорный текст!
</p>
</Transition>
css
.bounce-enter-active {
animation: bounce-in 0.5s;
}
.bounce-leave-active {
animation: bounce-in 0.5s reverse;
}
@keyframes bounce-in {
0% {
transform: scale(0);
}
50% {
transform: scale(1.25);
}
100% {
transform: scale(1);
}
}
Привет, вот какой-то задорный текст!
Пользовательские классы переходов
Вы также можете указать пользовательские классы переходов, передав в <Transition>
следующие входные параметры:
enter-from-class
enter-active-class
enter-to-class
leave-from-class
leave-active-class
leave-to-class
Они будут заменять обычные имена классов. Это особенно полезно, если вы хотите объединить систему переходов Vue с существующей библиотекой анимации CSS, например Animate.css:
template
<!-- при условии, что Animate.css добавлен на странице -->
<Transition
name="custom-classes"
enter-active-class="animate__animated animate__tada"
leave-active-class="animate__animated animate__bounceOutRight"
>
<p v-if="show">привет</p>
</Transition>
Совместное использование переходов и анимаций
Для определения завершения анимации Vue использует прослушиватели событий с типом transitionend
или animationend
, в зависимости от типа применяемых CSS-правил. Если используется только один подход из них, Vue определит правильный тип автоматически.
Однако, в некоторых случах может потребоваться использовать оба подхода на одном элементе. Например CSS-анимация, запущенная Vue, может соседствовать с эффектом CSS-перехода при наведении на элемент. В таких случаях потребуется явное указание типа события, на которое должен ориентироваться Vue. Для этого нужно использовать атрибут type
со значением animation
или transition
:
template
<Transition type="animation">...</Transition>
Вложенные переходы и явные длительности переходов
Хотя классы перехода применяются только к непосредственному дочернему элементу <Transition>
, мы можем переходить к вложенным элементам с помощью вложенных CSS-селекторов:
template
<Transition name="nested">
<div v-if="show" class="outer">
<div class="inner">
Привет
</div>
</div>
</Transition>
css
/* правила, нацеленные на вложенные элементы */
.nested-enter-active .inner,
.nested-leave-active .inner {
transition: all 0.3s ease-in-out;
}
.nested-enter-from .inner,
.nested-leave-to .inner {
transform: translateX(30px);
opacity: 0;
}
/* ... другие необходимые CSS не указаны */
Мы можем даже добавить задержку перехода во вложенный элемент при входе, что создаст ступенчатую последовательность анимации входа:
css
/* задержка появления вложенного элемента для эффекта */
.nested-enter-active .inner {
transition-delay: 0.25s;
}
Однако при этом возникает небольшая проблема. По умолчанию компонент <Transition>
пытается автоматически определить момент завершения перехода, слушая первое событие transitionend
или animationend
на корневом элементе перехода. При вложенном переходе желательным поведением будет ожидание завершения переходов всех внутренних элементов.
В таких случаях можно указать явную длительность перехода (в миллисекундах) с помощью параметра duration
компонента <transition>
. Общая длительность должна соответствовать длительности задержки плюс длительности перехода внутреннего элемента:
template
<Transition :duration="550">...</Transition>
Привет
При необходимости можно также задать отдельные значения для продолжительности входа и выхода с помощью объекта:
template
<Transition :duration="{ enter: 500, leave: 800 }">...</Transition>
Соображения по производительности
Вы можете заметить, что в приведенных выше анимациях в основном используются такие свойства, как transform
и opacity
. Эти свойства эффективны для анимации, поскольку:
Они не влияют на макет документа во время анимации, поэтому не вызывают дорогостоящих расчетов шаблона CSS на каждом кадре анимации.
Большинство современных браузеров могут использовать аппаратное ускорение GPU при
transform
.
Для сравнения, такие свойства, как height
или margin
вызывают шаблон CSS, поэтому их анимировать гораздо дороже, и использовать их следует с осторожностью. Мы можем обратиться к таким ресурсам, как CSS-Triggers, чтобы узнать, какие свойства будут вызывать компоновку, если мы их анимируем.
JavaScript хуки
Вы можете подключиться к процессу перехода с помощью JavaScript, прослушивая события на компоненте <Transition>
:
html
<Transition
@before-enter="onBeforeEnter"
@enter="onEnter"
@after-enter="onAfterEnter"
@enter-cancelled="onEnterCancelled"
@before-leave="onBeforeLeave"
@leave="onLeave"
@after-leave="onAfterLeave"
@leave-cancelled="onLeaveCancelled"
>
<!-- ... -->
</Transition>
js
// вызывается перед вставкой элемента в DOM.
// используется для установки состояния "enter-from" элемента
function onBeforeEnter(el) {}
// вызывается через один кадр после вставки элемента.
// используйте его для запуска анимации входа.
function onEnter(el, done) {
// вызов обратного вызова done для индикации окончания перехода
// необязателен, если используется в сочетании с CSS
done()
}
// вызывается по завершении перехода enter.
function onAfterEnter(el) {}
function onEnterCancelled(el) {}
// вызывается перед хуком leave.
// В большинстве случаев следует использовать только хук leave
function onBeforeLeave(el) {}
// вызывается, когда начинается переход к leave.
// используйте его для запуска анимации ухода.
function onLeave(el, done) {
// вызов обратного вызова done для индикации окончания перехода
// необязательно, если используется в сочетании с CSS
done()
}
// вызывается, когда переход к leave завершен
// элемент был удален из DOM.
function onAfterLeave(el) {}
// доступно только при использовании переходов v-show
function onLeaveCancelled(el) {}
Эти хуки могут использоваться как в сочетании с CSS-переходами/анимацией, так и самостоятельно.
При использовании переходов, основанных только на JavaScript, обычно рекомендуется добавить параметр :css="false"
. Это явно указывает Vue на необходимость пропускать автоматическое определение CSS-переходов. Помимо того, что это несколько повышает производительность, это также предотвращает случайное вмешательство CSS-правил в переход:
template
<Transition
...
:css="false"
>
...
</Transition>
При использовании :css="false"
мы также полностью отвечаем за контроль окончания перехода. В этом случае обратные вызовы done
необходимы для хуков @enter
и @leave
. В противном случае хуки будут вызываться синхронно и переход завершится немедленно.
Вот демонстрация использования библиотеки GreenSock для выполнения анимации. Конечно, вы можете использовать любую другую библиотеку анимации, например Anime.js или Motion One.
Переиспользуемые переходы
Переходы можно повторно использовать через систему компонентов Vue. Чтобы создать повторно используемый переход, мы можем создать компонент, который обертывает компонент <Transition>
и передает содержимое слота:
vue
<!-- MyTransition.vue -->
<script>
// Логика хуков JavaScript...
</script>
<template>
<!-- обернуть встроенный компонент Transition -->
<Transition
name="my-transition"
@enter="onEnter"
@leave="onLeave">
<slot></slot> <!-- передать содержимое слота -->
</Transition>
</template>
<style>
/*
Необходимый CSS...
Примечание: избегайте использования здесь <style scoped>,
так как это не относится к содержимому слота.
*/
</style>
Теперь MyTransition
можно импортировать и использовать так же, как встроенную версию:
template
<MyTransition>
<div v-if="show">привет</div>
</MyTransition>
Переход при появлении
Если вы также хотите применить переход при начальном рендеринге узла, вы можете добавить атрибут appear
:
template
<Transition appear>
...
</Transition>
Переход между элементами
Помимо переключения элемента с помощью v-if
/ v-show
, мы также можем осуществлять переход между двумя элементами с помощью v-if
/ v-else
/ v-else-if
, при условии, что в каждый момент времени отображается только один элемент:
template
<Transition>
<button v-if="docState === 'saved'">Редактировать</button>
<button v-else-if="docState === 'edited'">Сохранить</button>
<button v-else-if="docState === 'editing'">Отменить</button>
</Transition>
Щелкните мышью для переключения между состояниями:
Режимы перехода
В предыдущем примере элементы входа и выхода анимируются одновременно, и нам пришлось сделать position: absolute
чтобы избежать проблем с компоновкой, когда оба элемента присутствуют в DOM.
Однако в некоторых случаях это невозможно или просто нежелательно. Мы можем захотеть, чтобы сначала анимировался выходящий элемент, а входящий элемент вставлялся только после завершения анимации выхода. Организовать такую анимацию вручную было бы очень сложно - к счастью, мы можем включить это поведение, передав <Transition>
свойство mode
:
template
<Transition mode="out-in">
...
</Transition>
Вот предыдущая демонстрация с mode="out-in"
:
Щелкните мышью для переключения между состояниями:
<Transition>
также поддерживает режим mode="in-out"
, хотя он используется гораздо реже.
Переход между компонентами
<Transition>
также может использоваться вокруг динамических компонентов:
template
<Transition name="fade" mode="out-in">
<component :is="activeComponent"></component>
</Transition>
Компонент A
Динамические переходы
Входные параметры <Transition>
, например name
также может быть динамическим! Это позволяет нам динамически применять различные переходы в зависимости от изменения состояния:
template
<Transition :name="transitionName">
<!-- ... -->
</Transition>
Это может быть полезно, если вы определили CSS-переходы / анимацию, используя соглашения Vue о классах переходов, и хотите переключаться между ними.
Также можно применять различное поведение в переходах JavaScript-хуков в зависимости от текущего состояния компонента. Наконец, окончательный способ создания динамических переходов - это многократно используемые компоненты переходов, которые принимают входные параметры для изменения характера используемого перехода (переходов). Это может показаться банальным, но на самом деле единственное ограничение - это ваше воображение.
Связанное