过渡和动画基础

过渡和动画概述

Vue 在插入、更新或者移除 DOM 时,提供了多种过渡效果。

过渡,就是从一个状态向另外一个状态插入值,新的状态替换了旧的状态。

Vue 提供了内置的过渡封装组件 transition,可以结合 CSS 动画 @keyframes 实现动画效果。

transition 组件

Vue 提供了内置的过渡封装组件,即 transition 组件。

transition 组件的基本用法:

<!-- 过渡类名前缀:替换过渡类的v-前缀 -->
<transition name="过渡类名前缀">
    <!-- 需要添加过渡的元素 -->
    <!-- 组件在同一时间内只能有一个元素显示 -->
    <div></div>
</transition>

Vue 为 transition 标签内部的元素提供了 3 个进入过渡的类和 3 个离开过渡的类。

transition 组件的基本过渡类:

过渡状态过渡类型描述
进入(enter)v-enter进入过渡的开始状态,作用于开始的一帧
v-enter-active进入过渡生效时的状态,作用于整个过程
v-enter-to进入过渡的结束状态,作用于结束的一帧
离开(leave)v-leave离开过渡的开始状态,作用于开始的一帧
v-leave-active离开过渡生效时的状态,作用于整个过程
v-leave-to离开过渡的结束状态,作用于结束的一帧


示例:transition 组件

入口页面(index.html):

<style>
    /** 图形的初始状态 **/
    .box {
        width: 200px;
        height: 50px;
        background-color: blue;
    }

    /** 进入和离开的过程 **/
    .box-enter-active, .box-leave-active {
        transition: width 3s;
    }

    /** 进入的初始状态和离开的结束状态 **/
    .box-enter, .box-leave-to {
        width: 0px;
    }

    /** 进入的结束状态和离开的初始状态 **/
    .box-enter-to, .box-leave {
        width: 200px;
    }
</style>

<div id="app">
    <button @click="toggle">显示/隐藏</button><br><br>
    <transition name="box">
        <div class="box" v-if="show"></div>
    </transition>
</div>

<script>
    let vm = new Vue({
        el: '#app',
        data: {
            show: true
        },
        methods: {
            toggle() {
                this.show = !this.show
            }
        }
    })
</script>

示例效果:


自定义类名

transition 组件提供了一系列自定义类名属性,允许使用自定义的类名,且自定义类名的优先级高于普通类名,与第三方库结合实现更精美的过渡效果。

自定义类名的基本用法:

<transition 自定义类名属性="类名">
    <div></div>
</transition>

transition 组件的基本自定义类名属性:

过渡状态过渡类型描述
进入(enter)enter-class同 v-enter
enter-active-class同 v-enter-active
enter-to-class同 v-enter-to
离开(leave)leave-class同 v-leave
leave-active-class同 v-leave-active
leave-to-class同 v-leave-to

结合 animate.css 实现过渡效果

animate.css 是一个跨浏览器的 CSS3 动画库,其内置了很多经典的 CSS3 动画,通过 transition 组件的自定义类名和 animate.css 动画库结合,可以实现精美的过渡效果。

animate.css 文档:https://www.animate.style/


示例:结合 animate.css 实现过渡效果

入口页面(index.html):

<link rel="stylesheet" href="animate.css">

<div id="app">
    <button @click="toggle">显示/隐藏</button><br><br>
    <transition enter-active-class="animate__animated animate__backInDown"
        leave-active-class="animate__animated animate__backOutDown">
        <div class="box" v-if="show"></div>
    </transition>
</div>

示例效果:


使用 appear 初始渲染过渡效果

过渡效果都是在事件处理方法中控制的,在元素初始渲染时,并不会触发过渡效果。

appear 属性用于设置元素初始渲染时,给元素添加过渡效果。

appear 属性的基本用法:

<transition appear appear属性="值">
    <div></div>
</transition>

transition 组件的基本 appear 属性:

名称描述
appear-class初始时的 class 样式
appear-active-class应用在整个过渡过程中的 class 样式
appear-to-class过渡完成的 class 样式

示例:使用 appear 初始渲染过渡效果

入口页面(index.html):

<transition
        appear
        appear-active-class="animate__animated animate__swing"
        enter-active-class="animate__animated animate__backInDown" 
        leave-active-class="animate__animated animate__backOutDown">
    <div class="box" v-if="show"></div>
</transition>

示例效果:


使用 @keyframes 实现动画

@keyframes 用于声明关键帧创建动画。

@keyframes 规则创建动画,就是将一套 CSS 样式逐步演变成另一套样式,在创建动画过程中,可以多次改变 CSS 样式。

@keyframes 的基本用法:

@keyframes 动画名称 {
    from {
        /* 开始时,即0%时的CSS样式 */
    }
    xx% {
        /* xx%时的CSS样式 */
    }
    to {
        /* 结束时,即100%时的CSS样式 */
    }
}

示例:使用 @keyframes 实现动画

入口页面(index.html):

<style>
    .circle {
        width: 100px;
        height: 100px;
        background-color: red;
        border-radius: 50%;
    }

    @keyframes ami {
        0% {
            transform: scale(0);
            background-color: red;
        }
        20% {
            transform: scale(1);
            background-color: burlywood;
        }
        50% {
            transform: scale(1.5);
            background-color: blueviolet;
        }
        100% {
            transform: scale(1);
            background-color: burlywood;
        }
    }

    /** 进入过程 **/
    .bounce-enter-active {
        animation: ami 5s;
    }

    /** 离开过程 **/
    .bounce-leave-active {
        animation: ami 5s;
    }
</style>

<div id="app">
    <button @click="toggle">显示/隐藏</button>
    <br><br>
    <transition name="bounce">
        <div class="circle" v-if="show"></div>
    </transition>
</div>

<script>
    let vm = new Vue({
        el: '#app',
        data: {
            show: false
        },
        methods: {
            toggle() {
                this.show = !this.show
            }
        }
    })
</script>

示例效果:


使用钩子函数实现动画

Vue 中除了使用 CSS 动画外,还可以借助 JavaScript 来完成动画,transition 组件中定义了一些动画钩子函数,用来实现动画。

钩子函数的基本用法:

<transition @钩子函数="触发方法">
    <div></div>
</transition>

<script>
    let vm = new Vue({
        el: '#app',
        methods: {
            // beforeEnter等触发方法时,可以传入参数el
            // el:transition包裹的元素
            beforeEnter(el) {
                // ...
            },
            // enter和leave触发方法时,可以传入参数done
            enter(el, done) {
                // ...
                // 调用done()告诉Vue动画结束
                done()
            }
        }
    })
</script>

transition 组件的基本钩子函数:

名称描述
before-enter入场前
enter入场时
after-enter入场后
enter-cancelled取消入场时
before-leave出场前
leave出场时
after-leave出场后
leave-cancelled取消出场

示例:使用钩子函数实现动画

入口页面(index.html):

<div id="app">
    <button @click="toggle">显示/隐藏</button><br><br>
    <transition @enter="enter" @leave="leave">
        <div class="box" v-if="show"></div>
    </transition>
</div>

<script>
    let vm = new Vue({
        el: '#app',
        data: {
            show: true
        },
        methods: {
            toggle() {
                this.show = !this.show
            },
            enter(el, done) {
                el.style.width = '200px'
                done()
            },
            leave(el, done) {
                el.style.width = '0px'
                done()
            }
        }
    })
</script>

示例效果:


结合 Velocity.js 实现动画

Velocity.js 是一个简单易用、高性能且功能丰富的轻量级 JavaScript 动画库,其拥有颜色动画、转换动画、循环、缓动、SVG 动画和滚动动画等特色功能,通过 transition 组件的钩子函数和 Velocity.js 动画库结合,可以实现精美的动画效果。

Velocity.js 文档:http://www.velocityjs.org/


示例:结合 Velocity.js 实现动画

入口页面(index.html):

<script src="1.5.0/velocity.js"></script>

<div id="app">
    <button @click="toggle">显示/隐藏</button><br><br>
    <transition @enter="enter">
        <div class="box" v-if="show"></div>
    </transition>
</div>

<script>
    let vm = new Vue({
        el: '#app',
        data: {
            show: true
        },
        methods: {
            toggle() {
                this.show = !this.show
            },
            enter(el, done) {
                Velocity(el, "fadeIn", { duration: 1500 })
                done()
            }
        }
    })
</script>

示例效果:


多个元素过渡

不同标签名元素的过渡

不相同标签名元素可以使用 v-if 和 v-else 来进行过渡。


示例:不同标签名元素的过渡

入口页面(index.html):

<style>
    .fade-enter-active, .fade-leave-active {
        transition: opacity 3s;
    }

    .fade-enter, .fade-leave-to {
        opacity: 0;
    }

    .fade-enter-to, .fade-leave {
        opacity: 1;
    }
</style>

<div id="app">
    <button @click="toggle">切换登录/注册页面</button>
    <br>
    <transition name="fade">
        <h1 v-if="isLogin">登录页面</h1>
        <h2 v-else>注册页面</h2>
    </transition>
</div>

<script>
    let vm = new Vue({
        el: '#app',
        data: {
            isLogin: true
        },
        methods: {
            toggle() {
                this.isLogin = !this.isLogin
            }
        }
    })
</script>

示例效果:


相同标签名元素的过渡

当有相同标签名的元素切换时,需要通过 key 特性设置唯一值来标记,从而让 Vue 区分它们。如果没有为元素设置 key,Vue 为了效率只会替换相同标签中的内容。


示例:相同标签名元素的过渡

入口页面(index.html):

<style>
    .fade-enter-active, .fade-leave-active {
        transition: opacity 3s;
    }

    .fade-enter, .fade-leave-to {
        opacity: 0;
    }

    .fade-enter-to, .fade-leave {
        opacity: 1;
    }
</style>

<div id="app">
    <button @click="toggle">切换登录/注册按钮</button>
    <br><br>
    <transition name="fade">
        <button v-if="isLogin" key="login">登录按钮</button>
        <button v-else key="register">注册按钮</button>
    </transition>
</div>

<script>
    let vm = new Vue({
        el: '#app',
        data: {
            isLogin: true
        },
        methods: {
            toggle() {
                this.isLogin = !this.isLogin
            }
        }
    })
</script>

示例效果:


过渡模式

新旧两个元素参与过渡的时候,新元素的进入和旧元素的离开会同时触发,这是因为 transition 组件的默认行为进入和离开同时发生了。

如果要求离开的元素完全消失后,进入的元素再显示出来,可以使用 transition 提供的过渡模式属性,来解决当一个元素离开后,另一个元素进来时发生的位置的闪动或阻塞问题。

过渡模式的基本用法:

<transition mode="过渡模式">
    <div></div>
</transition>

transition 组件的基本过渡模式:

名称描述
in-out(默认)新元素先进行过渡进入,完成之后当前元素过渡离开
out-in当前元素先进行过渡离开,完成之后新元素过渡进入

示例:过渡模式

入口页面(index.html):

<transition name="fade" mode="out-in">
    <button v-if="isLogin" key="login">登录按钮</button>
    <button v-else key="register">注册按钮</button>
</transition>

示例效果:


多个组件过渡

多个组件之间的过渡,不需要使用 key 特性,只需要使用动态组件即可。

动态组件需要通过 Vue 中的 component 元素绑定 is 属性来实现多组件的过渡。

多个组件过渡的基本用法:

<transition>
    <component :is="组件名称"></component>
</transition>

示例:多个组件过渡

入口页面(index.html):

<style>
    .fade-enter-active, .fade-leave-active {
        transition: opacity 3s;
    }

    .fade-enter, .fade-leave-to {
        opacity: 0;
    }

    .fade-enter-to, .fade-leave {
        opacity: 1;
    }
</style>

<div id="app">
    <button @click="compontentName='login'">登录</button>
    <button @click="compontentName='register'">注册</button>
    <br><br>
    <transition name="fade" mode="out-in">
        <component :is="compontentName"></component>
    </transition>
</div>

<template id="login">
    <span>我是登录组件</span>
</template>

<template id="register">
    <span>我是注册组件</span>
</template>

<script>
    Vue.component('login', {
        template: '#login'
    })
    Vue.component('register', {
        template: '#register'
    })

    let vm = new Vue({
        el: '#app',
        data: {
            compontentName: ''
        }
    })
</script>

示例效果:


列表过渡

列表过渡需要使用 v-for 和 transition-group 组件来实现。

  • 列表的每一项都需要进行过渡,列表在循环时要给每一个列表项添加唯一的 key 属性。
  • 在进行列表过渡时 , 过渡模式不可用。

列表过渡的基本用法:

<!--
    <transition-group>:相当于给每一个被包裹的li元素在外面添加了<transition>
    tag:渲染的外层标签
-->
<transition-group name="list" tag="ul">
    <li v-for="item in items" :key="item">
    {{item}}
    </li>
</transition-group>

示例:列表过渡

入口页面(index.html):

<style>
    .list-enter-active, .list-leave-active {
        transition: all 2s;
    }
    .list-enter, .list-leave-to {
        opacity: 0;
    }
    .list-enter-to, .list-leave {
        opacity: 1;
    }
</style>

<div id="app">
    <button @click="add">随机插入一个数字</button>
    <button @click="remove">随机移除一个数字</button>
    <transition-group name="list" tag="ul">
      <li v-for="item in items" :key="item">
        {{item}}
      </li>
    </transition-group>
</div>

<script>
    let vm = new Vue({
        el: '#app',
        data: {
            items: [1, 2, 3, 4, 5],
            nextNum: 6
        },
        methods: {
            add () {
                this.items.push(this.nextNum++)
            },
            remove () {
                this.items.splice(-1, 1)
            }
        }
    })
</script>

示例效果: