Vue.js进入/离开 & 列表过渡

Posted JavaScript无所不能

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Vue.js进入/离开 & 列表过渡相关的知识,希望对你有一定的参考价值。

Vue.js进入/离开 & 列表过渡(1)

概述

Vue 在插入、更新或者移除 DOM 时,提供多种不同方式的应用过渡效果。
包括以下工具:

  • 在 CSS 过渡和动画中自动应用 class

  • 可以配合使用第三方 CSS 动画库,如 Animate.css

  • 在过渡钩子函数中使用 javascript 直接操作 DOM

  • 可以配合使用第三方 JavaScript 动画库,如 Velocity.js

在这里,我们只会讲到进入、离开和列表的过渡,你也可以看下一节的 管理过渡状态。

单元素/组件的过渡

Vue 提供了 transition 的封装组件,在下列情形中,可以给任何元素和组件添加进入/离开过渡

  • 条件渲染 (使用 v-if)

  • 条件展示 (使用 v-show)

  • 动态组件

  • 组件根节点

这里是一个典型的例子:

<div id="demo">
 <button v-on:click="show = !show">
   Toggle
 </button>
 <transition name="fade">
   <p v-if="show">hello</p>
 </transition>
</div>
new Vue({
 el: '#demo',
 data: {
   show: true
 }
})
.fade-enter-active, .fade-leave-active {
 transition: opacity .5s;
}
.fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */ {
 opacity: 0;
}

Toggle

hello

当插入或删除包含在 transition 组件中的元素时,Vue 将会做以下处理:

  1. 自动嗅探目标元素是否应用了 CSS 过渡或动画,如果是,在恰当的时机添加/删除 CSS 类名。

  2. 如果过渡组件提供了 JavaScript 钩子函数,这些钩子函数将在恰当的时机被调用。

  3. 如果没有找到 JavaScript 钩子并且也没有检测到 CSS 过渡/动画,DOM 操作 (插入/删除) 在下一帧中立即执行。(注意:此指浏览器逐帧动画机制,和 Vue 的 nextTick 概念不同)

过渡的类名

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

  1. v-enter:定义进入过渡的开始状态。在元素被插入之前生效,在元素被插入之后的下一帧移除。

  2. v-enter-active:定义进入过渡生效时的状态。在整个进入过渡的阶段中应用,在元素被插入之前生效,在过渡/动画完成之后移除。这个类可以被用来定义进入过渡的过程时间,延迟和曲线函数。

  3. v-enter-to2.1.8版及以上 定义进入过渡的结束状态。在元素被插入之后下一帧生效 (与此同时 v-enter 被移除),在过渡/动画完成之后移除。

  4. v-leave: 定义离开过渡的开始状态。在离开过渡被触发时立刻生效,下一帧被移除。

  5. v-leave-active:定义离开过渡生效时的状态。在整个离开过渡的阶段中应用,在离开过渡被触发时立刻生效,在过渡/动画完成之后移除。这个类可以被用来定义离开过渡的过程时间,延迟和曲线函数。

  6. v-leave-to2.1.8版及以上 定义离开过渡的结束状态。在离开过渡被触发之后下一帧生效 (与此同时 v-leave 被删除),在过渡/动画完成之后移除。

对于这些在过渡中切换的类名来说,如果你使用一个没有名字的 <transition>,则 v-是这些类名的默认前缀。如果你使用了 <transition name="my-transition">,那么 v-enter 会替换为 my-transition-enter

v-enter-active 和 v-leave-active 可以控制进入/离开过渡的不同的缓和曲线,在下面章节会有个示例说明。

CSS 过渡

常用的过渡都是使用 CSS 过渡。

下面是一个简单例子:

<div id="example-1">
 <button @click="show = !show">
   Toggle render
 </button>
 <transition name="slide-fade">
   <p v-if="show">hello</p>
 </transition>
</div>
new Vue({
 el: '#example-1',
 data: {
   show: true
 }
})
/* 可以设置不同的进入和离开动画 */
/* 设置持续时间和动画函数 */
.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
/* .slide-fade-leave-active for below version 2.1.8 */ {
 transform: translateX(10px);
 opacity: 0;
}

Toggle render

hello

CSS 动画

CSS 动画用法同 CSS 过渡,区别是在动画中 v-enter 类名在节点插入 DOM 后不会立即删除,而是在 animationend 事件触发时删除。

示例:(省略了兼容性前缀)

<div id="example-2">
 <button @click="show = !show">Toggle 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>
new Vue({
 el: '#example-2',
 data: {
   show: true
 }
})
.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);
 }
}

Toggle 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.

自定义过渡的类名

我们可以通过以下特性来自定义过渡类名:

  • enter-class

  • enter-active-class

  • enter-to-class (2.1.8+)

  • leave-class

  • leave-active-class

  • leave-to-class (2.1.8+)

他们的优先级高于普通的类名,这对于 Vue 的过渡系统和其他第三方 CSS 动画库,如 Animate.css 结合使用十分有用。

示例:

<link href="https://cdn.jsdelivr.net/npm/animate.css@3.5.1" rel="stylesheet" type="text/css">

<div id="example-3">
 <button @click="show = !show">
   Toggle render
 </button>
 <transition
   name="custom-classes-transition"
   enter-active-class="animated tada"
   leave-active-class="animated bounceOutRight"
 >
   <p v-if="show">hello</p>
 </transition>
</div>
new Vue({
 el: '#example-3',
 data: {
   show: true
 }
})

Toggle render

hello

同时使用过渡和动画

Vue 为了知道过渡的完成,必须设置相应的事件监听器。它可以是 transitionend 或 animationend ,这取决于给元素应用的 CSS 规则。如果你使用其中任何一种,Vue 能自动识别类型并设置监听。

但是,在一些场景中,你需要给同一个元素同时设置两种过渡动效,比如 animation 很快的被触发并完成了,而 transition 效果还没结束。在这种情况中,你就需要使用 type特性并设置 animation 或 transition 来明确声明你需要 Vue 监听的类型。

显性的过渡持续时间

2.2.0 新增

在很多情况下,Vue 可以自动得出过渡效果的完成时机。默认情况下,Vue 会等待其在过渡效果的根元素的第一个 transitionend 或 animationend 事件。然而也可以不这样设定——比如,我们可以拥有一个精心编排的一系列过渡效果,其中一些嵌套的内部元素相比于过渡效果的根元素有延迟的或更长的过渡效果。

在这种情况下你可以用 <transition> 组件上的 duration 属性定制一个显性的过渡持续时间 (以毫秒计):

<transition :duration="1000">...</transition>

你也可以定制进入和移出的持续时间:

<transition :duration="{ enter: 500, leave: 800 }">...</transition>

JavaScript 钩子

可以在属性中声明 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 结合时使用
 enter: function (el, done) {
   // ...
   done()
 },
 afterEnter: function (el) {
   // ...
 },
 enterCancelled: function (el) {
   // ...
 },

 // --------
 // 离开时
 // --------

 beforeLeave: function (el) {
   // ...
 },
 // 此回调函数是可选项的设置
 // 与 CSS 结合时使用
 leave: function (el, done) {
   // ...
   done()
 },
 afterLeave: function (el) {
   // ...
 },
 // leaveCancelled 只用于 v-show 中
 leaveCancelled: function (el) {
   // ...
 }
}

这些钩子函数可以结合 CSS transitions/animations 使用,也可以单独使用。

当只用 JavaScript 过渡的时候, 在 enter 和 leave 中,回调函数 done 是必须的 。否则,它们会被同步调用,过渡会立即完成。

推荐对于仅使用 JavaScript 过渡的元素添加 v-bind:css="false",Vue 会跳过 CSS 的检测。这也可以避免过渡过程中 CSS 的影响。

一个使用 Velocity.js 的简单例子:

<!--
Velocity 和 jQuery.animate 的工作方式类似,也是用来实现 JavaScript 动画的一个很棒的选择
-->
<script src="https://cdnjs.cloudflare.com/ajax/libs/velocity/1.2.3/velocity.min.js"></script>

<div id="example-4">
 <button @click="show = !show">
   Toggle
 </button>
 <transition
   v-on:before-enter="beforeEnter"
   v-on:enter="enter"
   v-on:leave="leave"
   v-bind:css="false"
 >
   <p v-if="show">
     Demo
   </p>
 </transition>
</div>
new Vue({
 el: '#example-4',
 data: {
   show: false
 },
 methods: {
   beforeEnter: function (el) {
     el.style.opacity = 0
     el.style.transformOrigin = 'left'
   },
   enter: function (el, done) {
     Velocity(el, { opacity: 1, fontSize: '1.4em' }, { duration: 300 })
     Velocity(el, { fontSize: '1em' }, { complete: done })
   },
   leave: function (el, done) {
     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 })
   }
 }
})

Toggle

初始渲染的过渡

可以通过 appear 特性设置节点在初始渲染的过渡

<transition appear>
 <!-- ... -->
</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 。最常见的多标签过渡是一个列表和描述这个列表为空消息的元素:

<transition>
 <table v-if="items.length > 0">
   <!-- ... -->
 </table>
 <p v-else>Sorry, no items found.</p>
</transition>

可以这样使用,但是有一点需要注意:

当有相同标签名的元素切换时,需要通过 key 特性设置唯一的值来标记以让 Vue 区分它们,否则 Vue 为了效率只会替换相同标签内部的内容。即使在技术上没有必要,给在 <transition> 组件中的多个元素设置 key 是一个更好的实践。

示例:

<transition>
 <button v-if="isEditing" key="save">
   Save
 </button>
 <button v-else key="edit">
   Edit
 </button>
</transition>

在一些场景中,也可以通过给同一个元素的 key 特性设置不同的状态来代替 v-if 和 v-else,上面的例子可以重写为:

<transition>
 <button v-bind:key="isEditing">
   {{ isEditing ? 'Save' : 'Edit' }}
 </button>
</transition>

使用多个 v-if 的多个元素的过渡可以重写为绑定了动态属性的单个元素过渡。例如:

<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>

可以重写为:

<transition>
 <button v-bind:key="docState">
   {{ buttonMessage }}
 </button>
</transition>
// ...
computed: {
 buttonMessage: function () {
   switch (this.docState) {
     case 'saved': return 'Edit'
     case 'edited': return 'Save'
     case 'editing': return 'Cancel'
   }
 }
}



以上是关于Vue.js进入/离开 & 列表过渡的主要内容,如果未能解决你的问题,请参考以下文章

Vue.js 过渡

Vue.js 过渡

Vue.js 过渡

Vue.js 状态过渡

VUE2中文文档:进入离开和列表过度

过渡模式