transition

transition组件可以给任何元素或组件添加进入/离开的过渡,使用情形:

  • 条件渲染(v-if)
  • 条件展示(v-show)
  • 动态组件
  • 组件根节点

类名

默认类名

在进入/离开的过渡中,会有6个class切换:

  1. v-enter-from:定义进入过渡的开始状态。在元素被插入之前生效,在元素被插入之后的下一帧移除。
  2. v-enter-active:定义进入过渡生效时的状态。在整个进入过渡的阶段中应用,在元素被插入之前生效,在过渡/动画完成之后移除。常用来定义进入过渡的过程时间,延迟和曲线函数。
  3. v-enter-to:定义进入过渡的结束状态。在元素被插入之后下一帧生效 (与此同时v-enter-from被移除),在过渡/动画完成之后移除。
  4. v-leave-from:定义离开过渡的开始状态。在离开过渡被触发时立刻生效,下一帧被移除。
  5. v-leave-active:定义离开过渡生效时的状态。在整个离开过渡的阶段中应用,在离开过渡被触发时立刻生效,在过渡/动画完成之后移除。常用来定义离开过渡的过程时间,延迟和曲线函数。
  6. v-leave-to:离开过渡的结束状态。在离开过渡被触发之后下一帧生效 (与此同时v-leave-from被移除),在过渡/动画完成之后移除。

为transition组件添加name属性,并在css中把上述类名的v替换成name属性值,再写入对应样式。

1
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
44
45
46
47
48
49
50
51
52
53
<template>
<div class="container">
<button @click="flag = !flag">switch</button>
<transition name="fade">
<div v-if="flag" class="box"></div>
</transition>
</div>
</template>

<script setup lang="ts">
import { ref } from "vue"

const flag = ref<boolean>(true)
</script>

<style lang="scss">
.box {
width: 200px;
height: 200px;
background-color: pink;
}
// 进入前的状态
.fade-enter-from {
width: 0;
height: 0;
transform: rotate(360deg);
}
// 进入时的过渡
.fade-enter-active {
transition: all 1s ease;
}
// 进入结束的状态
.fade-enter-to {
// 最好和本身的样式保持一致
width: 200px;
height: 200px;
}
// 退出前的状态
.fade-leave-from {
width: 200px;
height: 200px;
transform: rotate(360deg);
}
// 退出时的过渡
.fade-leave-active {
transition: all 1s ease;
}
// 退出结束的状态
.fade-leave-to {
width: 0;
height: 0;
}
</style>

自定义类名

为transition传prop:

  • enter-from-class
  • enter-active-class
  • enter-to-class
  • leave-from-class
  • leave-active-class
  • leave-to-class

例如设enter-from-class为”abc”,就能通过类名abc规定对应的动画过程了。

这样做更方便样式的复用,或结合第三方类库使用。

1
2
3
4
5
6
7
8
9
10
11
12
<transition enter-from-class="abc" name="fade">
<div v-if="flag" class="box"></div>
</transition>

// 省略......

<style>
// 进入前的状态
.abc {
// 省略......
}
</style>

结合Animate.css

自定义类名结合Animate.css动画库使用。

1
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
<template>
<div class="container">
<button @click="flag = !flag">switch</button>
<!-- duration:动画执行时间 -->
<!-- 传入一个数字,则进入和离开都遵循此时间(例 :duration="500") -->
<!-- 传入一个对象,则可对进入和离开单独定义(例 :duration="{enter:250,leave:500}") -->
<transition
leave-active-class="animate__animated animate__fadeOut"
enter-active-class="animate__animated animate__fadeIn"
:duration="{enter:250,leave:500}">
<div v-if="flag" class="box"></div>
</transition>
</div>
</template>

<script setup lang="ts">
import { ref } from "vue"
import "animate.css"

const flag = ref<boolean>(true)
</script>

<style lang="scss">
.box {
width: 200px;
height: 200px;
background-color: pink;
}
</style>

生命周期

transition有八个生命周期:

  • @before-enter="beforeEnter":对应enter-from
  • @enter="enter":对应enter-active
  • @after-enter="afterEnter":对应enter-to
  • @enter-cancelled="enterCancelled":显示过度打断
  • @before-leave="beforeLeave":对应leave-from
  • @leave="leave":对应enter-active
  • @after-leave="afterLeave":对应leave-to
  • @leave-cancelled="leaveCancelled":离开过度打断

因为有一些复杂的过渡效果无法被CSS实现,必须动用JS,所以Vue提供了transition的生命周期。

1
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
<template>
<div class="container">
<button @click="flag = !flag">switch</button>
<!-- duration:动画执行时间 -->
<!-- 传入一个数字,则进入和离开都遵循此时间(例 :duration="500") -->
<!-- 传入一个对象,则可对进入和离开单独定义(例 :duration="{enter:250,leave:500}") -->
<transition
@before-enter="EnterFrom"
@enter="EnterActive"
@after-enter="EnterTo"
@enter-cancelled="EnterCancel"
@before-leave="LeaveFrom"
@leave="LeaveActive"
@after-leave="LeaveTo"
@@leave-cancelled="LeaveCancel">
<div v-if="flag" class="box"></div>
</transition>
</div>
</template>

<script setup lang="ts">
import { ref } from "vue"
import "animate.css"

const flag = ref<boolean>(true)

// @param el 被过渡的元素
const EnterFrom = (el: Element)=> {
console.log("进入前")
}
// @param done 完成时执行的回调
const EnterActive = (el: Element, done: Function)=> {
console.log("进入时")
// 进入动画三秒后完成
setTimeout(() => {
done()
}, 3000)
}
const EnterTo = (el: Element)=> {
console.log("进入完")
}
const EnterCancel = (el: Element)=> {
console.log("打断进入")
}

const LeaveFrom = (el: Element)=> {
console.log("离开前")
}
const LeaveActive = (el: Element, done: Function)=> {
console.log("离开时")
// 离开动画三秒后完成
setTimeout(() => {
done()
}, 3000)
}
const LeaveTo = (el: Element)=> {
console.log("离开完")
}
const LeaveCancel = (el: Element)=> {
console.log("打断离开")
}
</script>

<style lang="scss">
.box {
width: 200px;
height: 200px;
background-color: pink;
}
</style>

结合GSAP

GSAP是一个JS动画库,可以结合transition组件的生命周期使用。

1
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
44
45
46
47
<template>
<div class="container">
<button @click="flag = !flag">switch</button>
<transition
@before-enter="EnterFrom"
@enter="EnterActive"
@leave="LeaveActive">
<div v-if="flag" class="box"></div>
</transition>
</div>
</template>

<script setup lang="ts">
import { ref } from "vue"
import gsap from "gsap"

const flag = ref<boolean>(true)

const EnterFrom = (el: Element)=> {
gsap.set(el, {
width:0,
height:0
})
}
const EnterActive = (el: Element, done: gsap.Callback)=> {
gsap.to(el, {
width:200,
height:200,
onComplete:done
})
}
const LeaveActive = (el: Element, done: gsap.Callback)=> {
gsap.to(el, {
width:0,
height:0,
onComplete:done
})
}
</script>

<style lang="scss">
.box {
width: 200px;
height: 200px;
background-color: pink;
}
</style>

appear

appear有三个属性:

  • appear-from-class
  • appear-active-class
  • appear-to-class

通过这些属性可以设置初始节点过渡,即页面加载完成就开始的动画。

但进入或离开的时候不会执行这些动画。

1
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
<template>
<div class="container">
<button @click="flag = !flag">switch</button>
<transition
appear
appear-from-class="from"
appear-active-class="active"
appear-to-class="to">
<div v-if="flag" class="box"></div>
</transition>
</div>
</template>

<script setup lang="ts">
import { ref } from "vue"

const flag = ref<boolean>(true)
</script>

<style lang="scss">
.box {
width: 200px;
height: 200px;
background-color: pink;
}
.from {
width: 0px;
height: 0px;
}
.active {
transition: all 1s ease;
}
.to {
width: 200px;
height: 200px;
}
</style>