# Анимация появления и исчезновения элемента
Vue предоставляет несколько способов для анимации эффектов переходов при добавлении элементов в DOM, обновлении или удалении из него. Эти возможности позволяют:
- автоматически применять CSS-классы для переходов и анимаций
- интегрировать сторонние библиотеки CSS-анимаций, такие как Animate.css (opens new window)
- использовать JavaScript для манипуляции DOM напрямую с помощью хуков
- интегрировать сторонние библиотеки JavaScript-анимаций
В этом разделе рассматриваются только анимации переходов появления и исчезновения элементов. В следующих — анимация списков и анимация переходов между состояниями.
# Анимация одиночного элемента/компонента
Vue предоставляет компонент-обёртку <transition>
, позволяющий добавить анимации появления/исчезновения для любого элемента или компонента для следующих случаев:
- Условная отрисовка (с использованием
v-if
) - Условное отображение (с использованием
v-show
) - Динамические компоненты
- Корневые элементы компонента
Посмотрим на небольшой пример в действии:
<div id="demo">
<button @click="show = !show">
Переключить
</button>
<transition name="fade">
<p v-if="show">привет</p>
</transition>
</div>
2
3
4
5
6
7
8
9
const Demo = {
data() {
return {
show: true
}
}
}
Vue.createApp(Demo).mount('#demo')
2
3
4
5
6
7
8
9
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.5s ease;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
2
3
4
5
6
7
8
9
See the Pen Простое использование компонента Transition by Vue (@Vue) on CodePen.
Когда элемент, обёрнутый в компонент <transition>
, будет добавляться или удаляться, произойдут следующие действия:
Vue автоматически определит, применяются ли к целевому элементу CSS-переходы или анимации. Если да, то в соответствующие моменты времени будут добавляться и удаляться классы CSS-переходов.
Если на компоненте есть JavaScript-хуки, то в соответствующие моменты времени они будут вызваны.
Если CSS-переходов или анимаций не обнаружено и нет JavaScript-хуков, DOM-операции для вставки и/или удаления будут вызваны незамедлительно или в следующем фрейме (примечание: это фрейм анимации браузера, отличный от концепции
nextTick
во Vue).
# Классы переходов
Существует шесть классов для анимаций переходов появления и исчезновения элементов.
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-
, если в <transition>
не указан атрибут name
. Например, для <transition name="super">
вместо класса v-enter-from
будет применяться super-enter-from
.
Классы v-enter-active
и v-leave-active
позволяют определить кривые плавности для переходов появления и исчезновения элемента. Пример использования рассмотрим ниже.
# CSS-переходы
Один из наиболее распространённых типов анимации — CSS-переходы:
<div id="demo">
<button @click="show = !show">
Переключить отображение
</button>
<transition name="slide-fade">
<p v-if="show">привет</p>
</transition>
</div>
2
3
4
5
6
7
8
9
const Demo = {
data() {
return {
show: true
}
}
}
Vue.createApp(Demo).mount('#demo')
2
3
4
5
6
7
8
9
/* Анимации появления и исчезновения могут иметь */
/* различные продолжительности и функции плавности. */
.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;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
See the Pen Различные переходы для появления и исчезновения by Vue (@Vue) on CodePen.
# CSS-анимации
CSS-анимации применяются таким же образом, что и CSS-переходы. Отличие лишь в том, что v-enter-from
удаляется не сразу после вставки элемента, а по событию animationend
.
Небольшой пример (браузерные CSS-префиксы опущены для краткости):
<div id="demo">
<button @click="show = !show">Переключить отображение</button>
<transition name="bounce">
<p v-if="show">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris facilisis
enim libero, at lacinia diam fermentum id. Pellentesque habitant morbi
tristique senectus et netus.
</p>
</transition>
</div>
2
3
4
5
6
7
8
9
10
const Demo = {
data() {
return {
show: true
}
}
}
Vue.createApp(Demo).mount('#demo')
2
3
4
5
6
7
8
9
.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);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
See the Pen Пример перехода с помощью CSS-анимации by Vue (@Vue) on CodePen.
# Пользовательские классы для переходов
Можно указать пользовательские классы для переходов с помощью следующих атрибутов:
enter-from-class
enter-active-class
enter-to-class
leave-from-class
leave-active-class
leave-to-class
Таким образом можно переопределить стандартные названия классов. Например, при необходимости комбинировать систему анимаций Vue с возможностями сторонних библиотек CSS-анимаций, таких как Animate.css (opens new window).
Пример переопределения стандартных имён классов:
<link
href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.0/animate.min.css"
rel="stylesheet"
type="text/css"
/>
<div id="demo">
<button @click="show = !show">
Переключить отображение
</button>
<transition
name="custom-classes-transition"
enter-active-class="animate__animated animate__tada"
leave-active-class="animate__animated animate__bounceOutRight"
>
<p v-if="show">привет</p>
</transition>
</div>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const Demo = {
data() {
return {
show: true
}
}
}
Vue.createApp(Demo).mount('#demo')
2
3
4
5
6
7
8
9
# Совместное использование переходов и анимаций
Чтобы определить завершение анимации, Vue прослушивает события transitionend
или animationend
, в зависимости от типа применяемых CSS-правил. Если используется только один подход из них — Vue автоматически определит правильный тип события.
Но в некоторых случах может потребоваться использовать оба подхода на одном элементе. Например CSS-анимация, запущенная Vue, может соседствовать с эффектом CSS-перехода при наведении на элемент. Для таких случаев потребуется явно указать тип события, на которое должен ориентироваться Vue. Для этого потребуется установить атрибут type
со значением animation
или transition
.
# Указание длительности перехода
Vue может автоматически определить завершение перехода для большинства случаев. По умолчанию Vue дожидается на корневом элементе первого события transitionend
или animationend
. Но такое поведение не всегда нужно — например, может потребоваться некая хореографическая последовательность переходов, в которой часть вложенных элементов могут иметь задержку перед началом перехода или другую продолжительность, отличную от корневого элемента.
В таких случаях можно явно задать продолжительность перехода (в миллисекундах) через входной параметр duration
на компоненте <transition>
:
<transition :duration="1000">...</transition>
Также можно устанавливать раздельные значения для анимаций появления и исчезновения:
<transition :duration="{ enter: 500, leave: 800 }">...</transition>
# JavaScript-хуки
На компоненте также можно указывать JavaScript-хуки:
<transition
@before-enter="beforeEnter"
@enter="enter"
@after-enter="afterEnter"
@enter-cancelled="enterCancelled"
@before-leave="beforeLeave"
@leave="leave"
@after-leave="afterLeave"
@leave-cancelled="leaveCancelled"
:css="false"
>
<!-- ... -->
</transition>
2
3
4
5
6
7
8
9
10
11
12
13
// ...
methods: {
// ---------
// ПОЯВЛЕНИЕ
// ---------
beforeEnter(el) {
// ...
},
// коллбэк done опционален
// при использовании в комбинации с CSS
enter(el, done) {
// ...
done()
},
afterEnter(el) {
// ...
},
enterCancelled(el) {
// ...
},
// ------------
// ИСЧЕЗНОВЕНИЕ
// ------------
beforeLeave(el) {
// ...
},
// коллбэк done опционален
// при использовании в комбинации с CSS
leave(el, done) {
// ...
done()
},
afterLeave(el) {
// ...
},
// хук leaveCancelled доступен только для v-show
leaveCancelled(el) {
// ...
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
Использовать хуки можно как самостоятельно, так и в сочетании с CSS-переходами и анимациями.
Если анимация реализуется только на JavaScript — обязательно вызывайте коллбэки done
в хуках enter
и leave
. Если этого не сделать, то хуки будут вызваны синхронно и переход сразу закончится. Также можно помочь Vue не тратить время на обнаружение CSS указав :css="false"
. Кроме бонуса в производительности, это предотвратит случайное влияние друг на друга CSS-правил и JS-перехода.
Пример JavaScript-перехода с использованием GreenSock (opens new window):
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.3.4/gsap.min.js"></script>
<div id="demo">
<button @click="show = !show">
Переключить отображение
</button>
<transition
@before-enter="beforeEnter"
@enter="enter"
@leave="leave"
:css="false"
>
<p v-if="show">
Демо
</p>
</transition>
</div>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const Demo = {
data() {
return {
show: false
}
},
methods: {
beforeEnter(el) {
gsap.set(el, {
scaleX: 0.8,
scaleY: 1.2
})
},
enter(el, done) {
gsap.to(el, {
duration: 1,
scaleX: 1.5,
scaleY: 0.7,
opacity: 1,
x: 150,
ease: 'elastic.inOut(2.5, 1)',
onComplete: done
})
},
leave(el, done) {
gsap.to(el, {
duration: 0.7,
scaleX: 1,
scaleY: 1,
x: 300,
ease: 'elastic.inOut(2.5, 1)'
})
gsap.to(el, {
duration: 0.2,
delay: 0.5,
opacity: 0,
onComplete: done
})
}
}
}
Vue.createApp(Demo).mount('#demo')
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
See the Pen Переход с использованием JavaScript-хуков by Vue (@Vue) on CodePen.
# Анимация первоначальной отрисовки
Если необходимо применить анимацию перехода при первоначальной отрисовке элемента, то её можно добавить с помощью атрибута appear
:
<transition appear>
<!-- ... -->
</transition>
2
3
# Анимация перехода между элементами
Анимация перехода между компонентами будет рассмотрена ниже, но создавать переходы можно также и между обычными элементами с помощью v-if
/v-else
. Один из наиболее распространённых случаев перехода между двумя элементами — между контейнером со списком элементов и сообщением об их отсутствии в нём:
<transition>
<table v-if="items.length > 0">
<!-- ... -->
</table>
<p v-else>Нет элементов</p>
</transition>
2
3
4
5
6
Фактически, переход возможен между любым количеством элементов, используя v-if
/v-else-if
/v-else
или с помощью привязки элемента к динамическому свойству. Например:
<transition>
<button v-if="docState === 'saved'" key="saved">
Изменить
</button>
<button v-else-if="docState === 'edited'" key="edited">
Сохранить
</button>
<button v-else-if="docState === 'editing'" key="editing">
Отмена
</button>
</transition>
2
3
4
5
6
7
8
9
10
11
Что также можно записать и следующим образом:
<transition>
<button :key="docState">
{{ buttonMessage }}
</button>
</transition>
2
3
4
5
// ...
computed: {
buttonMessage() {
switch (this.docState) {
case 'saved': return 'Изменить'
case 'edited': return 'Сохранить'
case 'editing': return 'Отмена'
}
}
}
2
3
4
5
6
7
8
9
10
# Режимы переходов
Однако сохраняется одна проблема. Попробуйте в примере ниже кликнуть на кнопку:
See the Pen Проблема с кнопками и режимы переходов by Vue (@Vue) on CodePen.
Во время анимации перехода от кнопки «on» к кнопке «off» одновременно будут видны обе кнопки: одна — исчезая, другая — появляясь. Это стандартное поведение <transition>
— анимации появления и исчезновения происходят одновременно.
Иногда такое поведение подходит, например когда обе кнопки позиционируются абсолютно друг над другом:
See the Pen Проблема с кнопками и режимы переходов - позиционирование by Vue (@Vue) on CodePen.
Но иногда это не вариант, когда имеем дело с более сложными случаями, где состояния появления и исчезновения должны быть скоординированы. Для таких ситуаций Vue предоставляет возможность указать режима перехода:
in-out
: Сначала появляется новый элемент и только после этого исчезает старый.out-in
: Сначала исчезает старый элемент и только после этого появляется новый.
Совет
Очень быстро поймёте, что out-in
— то, что требуется чаще всего 😃
Доработаем пример для кнопок «on»/«off» с использованием режима перехода out-in
:
<transition name="fade" mode="out-in">
<!-- ... кнопки ... -->
</transition>
2
3
See the Pen Проблема с кнопками и режимы переходов - решение by Vue (@Vue) on CodePen.
Добавив один атрибут, получилось исправить работу анимации перехода и избежать необходимости указывать стили специально для решения этой проблемы.
Их можно использовать и для координации более выразительных движений, к примеру переворачивания карты (см. пример ниже). На самом деле это два элемента, переходящие друг в друга, но так как начальное и конечное состояние одинаковы: по горизонтали до 0, то похоже на одно плавное движение. Это может пригодиться для создания реалистичных микро-взаимодействий в пользовательском интерфейсе:
See the Pen Режимы переходов: переворачивающиеся карты by Vue (@Vue) on CodePen.
# Анимация перехода между компонентами
Анимация перехода между компонентами ещё проще — не требуется даже атрибут key
. Всё, что потребуется — обернуть динамический компонент в <transition>
:
<div id="demo">
<input v-model="view" type="radio" value="v-a" id="a"><label for="a">A</label>
<input v-model="view" type="radio" value="v-b" id="b"><label for="b">B</label>
<transition name="component-fade" mode="out-in">
<component :is="view"></component>
</transition>
</div>
2
3
4
5
6
7
const Demo = {
data() {
return {
view: 'v-a'
}
},
components: {
'v-a': {
template: '<div>Компонент А</div>'
},
'v-b': {
template: '<div>Компонент B</div>'
}
}
}
Vue.createApp(Demo).mount('#demo')
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
.component-fade-enter-active,
.component-fade-leave-active {
transition: opacity 0.3s ease;
}
.component-fade-enter-from,
.component-fade-leave-to {
opacity: 0;
}
2
3
4
5
6
7
8
9
See the Pen Transitioning between components by Vue (@Vue) on CodePen.