# 大纲
- Vue动画方式 1 -
CSS transition
- Vue动画方式 2 -
CSS animation
- Vue动画方式 3 - JS 操作动画
- Vue动画方式 4 - 多元素动画
- Vue动画方式 5 - 列表动画
- Vue动画方式 6 - 组件过渡动效
- 总结之前的几种动画库
- 项目运用
首先了解 Vue文档中的 进入/离开 & 列表过渡 (opens new window)
- Vue 在插入、更新或者移除 DOM 时,提供多种不同方式的应用过渡效果。包括以下工具:
- 在 CSS 过渡和动画中自动应用
class
- 可以配合使用第三方 CSS 动画库,如
Animate.css
- 在过渡钩子函数中使用
JavaScript
直接操作 DOM - 可以配合使用第三方
JavaScript
动画库,如Velocity.js(dead)
anime.js
GSAP(Vue3.0推荐)
- 在 CSS 过渡和动画中自动应用
# Vue动画方式 1 - CSS transition
- Vue 提供封装的
<transition name="xxx"></transition>
标签,在标签内写需要设置动画的节点 - 给任何元素/组件 添加 进入/离开 过渡效果
- 条件渲染
v-if
- 条件展示
v-show
- 动态组件
v-bind:is=""
- 组件根节点
- 条件渲染
- 必须设置相关类样式的前缀
name="xxx"
,必须包括如下样式:xxx-enter-active
xxx-leave-active
xxx-enter
xxx-leave-to
- 注意 进入/离开 过渡样式相同时,可以并列(例如设置
name="fade"
).fade-enter-active, .fade-leave-active {}
.fade-enter, .fade-leave-to {}
- 一些 UI 组件库的CSS全局样式会污染默认样式
例子
Transition
Hello
CSS transition
<template>
<div class="fixHeight80">
<h2>{{ title }}</h2>
<br>
<button @click="visible = !visible">
Toggle
</button>
<transition :name="transitionName">
<p v-if="visible">{{ showText }}</p>
</transition>
<br>
</div>
</template>
<script>
export default {
data() {
return {
visible: true,
title: 'Transition',
transitionName: 'fade',
showText: 'Hello',
}
}
}
</script>
<style>
.fixHeight100 {
height: 100px;
}
.fade-enter-active,
.fade-leave-active {
transition: all 1s;
}
.fade-enter,
.fade-leave-to {
opacity: 0;
width: 100px;
}
p {
width: 300px;
}
</style>
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
分析 1
- 用
<transition></transition>
包裹需要加动画的节点 - 设置属性
name
:<transition name="fade">
- 设置初始样式,比如
- 无自身初始值的属性,比如
width
必须有初始值 opacity
无需设置初始值,初始即为1
- 无自身初始值的属性,比如
- 设置过度样式
.fade-enter-active, .fade-leave-active {transition: all 2s;}
.fade-enter, .fade-leave-to {...}
- 设置 切换条件
v-if
/v-show
分析 2
- 过渡过程
name
属性值替代v
- 使用一个没有名字的
<transition>
,则v-
是这些类名的默认前缀 - 如果使用了
<transition name="my-transition">
,那么v-enter
会替换为my-transition-enter
- 使用一个没有名字的
- 淡入的第一帧
.v-enter
元素插入前生效的样式,插入后下一帧删除 - 进入过度生效时状态
.v-enter-active
过渡/动画中.v-active {transition: all 2s;}
此时使用transition
过渡 - 进入过度结束状态
.v-enter-to
淡入最后一帧 可添加样式 - 一般不需要
.v-enter-to
或.v-leave
,结束状态即初始状态 - 完成过渡,删除所有过渡
v
的样式,只保留元素自身原来的样式 v-leave
->v-leave-active
->v-leave-to
离开过渡.v-enter-active
v-leave-active
在 过渡/动画 完成之后移除- 这个类可以被用来定义离开过渡的过程时间
duration
,延迟delay
和曲线函数easing-function
等
- 这个类可以被用来定义离开过渡的过程时间
v-enter-to
v-leave-to
在 **过渡/动画 **完成之后移除
CSS transition 过渡 另一个例子 Slide
封装通用组件ShowPanel
<template>
<div>
<br>
<h2>{{ title }}</h2>
<br>
<button @click="toggle">
{{buttonText}}
</button>
<transition :name="transitionName"
v-if="animationMode"
>
<p class="animateTarget" v-show="visible">{{ showText }}</p>
</transition>
<transition v-else
:name="transitionName"
:enter-active-class="animeEnterClassName"
:leave-active-class="animeLeaveClassName"
>
<p class="animateTarget" v-show="visible">{{ showText }}</p>
</transition>
<br>
</div>
</template>
<script>
export default {
data() {
return {
visible: true
}
},
methods: {
toggle() {
this.visible = !this.visible
}
},
props: {
title: {
type: String,
required: true,
default: '',
},
transitionName: {
type: String,
},
showText: {
type: String,
required: true,
default: 'Hello',
},
buttonText: {
type: String,
default: 'Toggle',
},
animationMode: {
type: Boolean,
default: true,
},
animeEnterClassName: {
type: String,
default: '',
},
animeLeaveClassName: {
type: String,
default: '',
}
}
}
</script>
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
hello
CSS transition
<template>
<div class="demoHeight">
<button @click="show = !show">
Toggle render
</button>
<transition name="slide-fade">
<p v-if="show">hello</p>
</transition>
</div>
</template>
<script>
export default {
name: 'slide',
data() {
return {
show: true
}
}
}
</script>
<style>
.demoHeight {
height: 40px;
}
.slide-fade-enter-active {
transition: all .3s ease;
}
.slide-fade-leave-active {
transition: all .8s cubic-bezier(1.0, 0.5, 0.8, 1.0);
}
.slide-fade-enter, .slide-fade-leave-to {
transform: translateX(10px);
opacity: 0;
}
</style>
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
小结
- 使用
<transition name="xxx"></transition>
标签,元素作为单个元素/组件的过渡效果 <transition>
只会把过渡效果应用到其包裹的内容上- 而不会额外渲染 DOM 元素
- 也不会出现在可被检查的组件层级中
vue transition API
(opens new window)- 切换条件
v-if
/v-show
- 添加过渡样式
- 元素自身样式
- 添加初始样式(
in / out
) - 添加过渡属性
transition: transition-property,transition-duration,transition-timing-function transition-delay ;
在
codepen
可以快速尝试适合的依赖环境
# Vue动画方式2- CSS animation
# 使用animation
属性和@keyframes
关键帧动画
使用方法
- 同样使用
<transition name="xxx">
标签,name="xxx"
为类样式前缀 - 区别于
CSS transition
过渡属性:- 在
CSS animation
动画属性v-enter
类名在节点插入DOM
后不会立即删除 - 默认情况下,Vue 会等待过渡所在根元素的第一个 transitionend 或 animationend 事件
v-enter
类在animationend
事件 (MDN) 被触发时删除animationend
事件 (MDN) (opens new window)
- 在
- 在
@keyframes
各个关键帧中设置 变形、颜色、透明等属性- 需要设置过渡状态的类样式
.xxx-enter-active{animation: ...}
:正在淡入.xxx-leave-active{animation: ...}
:正在淡出- 在
@keyframes
中 设置关键帧(to / from / 0% / 100%
)代替enter / leave
reverse
反向淡出动画,设置.xxx-leave.active{animation: animationName 1s reverse}
- 保证最终状态即正常状态
animation
属性和@keyframes
关键帧动画示例
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris facilisis enim libero, at lacinia diam fermentum id. Pellentesque habitant morbi tristique senectus et netus.
CSS animation
<template>
<div class="fixHeight150">
<button @click="showAnimation = !showAnimation">
show Animation
</button>
<transition name="bounce">
<p v-if="showAnimation">
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>
</template>
<script>
export default {
data() {
return {
showAnimation: true
}
},
}
</script>
<style>
.fixHeight150 {
height: 150px;
}
.bounce-enter-active {
animation: bounce-in .5s;
}
.bounce-leave-active {
animation: bounce-in .5s reverse;
}
@keyframes bounce-in {
0% {
transform: scale(0);
}
50% {
transform: scale(1.5);
}
100% {
transform: scale(1);
}
}
</style>
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
# 自定义过渡类名
在以下的
<trasition>
标签属性中可添加自定义过渡类名
enter-class
enter-active-class
enter-to-class (2.1.8+)
leave-class
leave-active-class
leave-to-class (2.1.8+)
例如:
<transition enter-active-class="xxx" leave-active-class="yyy">...</transition>
# 结合animate.css
动画库使用
测试支持的版本
- 在
codepen
中,直接选取依赖版本尝试运行是否成功 - 如果是本地项目
- 先在
BootCDN
找到库引用,选择版本,直接插入项目的index.html
- 尝试是否可以运行
- 将可以正常运行的版本号作为
yarn add -D xxx@***
的依据 - 导入入口文件
import ...*.js/ts
- 先在
Vue2.x
运行环境(经过在codepen
中的测试)
@vue/cli-plugin-babel@4.1.1
animate.css@3.6.1
或animate.css@4.1.1
vue@2.6.11
或者
Vue3.x
运行环境
@vue/cli-plugin-babel@4.5.12
animate.css@4.1.1
vue@3.0.11
安装
animate.css
yarn add animate.css@3.6.1
# yarn add animate.css@4.1.1
2
全局引入:在入口文件
main.ts
中导入 (或者在需要用到的vue组件中局部引入)
import 'animate.css'
...
2
使用
animate.css
- 注意使用时
animate.css@3.6.1
与animate.css@4.1.1
类前缀不同 - 导入到入口文件
main.js/ts
中去:import 'animate.css'
- 编译后会自动添加相应的
<style></style>
标签为全局样式
animate.css
示例
hell O
CSS animate.css@3.6.1
<template>
<div>
<button @click="showAnimateCSS = !showAnimateCSS">
Toggle
</button>
<transition enter-active-class="animated tada"
leave-active-class="animated bounceOutRight">
<p v-if="showAnimateCSS">hell O</p>
</transition>
</div>
</template>
<script>
export default {
data() {
return {
showAnimateCSS: true
}
}
}
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
- CSS 动画样式在第三方库中已预设
- 添加属性
enter-active-class
leave-active-class
<transition enter-active-class="animated tada" leave-active-class="animated bounceOutRight">
- Animate Css官网 (opens new window)
自定义过渡类名配合使用
animate.css
和使用animation
属性和@keyframes
关键帧动画不兼容
# Vue动画方式 3 - JS 操作动画
- 在
attribute
(横线连接 (kebab-case
)) 中声明 JavaScript 钩子(驼峰式连接) - 在
methods
中写对应的方法
<transition
@before-enter="beforeEnter"
@enter="enter"
@after-enter="afterEnter"
@enter-cancelled="enterCancelled"
@before-leave="beforeLeave"
@leave="leave"
@after-leave="afterLeave"
@leave-cancelled="leaveCancelled">
<!-- ... -->
</transition>
2
3
4
5
6
7
8
9
10
11
- 动画的生命周期
@before-enter
@enter
@after-enter
@enter-cancelled
@before-leave
@leave
@after-leave
@leave-cancelled
# 不推荐Velocity.js
(译为:速度) 动画库(github REPO
已停止维护)
yarn add velocity-animate@beta
velocity-animate@2.0.6
(opens new window)- Velocity v2 文档 (opens new window)
安装稳定版
yarn add velocity-animate
使用略
# Vue
配合动画库anime.js
版本animejs@3.2.1
安装
yarn add animejs@3.2.1 --save
- 安装
"animejs": "^3.2.1"
(如果装TS 还需安装对应类型"@types/animejs": "^3.1.3"
)
官方链接
参考文章
- 推荐使用自定义指令实现的
anime.js
二次封装库vue-anime (opens new window) - 支持时间线控制,分组动画的
anime.js
二次封装库vue-anime (opens new window) - Anime+Vue<粒子按键效果> (opens new window)
- Anime+Vue<小人奔跑效果> (opens new window)
temp link
- webpack - 如何在Vue-Cli项目中使用anime.js(或任何外部库)? (opens new window)
- 如何将anime.js导入我的Vue项目? (opens new window)
示例
# 配合动画库GSAP
(Vue3.x
推荐)
# Vue动画方式 4 - 多元素动画
示例
<transition>
<article v-if="items.length > 0">
<!-- 多标签过渡是一个列表和描述这个列表为空消息的元素 -->
</article>
<p v-else>Sorry, no items found.</p>
</transition>
<!-- 相同标签名的元素切换 -->
<transition>
<button v-if="isEditing" key="save">
Save
</button>
<button v-else key="edit">
Edit
</button>
</transition>
<!-- d多个标签名的元素切换 -->
<transition>
<button v-if="docState === 'saved'" key="saved">
Edit
</button>
<button v-if="docState === 'edited'" key="edited">
Save
</button>
<button v-if="docState === 'editing'" key="editing">
Cancel
</button>
</transition>
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
- 写在
<transition></transition>
标签上的属性 过渡模式mode="out-in"
,先退出,再进入 <transition></transition>
标签中包含 原生标签- 对于不同标签名的元素切换
- 使用
v-if/v-else
- 使用
- 对于相同标签名的元素切换
- 必须通过
key attribute
设置唯一的值来标记以让Vue
区分 - 多个相同结构与属性的元素进行合并,使用动态绑定
:key
区分,代替v-if/v-else
- 必须通过
- 对于不同标签名的元素切换
- 如果使用 过渡模式
mode="in-out"
,需要将元素设置为绝对定位position: absolute;
- 轮播效果无需设置过渡模式
# 元素切换实例
切换两个元素
切换三个元素
切换三个元素
mode: in-out
/docs/.vuepress/mixins/MultiElements.js
import MultiElementTemplate from "../components/Vue/Animation/MultiElementTemplate.vue";
export default {
components: {MultiElementTemplate},
data() {
return {
docStateList: ['saved', 'edited', 'editing'],
docState: 'saved',
tempList: [],
stateCount: 0
}
},
computed: {
buttonMessage: {
get: function () {
return {
'saved': 'Edit',
'edited': 'Save',
'editing': 'Cancel',
}[this.docState]
},
set: function (state) {
this.docState = state
}
}
},
beforeMount() {
this.tempList = [...this.docStateList]
},
methods: {
switchingDocState() {
this.docState = this.tempList.pop();
if (this.tempList.length === 0) {
this.tempList = [...this.docStateList]
}
}
}
}
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
切换三个元素
Carousel with no mode
- 注意:不要在
<transition>
标签内写JS行内表达式,而是要写到方法methods
中 - 否则会引起无线循环渲染报错
[Vue warn]: You may have an infinite update loop in a component render function
# 多个组件的过渡
- 不需要使用
key attribute
,使用 动态组件 (opens new window) 实现<component :is="..."></component>
<transition name="component-fade" mode="out-in">
<component :is="view"></component>
</transition>
2
3
示例
<template>
<div>
<button @click="view='AnimeJS'">AnimeJS</button>
<button @click="view='Slide'">Slide</button>
<transition name="component-fade" mode="out-in">
<component :is="view"></component>
</transition>
</div>
</template>
<script>
import AnimeJS from "./AnimeJS.vue";
import Slide from "./Slide.vue";
export default {
name: "MultiComponents",
data() {
return {
view: 'AnimeJS'
}
},
components: {
AnimeJS,
Slide
}
}
</script>
<style lang="scss" scoped>
.component-fade-enter-active,
.component-fade-leave-active {
transition: opacity .3s ease;
}
.component-fade-enter, .component-fade-leave-to {
opacity: 0;
}
</style>
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
# Vue动画方式 5 - 列表动画 <transition-group>
TIP
- 除了渲染单个节点或者同一时间渲染多个节点中的一个,
Vue
还能同时渲染整个列表
<div>
<button @click="add">Add</button>
<button @click="remove">Remove</button>
<transition-group name="list" tag="p">
<span v-for="item in items" :key="item">
{{ item }}
</span>
</transition-group>
</div>
2
3
4
5
6
7
8
9
- 利用
<transition-group></transition-group>
可以对v-for
渲染的每个元素应用过渡效果
TIP
- 由于循环渲染的多个元素必须放在一个标签中
- 直接在
<transition-group></transition-group>
中写一个包裹元素 - 则子元素不是循环渲染的元素,无法识别
- 直接在
vue
使用tag attribute
,编译时将<transition-group tag="xxx">
替换为设置的元素名xxx
- 不同于
<transition>
,用<transition-group></transition-group>
会以一个真实元素呈现:- 默认为一个
<span>
- 可以通过
tag attribute
更换为其他元素
- 默认为一个
- 过渡模式
mode attribute
不可用,因为不再相互切换特有的单个元素 - 内部元素必须提供唯一的
key attribute
值 - CSS 过渡的类将会应用在内部的元素中,而不是这个组/容器本身
# 列表的进入/离开过渡(enter/leave
)
示例
TIP
- 数据
data
里不能循环引用和写入Vue
实例
...
// error
data() {
return {
items: [1, 2, 3, 4, 5],
nextNum: this.items
}
}
2
3
4
5
6
7
8
解决当 添加和移除 元素时 平滑过渡 的问题
- 当添加和移除元素的时候,周围的元素会瞬间移动到他们的新布局的位置,而不是平滑的过渡
- 设置
<transition-group name="xxx">
的xxx-move {}
和xxx-leave-active {}
类的样式可以实现平滑过渡xxx-move {transition: all 1s;}
xxx-leave-active {position: absolute;}
# 列表的移动过渡(move
)
- 使用
v-move class
(其中的v
是用置<transition-group name="xxx">
中的属性name
代替)- 不仅可以进入和离开动画,还可以改变定位,即为在元素的改变定位的过程中应用
- 设置 过渡的切换时机 和 过渡曲线 的例子
- 可以通过
name attribute
来自定义前缀 - 也可以通过
move-class attribute
手动设置
示例
- 安装
yarn add lodash
- 引入
import _ from 'lodash';
- 示例中为列表设置了排序时移动的过渡
- 内部的实现:Vue 使用了一个叫 FLIP (opens new window) 简单的动画队列
- 使用 transforms 将元素从之前的位置平滑过渡新的位置
- 注意使用 FLIP 过渡的元素不能设置为
display: inline
- 作为替代方案,可以设置为
display: inline-block
或者放置于flex
中
综合示例 使列表的一切变动都会有动画过渡
- 封装了
.../mixins/random.js
的逻辑
export default {
methods: {
randomIndex() {
return Math.floor(Math.random() * this.itemList.length)
},
add() {
this.itemList.splice(this.randomIndex(), 0, this.nextNum++)
},
remove() {
this.itemList.splice(this.randomIndex(), 1)
},
recover() {
this.itemList = [...this.originItems]
},
oddEven() {
((10 - Math.floor(Math.random() * 10)) % 2 === 0) ?
(this.add()) :
(this.remove())
},
checkItemList() {
if (this.itemList.length === 0) {
this.itemList.push(0)
this.recover()
}
},
},
watch: {
itemList: {
handler: 'checkItemList',
}
},
created() {
this.originItems = [...this.itemList]
},
}
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
# 列表<transition-group>
的多维网格过渡 Grid
示例
# 列表<transition-group>
的交错过渡 Staggered
示例
# 列表动画小结
# <transition>
或 <transition-group>
的可复用的过渡
过渡可以通过 Vue 的组件系统实现复用。要创建一个可复用过渡组件:
- 直接将
<transition>
或<transition-group>
作为根组件,加上方法或样式,进行统一封装 - 使用插槽
<slot></slot>
,使用时将任何子组件放置在其中
<template>
<transition
name="very-special-transition"
mode="out-in"
@before-enter="beforeEnter"
@after-enter="afterEnter">
<slot></slot>
</transition>
</template>
<script>
export default {
methods: {
beforeEnter(el) { // ... },
afterEnter(el) { // ... }
}
}
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# <transition>
或 <transition-group>
的动态过渡
- 过渡也是数据驱动
- 动态过渡最基本的例子是通过
name attribute
来绑定动态值 - 用 Vue 的过渡系统来定义的 CSS 过渡/动画在 不同 过渡间 切换
<transition :name="transitionName">
<!-- ... -->
</transition>
2
3
- 所有过渡
attribute
都可以动态绑定,比如move attribute
- 还可以通过事件钩子获取上下文中的所有数据,因为事件钩子都是方法
- 根据组件的状态不同,JS 过渡 会有不同的表现
示例
- 创建动态过渡的最终方案是组件通过接受 props 来动态修改之前的过渡
---
## 总结之前的几种动画
---
## 动画库
### 第三方 CSS 动画库[`Animate.css`](https://www.dowebok.com/demo/2014/98/)
- 安装`yarn add animate.css@3.6.1`
```html
<template>
<div>
<button @click="showAnimateCSS = !showAnimateCSS">
Toggle render
</button>
<transition name="custom-classes-transition"
enter-active-class="animated tada"
leave-active-class="animated bounceOutRight">
<p v-if="showAnimateCSS">hello</p>
</transition>
</div>
</template>
<script>
export default {
data() {
return {
showAnimateCSS: true
}
},
}
</script>
<style lang="scss" scoped>
// ...
</style>
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
- CSS在第三方库中预设好了
- 添加属性
enter-active-class
leave-active-class
<transition enter-active-class="animated tada" leave-active-class="animated bounceOutRight">
- animate.css 官网 (opens new window)
方案导向
# 项目运用
- Tab切换
- 轮播
- 跳动的心动画
参考
- codepen
- codeSandBox
- vue中使用animate.css实现动画 (opens new window)
- 5分钟学会Vue动画效果 (opens new window)
- 优美的v-for列表加载动画:vue动画钩子实践 (opens new window)
- 4 个 Vue 路由过渡动效 (opens new window)
- 【Vue学习】关于transition过渡动画的收获 (opens new window)
- 饥人谷 【Vue全解】深入讲解 Vue 动画原理 by 方应杭 (opens new window)