一. vue的过渡与动画
Vue 在插入、更新或者移除 DOM 时,提供多种不同方式的应用过渡效果。过渡效果的应用可以通过不同方式实现:
- 在CSS过渡和动画中自动应用class
- 在过渡钩子函数中使用JavaScript直接操作dom
- 配合第三方css动画库:Animate.css 或者第三方javascript动画库:Velocity
上面三种方式其实主要就是两种:一个是利用CSS过渡或者动画,另一个是利用JavaScript钩子函数。
二. 如何应用过渡与动画
下面从进入离开过渡以及列表过渡两大方向,概述vue如何为元素添加过渡效果。
2-1. 单元素/组件进入离开过渡
1. css过渡
vue提供了transition
封装组件,在以下情况下为任何单元素以及组件添加进入/离开过渡。
- 条件渲染
v-if
/ 条件展示v-show
- 动态组件
- 组件根节点
例子如下:
<div>
<button @click="show = !show">切换</button>
<transition name="fade">
<p v-if="show">看我显示隐藏!!!</p>
</transition>
</div>
.fade-enter-active {
transition: all 0.5s ease;
}
.fade-leave-active {
transition: all 0.9s cubic-bezier(1, 0.5, 0.8, 1);
}
.fade-enter,
.fade-leave-to {
opacity: 0;
transform: translateX(20px);
}
2. css动画
CSS 动画用法同 CSS 过渡,区别是在动画中 v-enter
类名在节点插入 DOM 后不会立即删除,而是在 animationend
事件触发时删除。
<transition name="bounce">
<div style="width:300px;height:400px;" 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.
</div>
</transition>
3. javascript钩子函数
可以在<transition>
组件上声明JavaScript钩子函数。
<transition
v-on:before-enter="beforeEnter"
v-on:enter="enter"
v-on:after-enter="afterEnter"
v-on:enter-cancelled="enterCancelled"
v-on:before-leave="beforeLeave"
v-on:leave="leave"
v-on:after-leave="afterLeave"
v-on:leave-cancelled="leaveCancelled"
>
</transition>
methods: {
// ---- 进入中 ------
beforeEnter: function (el) {
},
// 当与 CSS 结合使用时
// 回调函数 done 是可选的
enter: function (el, done) {
// ...
done()
},
afterEnter: function (el) {
// ...
},
enterCancelled: function (el) {
// ...
}
}
4. 过渡的类名
进入/离开过渡的类名
-
v-enter
:定义过渡的开始状态。在元素插入之前生效,在元素被插入的下一帧移除 -
v-enter-active
:定义过渡生效时候的状态。在整个进入过渡的阶段中应用。这个类可以被用来定义进入过渡的过程时间,延迟和曲线函数 -
v-enter-to
:定义过渡的结束状态。在元素被插入之后下一帧生效,在过渡/动画完成之后移除。 -
v-leave
:定义离开过渡的开始状态。在离开过渡被触发时立刻生效,下一帧被移除。 -
v-leave-active
:定义离开过渡生效时的状态。在整个离开过渡的阶段中应用。 -
v-leave-to
:定义离开过渡的结束状态。
- 在
<transition name="fade">
组件中,通过name
属性,定义过渡的类名;如果没有命名,则使用默认的v-
为类名的前缀,例如:v-enter
... - 我们还可以自定义过渡的类名:
enter-class
,enter-active-class
,enter-to-class
,leave-class
,leave-active-class
,leave-to-class
。它们的优先级高于普通的类名。
代码如下:
<button @click="show = !show">切换</button>
<transition
name="custom-classes-transition"
enter-active-class="animated tada"
leave-active-class="animated bounceOutRight"
>
<p v-if="show">看我!</p>
</transition>
.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.5);
}
0% {
transform: scale(1);
}
}
5. 同时使用css过渡/动画
使用 type
特性并设置 animation
或 transition
来明确声明你需要 Vue 监听的类型。
6. 过渡持续事件
用 <transition>
组件上的 duration
属性定制一个显性的过渡持续时间 (以毫秒计):
<transition :duration="1000">...</transition>
<transition :duration="{ enter: 500, leave: 800 }">...</transition>
7. 初始渲染的过渡
使用apear
特性设置节点在初始渲染的过渡。与进入和离开一样,可以自定义css类名以及javascript钩子函数。
<transition
appear
appear-class="custom-appear-class"
appear-to-class="custom-appear-to-class"
appear-active-class="custom-appear-active-class"
>
<!-- ... -->
</transition>
<transition
appear
v-on:before-appear="customBeforeAppearHook"
v-on:appear="customAppearHook"
v-on:after-appear="customAfterAppearHook"
v-on:appear-cancelled="customAppearCancelledHook"
>
<!-- ... -->
</transition>
8. 多个元素/组件 过渡
-
<transition>
内如果是原生标签,可以使用v-if
/v-else
。
注意事项:当有相同标签名的元素切换时,需要给在 <transition> 组件中的多个元素设置 key 是一个更好的实践。
- 多组件过渡,不需要提供
key
,只需要使用动态组件
。
<transition>
<button v-if="isEditing" key="save"> Save </button>
<button v-else key="edit"> Edit </button>
</transition>
9. vue 提供的过渡模式
-
in-out
:新元素先进行过渡,完成之后当前元素过渡离开。 -
out-in
:当前元素先进行过渡,完成之后新元素过渡进入。
2-2 列表过渡
在使用v-for
同时渲染整个列表的时候,可以使用<transition-group>
组件。其与<transition>
组件的区别:
- 以真实元素呈现,默认是
<span>
,可以通过tag
属性更换其他元素。 -
过渡模式
不可用 - 内部元素总是需要提供key属性值
列表的三种过渡模式:
- 列表的进入/离开过渡
- 列表的排序过渡
- 列表的交错过渡
代码汇总如下:
<!-- 1. 列表交错过渡 -->
<div id="staggered-list-demo">
<input v-model="query">
<transition-group
name="staggered-fade"
tag="ul"
v-bind:css="false"
v-on:before-enter="beforeEnter"
v-on:enter="enter"
v-on:leave="leave"
>
<li
v-for="(item, index) in computedList"
v-bind:key="item.msg"
v-bind:data-index="index"
>{{ item.msg }}</li>
</transition-group>
</div>
<!-- 2. 列表进入-离开过渡 -->
<button @click="add">add</button>
<button @click="remove">remove</button>
<transition-group tag="p" name="list">
<span v-for="item in items" :key="item" class="list-item">{{item}}</span>
</transition-group>
<!-- 3. 列表排序过渡 -->
<button @click="Shuffle">Shuffle</button>
<transition-group tag="ul" name="flip-list">
<li v-for="item in items" :key="item" class="list-item">{{item}}</li>
</transition-group>
export default {
name: "listTransition",
data() {
return {
list: [
{ msg: "Bruce Lee" },
{ msg: "Jackie Chan" },
{ msg: "Chuck Norris" },
{ msg: "Jet Li" },
{ msg: "Kung Fury" }
],
items: [1, 2, 3, 4, 5, 6, 7, 8, 9],
nextNum: 10
};
},
computed: {
computedList: function() {
var vm = this;
return this.list.filter(function(item) {
return item.msg.toLowerCase().indexOf(vm.query.toLowerCase()) !== -1;
});
}
},
methods: {
// 1. 当只用 JavaScript 过渡的时候,在 enter 和 leave 中必须使用 done 进行回调。否则,它们将被同步调用,过渡会立即完成。
// 2. 推荐对于仅使用 JavaScript 过渡的元素添加 v-bind:css="false",Vue 会跳过 CSS 的检测。这也可以避免过渡过程中 CSS 的影响。
// appear 特性设置节点在初始渲染的过渡
// <transition-group>组件 用于列表进入离开过渡 / 排序过渡(v-move)
// 自定义类名前缀:(1)name属性 (2)move-class/enter-class...属性手动设置
beforeEnter: function(el) {
el.style.opacity = 0;
el.style.height = 0;
},
enter: function(el, done) {
var delay = el.dataset.index * 150;
setTimeout(function() {
Velocity(el, { opacity: 1, height: "1.6em" }, { complete: done });
}, delay);
},
leave: function(el, done) {
var delay = el.dataset.index * 150;
setTimeout(function() {
Velocity(el, { opacity: 0, height: 0 }, { complete: done });
}, delay);
},
// 2. 列表进入-离开过渡
randomIndex: function() {
return Math.floor(Math.random() * this.items.length);
},
add() {
this.items.splice(this.randomIndex(), 0, this.nextNum++);
},
remove() {
this.items.splice(this.randomIndex(), 1);
},
// 3. 列表-排序过渡
Shuffle() {
this.items = _.shuffle(this.items);
}
}
};
/* 2. 进入离开过渡-列表 */
.list-item {
margin-right: 20px;
display: inline-block;
}
.list-enter-active,
.list-leave-active {
transition: all 1s;
}
.list-enter,
.list-leave-to {
opacity: 0;
transform: translateY(30px);
}
/* 排序 */
.flip-list-move{
transition: transform 1s;
}