首页 > 编程笔记

Vue实现单个元素的过渡动画(超级详细)

Vue.js 提供了 transition 的封装组件,在下列情形中,可以给任何元素和组件添加进入/离开过渡。
使用 transition 控制 <p>hello</p> 动态隐藏和显示,代码如下:
<!DOCTYPE html> 
<html lang="en"> 
<head> 
    <meta charset="UTF-8"> 
    <meta http-equiv="x-UA-Compatible" content="IE=edge"> 
    <meta name="viewport" content="width=device-width, initial-scale=1.0"> 
    <!-- 引入 Vue.js --> 
    <script src="../static/js/Vue.js" type="text/javascript"></script> 
    <title>Demo vue</title> 
</head> 
<body> 
    <!-- 定义Vue的视图 --> 
    <div id="app"> 
        <button v-on:click="show = !show">Toggle</button> 
        <transition name="fade"> 
            <p v-if="show">hello</p> 
        </transition> 
    </div> 
    <script type="text/javascript"> 
        // 创建Vue.js对象 
        const vm = new Vue({ 
            el: "#app", 
            data: { 
                show: true 
            } 
        }); 
    </script> 
    <style type="text/css"> 
        .fade-enter-active, .fade-leave-active { 
            transition: opacity .5s; 
        } 
        .fade-enter, .fade-leave-to { 
            /* .fade-leave-active below version 2.1.8 */ 
            opacity: 0; 
        } 
    </style> 
</body> 
</html>

Vue单元素/组件过渡

在进入/离开的过渡中,会有 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被删除),在过渡/动画完成之后移除。

这 6 个 class 的关系如下图所示:


图 1 transition class关系图

这些过渡中切换的类名,默认为以 v- 为前缀,例如 v-enter、v-enter-active 等,如果给 transition 组件的 name 赋值了,则类名就以 name 的值加-为前缀。

常用的过渡是使用 CSS 过渡的,v-enter-active 和 v-leave-active 可以控制进入/离开过渡的不同的缓和曲线,代码如下:
<!DOCTYPE html> 
<html lang="en"> 
<head> 
    <meta charset="UTF-8"> 
    <meta http-equiv="x-UA-Compatible" content="IE=edge"> 
    <meta name="viewport" content="width=device-width, initial-scale=1.0"> 
    <!-- 引入 Vue.js --> 
    <script src="../static/js/Vue.js" type="text/javascript"></script> 
    <title>Demo vue</title> 
    <style type="text/css"> 
        /* 可以设置不同的进入和离开动画 */ 
        /* 设置持续时间和动画函数 */ 
        .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; 
        } 
    </style> 
</head> 
<body> 
    <!-- 定义Vue的视图 --> 
    <div id="app"> 
        <button @click="show = !show">Toggle render</button> 
        <transition name="slide-fade"> 
            <p v-if="show">hello</p> 
        </transition> 
    </div> 
    <script type="text/javascript"> 
        // 创建Vue.js对象 
        const vm = new Vue({ 
            el: "#app", 
            data: { 
                show: true 
            } 
        }); 
    </script> 
</body> 
</html>
CSS 动画用法同 CSS 过渡一样,其区别是在动画中 v-enter 类名在节点插入 DOM 后不会立即被删除,而是在 animationend 事件触发时被删除,代码如下:
<!DOCTYPE html> 
<html lang="en"> 
<head> 
    <meta charset="UTF-8"> 
    <meta http-equiv="X-UA-Compatible" content="IE=edge"> 
    <meta name="viewport" content="width=device-width, initial-scale=1.0"> 
    <!-- 引入 Vue.js --> 
    <script src="../static/js/Vue.js" type="text/javascript"></script> 
    <title>Demo vue</title> 
    <style> 
        .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); 
            } 
            100% { 
                transform: scale(1); 
            } 
        } 
    </style> 
</head> 
<body> 
    <!-- 定义Vue的视图 --> 
    <div id="app"> 
        <button @click="show = !show">Toggle show</button> 
        <transition name="bounce"> 
            <p v-if="show">测试动画...</p> 
        </transition> 
    </div> 
    <script type="text/javascript"> 
        // 创建Vue.js对象 
        const vm = new Vue({ 
            el: "#app", 
            data: { 
                show: true 
            } 
        }); 
    </script> 
</body> 
</html>
根据需求,开发人员可以使用 transition 的相关属性,自定义过渡类名,这些属性的名称分别是 enter-class、enter-active-class、enter-to-class(2.1.8+)、leave-class、leave-activeclass 和 leave-to-class(2.1.8+)。

它们的优先级高于普通的类名,这对于 Vue.js 的过渡系统和其他第三方 CSS 动画库(如 Animate.css)结合使用十分有用,代码如下:
<!DOCTYPE html> 
<html lang="en"> 
<head> 
    <meta charset="UTF-8"> 
    <meta http-equiv="X-UA-Compatible" content="IE=edge"> 
    <meta name="viewport" content="width=device-width, initial-scale=1.0"> 
    <!-- 引入 Vue.js --> 
    <script src="../static/js/Vue.js" type="text/javascript"></script> 
    <!-- 引入 animate.css --> 
    <link href="../static/css/animate.css" rel="stylesheet" type="text/css"> 
    <title>Demo vue</title> 
</head> 
<body> 
    <!-- 定义Vue的视图 --> 
    <div id="app"> 
        <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> 
    <script type="text/javascript"> 
        // 创建Vue.js对象 
        const vm = new Vue({ 
            el: "#app", 
            data: { 
                show: true 
            } 
        }); 
    </script> 
</body> 
</html>
Vue.js 通过设置 transitioned 或 animationend 事件监听器,来判断元素的过渡或动画是否完成,具体设置哪个监听器监听,Vue.js 可以根据元素的 CSS 规则自动判断。

如果在样式规则中同时存在 transition 和 animation,则 Vue.js 无法知道应该添加哪个监听器。这种情况下,需要开发人员使用 transition 的 type 属性,指定当前需要添加哪个监听器。type 属性的值选项是 transition和animation。前者表示添加过渡监听器,后者表示添加动画监听器。

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

在这种情况下可以用 transition 组件上的 duration prop 定制一个显性的过渡持续时间(以毫秒计),代码如下:
<transition :duration="1000">...</transition> 
也可以定制进入和移出的持续时间,代码如下:
<transition :duration="{enter:500,leave:800}">...</transition> 
在元素的过渡和动画过程中,可以通过 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结合使用时,可能需要在这里设置初始的类名 
    }, 
 
    // 进入过渡中 
    enter: function (el, done) { 
      // 过渡中的逻辑 
      // 当与CSS结合使用时,回调函数done是可选的,可以在过渡完成后调用 
      done(); // 过渡完成后调用 
    }, 
 
    // 进入过渡后 
    afterEnter: function (el) { 
      // 过渡后的逻辑 
    }, 
 
    // 进入过渡被取消时 
    enterCancelled: function (el) { 
      // 当过渡被取消时的逻辑,例如由于条件变化导致过渡未开始 
    }, 
 
    // 离开过渡前 
    beforeLeave: function (el) { 
      // 离开过渡前的准备 
    }, 
 
    // 离开过渡中 
    leave: function (el, done) { 
      // 离开过渡中的逻辑 
      done(); // 过渡完成后调用 
    }, 
 
    // 离开过渡后 
    afterLeave: function (el) { 
      // 离开过渡后的逻辑 
    }, 
 
    // 离开过渡被取消时(通常只在v-show中使用) 
    leaveCancelled: function (el) { 
      // 当离开过渡被取消时的逻辑 
    } 
}
这些钩子函数可以结合 CSS transitions/animations 使用,也可以单独使用。

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

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

推荐阅读