Vue 在插入、更新或者移除 DOM (就是添加、移除元素节点) 时,提供多种不同方式的应用过渡效果。
包括以下工具:
- 在 CSS 过渡 和动画中自动应用 class (在 <style> 中定义 css 效果)
- 在过渡钩子函数中使用 JavaScript 直接操作 DOM (methods 里面直接定义函数,并在<transition> 组件中用v-on 绑定 过渡状态 和methods 里的函数)
- 可以配合使用第三方 CSS 动画库,如 Animate.css
- 可以配合使用第三方 JavaScript 动画库,如 Velocity.js
过渡的类名:
在进入/离开的过渡中,会有 6 个 class 切换。
1. v-enter
:定义进入过渡的开始状态。在元素被插入之前生效,在元素被插入之后的下一帧移除。
2. v-enter-active
:定义进入过渡生效时的状态。在整个进入过渡的阶段中应用,在元素被插入之前生效,在过渡/动画完成之后移除。这个类可以被用来定义进入过渡的过程时间,延迟和曲线函数。
3. v-enter-to
: 2.1.8版及以上 定义进入过渡的结束状态。在元素被插入之后下一帧生效 (与此同时v-enter
被移除),在过渡/动画完成之后移除。
4. v-leave
: 定义离开过渡的开始状态。在离开过渡被触发时立刻生效,下一帧被移除。
5. v-leave-active
:定义离开过渡生效时的状态。在整个离开过渡的阶段中应用,在离开过渡被触发时立刻生效,在过渡/动画完成之后移除。这个类可以被用来定义离开过渡的过程时间,延迟和曲线函数。
6. v-leave-to
: 2.1.8版及以上 定义离开过渡的结束状态。在离开过渡被触发之后下一帧生效 (与此同时v-leave
被删除),在过渡/动画完成之后移除。
过渡的名字 name:
如果你使用一个前没有名字的
<transition>
,则v-
是这些类名的默认缀。如果你使用了<transition name="my-transition">
,那么v-enter
会替换为my-transition-enter
。
动画animation 与过渡 transition 的区别
在动画中
v-enter
类名在节点插入 DOM 后不会立即删除,而是在animationend
事件触发时删除。
使用过渡 transtion和 动画animation 的3种方式:
一、使用CSS (使用 name 特性,绑类名 css 样式)
1. 在<style>标签里设置过渡的类的样式(6 个 class:v-enter-active,v-enter,v-enter-to;v-leave,v-leave-to,v-leave-active)
2. 在 <transition> 组件里 使用 name 特性,绑类名 css 样式(例如: <transition name="change">,<style> </style> 里面的样式名应绑定为 change-enter-active,change-enter,change-enter-to;change-leave,change-leave-to,change-leave-active)。(例如:<transition name="change">)
<style> .change-enter{ color:red; font-size:20px; transition:2s; } .change-enter-to{ color:blue; font-size:58px; transition:5s; } .change-enter-active{ transition-timing-function:ease; } .change-leave-active{ color:yellow; font-size:2px; transition:3s;} </style>
<body> <div id="demo"> <button v-on:click="show=!show">love?</button> <transition name="change"> <div v-if="show">{{mes}}</div> </transition> </div>
<script> new Vue({ el:"#demo", data:{ show:true, mes:"I love you!" } }) </script>
渲染效果:
二、自定义类名(优先级高于普通的类名,一般用于第三方库。如 Animate.css 。不用于库也可以 -可直接绑定css 样式。具体见下文 3. )
enter-class
enter-active-class
enter-to-class
leave-class
leave-active-class
leave-to-class
1. 在<link>中引用文件
<link href="https://cdn.jsdelivr.net/npm/animate.css@3.5.1" rel="stylesheet" type="text/css">
<link href="https://cdn.jsdelivr.net/npm/animate.css@3.5.1" rel="stylesheet" type="text/css">
2.在<transition> 组件中绑定类名和第三方库中的样式名
<transition name="custom-classes-transition" enter-active-class="animated tada" leave-active-class="animated bounceOutRight" >
完整案例
使用第三方库 Animate.css示例代码:
<div id="demo"> <button @click="show = !show"> love? </button> <transition enter-active-class="animated flip" leave-active-class="animated hinge"> <div v-if="show">{{MES}}</div> </transition> </div> <script> new Vue({ el: '#demo', data:{ MES:"I LOVE YOU", show:true } }) </script>
渲染效果:
3. 不用于库也可以 -可直接绑定css 样式。优先于name+css 。
例子解析:这里的enter-class绑定了enter-to 这个自定义样式绑定了,比 name+css 的样式 change-enter-to 优先级更高。(所以enter-to 这个样式生效,change-enter-to 这个样式不显示)
<transition name="change" enter-class="enter-to">
<style> .change-enter-to{ color:red; font-size:20px; transition:2s; } .enter-to{ color:blue; font-size:58px; transition:5s; } </style>
三、声明钩子
1. 在vue 实例methods 中定义函数
2. 在<transition> 组件中 绑定钩子和函数 <transition v-on:before-enter="beforeEnter">
注意:
1)需要使用done回调函数防止过渡钩子同时进行。当只用 JavaScript (就是使用JS函数的方法过渡,而不使用CSS过渡)过渡的时候,在
enter
和leave
中必须使用done
进行回调。否则,它们将被同步调用,过渡会立即完成。也就是说,使用了done 回调函数,钩子才能一个一个按顺序执行,不会同步执行(同步执行时,值看到最后顺序的钩子的执行效果)2)
v-bind:css="false"屏蔽CSS效果(使钩子函数不受<style>里面的CSS效果影响)。
对于仅使用 JavaScript 过渡的元素添加v-bind:css="false"
,Vue 会跳过 CSS 的检测。这也可以避免过渡过程中 CSS 的影响。<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> <script> new Vue({ el: '#demo', data: { show: true }, methods: { // -------- // 进入中 // -------- beforeEnter: function (el) { // ... }, // 当与 CSS 结合使用时 // 回调函数 done 是可选的 enter: function (el, done) { // ... done() }, afterEnter: function (el) { // ... }, enterCancelled: function (el) { // ... }, // -------- // 离开时 // -------- beforeLeave: function (el) { // ... }, // 当与 CSS 结合使用时 // 回调函数 done 是可选的 leave: function (el, done) { // ... done() }, afterLeave: function (el) { // ... }, // leaveCancelled 只用于 v-show 中 leaveCancelled: function (el) { // ... } } }) </script>
apper 特性: 初始渲染的过渡
(就是给元素设置第一次渲染的过渡效果)
一、apper 特性
<transition appear appear-class="custom-appear-class" appear-to-class="custom-appear-to-class" (2.1.8+) appear-active-class="custom-appear-active-class" > <!-- ... --> </transition>
二、可定义CSS 类名:
<transition appear appear-class="custom-appear-class" appear-to-class="custom-appear-to-class" (2.1.8+) appear-active-class="custom-appear-active-class" > <!-- ... --> </transition>
三、自定义 JavaScript 钩子:
<transition appear v-on:before-appear="customBeforeAppearHook" v-on:appear="customAppearHook" v-on:after-appear="customAfterAppearHook" v-on:appear-cancelled="customAppearCancelledHook" > <!-- ... --> </transition>
多元素过渡
一、用 v-if v-else 语句 (需要设置 key 特性,让 vue 区分不同的元素)
<div id="demo"> <transition v-on:before-enter="beforeEnter" v-on:enter="enter" v-on:leave="leave"> <button v-if="isEditing" key="save" v-on:click="isEditing=!isEditing" > Save </button> <button v-else key="edit" v-on:click="isEditing=!isEditing"> Edit </button> </transition> </div> <script> new Vue({ el: '#demo', data:{ isEditing:true }, methods:{ beforeEnter: function (el) { ...... }} }) </script>
二、通过给同一个元素的 key 特性设置不同的状态来代替 v-if 和 v-else
解析:
1. 用 computed 计算属性(详细可参考:计算属性原理),
a. 创建了 buttonMessage 这个计算属性。这个buttonMessage 计算属性,依赖于 docState,随着 docState 的值的 变化 而变化。响应 docState 值的变化。
b. computed 计算属性需要 getter 和 setter 两个函数。(详细可参考:computed计算属性设置setter 、computed 的get 和set),
2. 使用 switch case 条件语句
<transition v-on:before-enter="beforeEnter" v-on:enter="enter" v-on:leave="leave" v-bind:css="false"> <button v-bind:key="docState" v-on:click="change"> {{ buttonMessage }} </button> </transition> </div> <script> new Vue({ el: '#demo', data:{ docState:"saved" }, computed: { buttonMessage: { get:function (){ switch (this.docState) { case 'saved': return 'Edit' case 'edited': return 'Save' case 'editing': return 'Cancel' }}, set:function(){ }} }
完整案例代码
<body> <div id="demo"> <transition v-on:before-enter="beforeEnter" v-on:enter="enter" v-on:leave="leave" v-bind:css="false"> <button v-bind:key="docState" v-on:click="change"> {{ buttonMessage }} </button> </transition> </div> <script> new Vue({ el: '#demo', data:{ docState:"saved" }, computed: { buttonMessage: { get:function (){ switch (this.docState) { case 'saved': return 'Edit' case 'edited': return 'Save' case 'editing': return 'Cancel' }}, set:function(){ }} }, methods:{ change:function(){ switch (this.buttonMessage) { case 'Edit': this.buttonMessage='Save' this.docState='edited' break; case 'Save': this.buttonMessage='Cancel' this.docState='editing' break; case 'Cancel': this.buttonMessage= 'Edit' this.docState='saved' break;} }, beforeEnter: function (el) { el.style.opacity =0.5 el.style.transformOrigin = 'left' Velocity(el, { opacity: 1, fontSize: '5em' }, { duration: 100 }) el.style.color='red' }, enter: function (el, done) { el.style.color='black' el.style.backgroundColor='yellow' Velocity(el, { opacity: 1, fontSize: '1.4em' }, { duration: 300 }) Velocity(el, { fontSize: '1em' }, { complete: done }) }, leave: function (el, done) { el.style.color='red' Velocity(el, { translateX: '15px', rotateZ: '50deg' }, { duration: 600 }) Velocity(el, { rotateZ: '100deg' }, { loop: 2 }) Velocity(el, { rotateZ: '45deg', translateY: '30px', translateX: '30px', opacity: 0 }, { complete: done }) } } }) </script>
渲染效果
多个组件过渡:动态组件 <component is="">
<div id="demo"> <ul> <li v-on:click="currentTab = 'a'">a</li> <li v-on:click="currentTab = 'b'">b</li> <li v-on:click="currentTab = 'c'">c</li> </ul> <transition appear mode="out-in" v-on:before-enter="beforeEnter" v-on:enter="enter" v-on:leave="leave" v-bind:css="false"> <component v-bind:is="which"></component> </transition> </div>
完整代码:
<script src="https://unpkg.com/vue/dist/vue.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/velocity/1.2.3/velocity.min.js"></script> </head> <body> <div id="demo"> <ul> <li v-on:click="currentTab = 'a'">a</li> <li v-on:click="currentTab = 'b'">b</li> <li v-on:click="currentTab = 'c'">c</li> </ul> <transition appear mode="out-in" v-on:before-enter="beforeEnter" v-on:enter="enter" v-on:leave="leave" v-bind:css="false"> <component v-bind:is="which"></component> </transition> </div> <script> new Vue({ el: '#demo', data:{ currentTab:"a" }, components:{ acom:{ template:` <div>这里是组件A</div> ` }, bcom:{ template:` <div>这里是组件B</div> ` }, ccom:{ template:` <div>这里是组件C</div> ` } }, computed:{ which:{ get:function(){ return this.which=this.currentTab+"com" }, set:function(){}}}, methods:{ enter: function (el, done) { el.style.color='black' el.style.backgroundColor='yellow' Velocity(el, { opacity: 1, fontSize: '1.4em' }, { duration: 300 }) Velocity(el, { fontSize: '1em' }, { complete: done }) } } }) </script>
渲染效果: