08-Vue过渡动画

407 阅读8分钟

一、基本介绍

1,<transition> 组件

(1)如果某个元素或者组件需要使用过渡动画效果,只需使用 vue 提供的 <transition> 组件将其包裹起来封装成过渡组件。
(2)Vue 只有在插入,更新或者移除 DOM 元素时才会应用过渡效果,例如:

  • v-if(条件渲染)
  • v-show(条件展示)
  • 动态组件
  • 在组件的根节点上,并且被 vue 实例 DOM 方法触发。比如使用 appendTo 方法把组件添加到某个根节点上

2,过渡效果实现方式

过渡效果具体的实现方式分为如下几种:

  • 在 CSS 过渡和动画中自动应用 class
  • 可以配合使用第三方 CSS 动画库,如 Animate.css
  • 在过渡钩子函数中使用 JavaScript 直接操作 DOM
  • 可以配合使用第三方 JavaScript 动画库,如 Velocity.js

3,动画执行逻辑

封装成过渡组件的元素被插入或者删除时,vue 将会做如下事情:

  • 首先查找目标元素是否有 CSS 过渡或者动画,如果有就在适当的时候进行处理。
  • 如果过渡组件设置了 JavaScript 钩子函数,vue 会在相应阶段调用钩子函数。
  • 如果以上两者都没有,DOM 操作(插入或者删除)就在下一帧立即执行。

二、结合 CSS 实现动画效果

1,基本用法

(1)在组件过渡过程中,会有如下六个 CSS 类名进行切换:

  • v-enter:进入过渡的开始状态,元素被插入时生效,只应用一帧后立刻删除。
  • v-enter-active:进入过渡的结束状态,元素被插入时就生效,在过渡过程完成后移除。
  • v-enter-to:定义进入过渡的结束状态。在元素被插入之后下一帧生效 (与此同时 v-enter 被移除),在过渡/动画完成之后移除。(2.1.8 版加入)
  • v-leave:离开过渡的开始状态,元素被删除时触发,只应用一帧后立刻删除。
  • v-leave-active:离开过渡的结束状态,元素被删除时生效,离开过渡完成后被删除。
  • v-leave-to:定义离开过渡的结束状态。在离开过渡被触发之后下一帧生效 (与此同时 v-leave 被删除),在过渡/动画完成之后移除。(2.1.8 版加入)

(2)下面是一个简单的样例:

  • 通过点击按钮对下方文字进行显示或隐藏(交替)。
  • 在显示或隐藏的过程中,会有淡入淡出的效果。

// HTML
<div id="app">
    <button @click="show = !show">显示/隐藏</button>
    <transition>
        <div v-if="show">hello</div>
    </transition>
</div>
// CSS
.v-enter{
    opacity: 0;
}
.v-enter-to{
    opacity: 1;
}
.v-enter-active{
    transition: all 1s;
}
.v-leave{
    opacity: 1;
}
.v-leave-to{
    opacity: 0;
}
.v-leave-active{
    transition: all 1s;
}
// JS
<script>
    new Vue({
        el: '#app',
        data: {
            show: true
        }
    });
</script>

注意

  1. transition 中只能放 一个 元素, 多个元素无效
    如果想给多个元素添加过渡动画, 那么就必须创建多个transition组件
    比如这里, 在一个transition组件放了两个div就会报错, 这个时候应该把两个div分别放在两个transition中
<transition>
    <div v-if="show">hello</div>
    <div v-if="show">hello</div>
</transition>
  1. 初始动画设置
    默认情况下第一次进入的时候没没有动画的。
    如果想一进来就有动画, 我们可以通过给transition添加appear属性的方式, 告诉Vue第一次进入就需要显示动画
<transition appear>
    <div v-if="show">hello</div>
</transition>
  1. 给多个不同的元素指定不同的动画
    如果有多个不同的元素需要执行不同的过渡动画,那么我们可以通过给transition指定name的方式 来指定"进入之前/进入之后/进入过程中, 离开之前/离开之后/离开过程中"对应的类名 来实现不同的元素执行不同的过渡动画
// HTML
<transition appear name="fade">
    <div v-if="show">hello</div>
</transition>
// CSS
.fade-enter-active, .fade-leave-active{
    transition: opacity 1s;
}
.fade-enter, .fade-leave-to{
    opacity: 0;
}

2,自定义过渡类名

(1)前面的样例中六个过渡类名都是根据 transition 的 name 属性自动生成的,我们也可以通过 enter-class、enter-active-class、enter-to-class、leave-class、leave-active-class、leave-to-class 这六个属性来分别定义这六个类名。

<transition name="fade"
  enter-class="fade-in-enter"
  enter-active-class="fade-in-active"
  enter-to-class="fad-in-end"
  leave-class="fade-out-enter"
  leave-active-class="fade-out-active"
  leave-to-class="fade-out-end">
  
  <div v-if="show">hello</div>
</transition>

(2)自定义过渡类名的优先级高于普通的类名,这对于 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>

3,通过 CSS 动画(animation)实现过渡效果

组件过渡效果不但可以通过 CSS 过渡实现,还可以通过 CSS 动画(关键帧动画)实现。
(1)效果

  • 通过点击按钮让下方图片进行显示或隐藏(交替)。
  • 在显示或隐藏的过程中,图片除了会有淡入淡出的效果,还会左右摇摆逐渐放大(或逐渐缩小)

(2)样例代码

// HTML
<template>
  <div id="app">
    <button @click="show = !show">显示/隐藏</button>
    <br>
    <transition name="fold">
      <img v-show="show" src="./assets/logo.png">
    </transition>
  </div>
</template>
// JS
<script>
export default {
  name: 'App',
  data: function(){
    return {
      show: true
    }
  }
}
</script>
// CSS
<style>
 .fold-enter-active {
   animation-name: fold-in;
   animation-duration: .5s;
 }
 .fold-leave-active {
   animation-name: fold-out;
   animation-duration: 5.5s;
 }
 @keyframes fold-in {
   0% {
     opacity: 0;
     transform: scale(0.7) rotate(0deg);
   }
   33% {
     opacity: 0.33;
     transform: scale(0.8) rotate(5deg);
   }
   67% {
     opacity: 0.67;
     transform: scale(0.9) rotate(-5deg);
   }
   100% {
     opacity: 1.0;
     transform: scale(1.0) rotate(0deg);
   }
 }
 @keyframes fold-out {
   0% {
     opacity: 1.0;
     transform: scale(1.0) rotate(0deg);
   }
   33% {
     opacity: 0.67;
     transform: scale(0.9) rotate(-5deg);
   }
   67% {
     opacity: 0.33;
     transform: scale(0.8) rotate(5deg);
   }
   100% {
     opacity: 0;
     transform: scale(0.7) rotate(0deg);
   }
 }
</style>

4,显示地指定过渡持续时间

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

(1)我们可以用<transition>组件上的 duration 属性定制一个显性的过渡持续时间 (以毫秒计):

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

(2)也可以分别指定进入和移出的持续时间:

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

三、JavaScript 钩子

我们先来看一个案例:


在这个案例中, div在执行完最终动画后会直接跳回到原点.
可以发现通过 transition+类名 的方式确实能够实现过渡效果 ,但是实现的过渡效果并不能保存动画之后的状态。 因为Vue内部的实现是在过程中动态绑定类名, 过程完成之后删除类名 正是因为删除了类名, 所以不能保存最终的效果。想要解决这个问题, 就需要使用JavaScript钩子.

1,基本介绍

我们可以在 transition 属性中声明 JavaScript 钩子,这些钩子函数可以结合 CSS transitions/animations 使用,也可以单独使用。

// HTML
<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>
// JS
methods: {
    // 进入动画之前
    beforeEnter: function (el) {
        // ...
    },
    // 进入动画执行过程中
    enter: function (el, done) {
        // 当与 CSS 结合使用时, 回调函数 done 是可选的
        done();
    },
    // 进入动画完成之后
    afterEnter: function (el) {
        // ...
    },
    // 进入动画被取消
    enterCancelled: function (el) {
        // ...
    },


    // 离开动画之前    
    beforeLeave: function (el) {
        // ...
    },
    // 离开动画执行过程中
    leave: function (el, done) {
        // 当与 CSS 结合使用时, 回调函数 done 是可选的
        done();
    },
    // 离开动画完成之后
    afterLeave: function (el) {
        // ...
    },
    // 离开动画被取消
    // leaveCancelled 只用于 v-show 中
    leaveCancelled: function (el) {
        // ...
    }
}

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

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