Vue 入门到实战 五

第5章 过渡与动画

目录

[5.1 单元素/组件过渡](#5.1 单元素/组件过渡)

[5.1.1 过渡class](#5.1.1 过渡class)

[5.1.2 CSS 过渡](#5.1.2 CSS 过渡)

[5.1.3 CSS 动画](#5.1.3 CSS 动画)

[5.1.4 同时使用过渡和动画](#5.1.4 同时使用过渡和动画)

[5.1.5 JavaScript 钩子方法](#5.1.5 JavaScript 钩子方法)

[5.2 多元素/组件过渡](#5.2 多元素/组件过渡)

[5.2.1 多元素过渡](#5.2.1 多元素过渡)

[5.2.2 多组件过渡](#5.2.2 多组件过渡)

[5.3 列表过渡](#5.3 列表过渡)

[5.3.1 列表的普通过渡](#5.3.1 列表的普通过渡)

[5.3.2 列表的平滑过渡](#5.3.2 列表的平滑过渡)

[5.3.3 列表的变换过渡](#5.3.3 列表的变换过渡)

[5.3.4 多维列表的过渡](#5.3.4 多维列表的过渡)


5.1 单元素/组件过渡

Vue在插入、更新或者移除 DOM 时,提供多种不同方式的过渡效果(一个淡入淡出的效果)。在条件渲染(使用v-if)、条件展示(使用v-show)、动态组件、组件根节点等情形中,可以给任何元素和组件添加进入/离开过渡。Vue提供了内置的过渡封装组件<transition>,该组件用于包裹要实现过渡效果的组件。具体语法如下:

<transition name = "过渡名称">

<!--要实现过渡效果的组件-->

</transition>

5.1.1 过渡class

总结起来就分为 进入 和 离开 动画的 初始状态、生效状态、结束状态,具体如下:

  • v-enter-from
    • 进入 动画的 起始状态
    • 在元素插入之前添加,在元素插入完成后的 下一帧移除
  • v-enter-active
    • 进入 动画的 生效状态,应用于整个进入动画阶段
    • 在元素被插入之前添加,在过渡或动画完成之后移除
    • 这个 class 可以被用来定义进入动画的持续时间、延迟与速度曲线类型
  • v-enter-to
    • 进入 动画的 结束状态
    • 在元素插入完成后的下一帧被添加 (也就是 v-enter-from 被移除的同时),在过渡或动画完成之后移除
  • v-leave-from
    • 离开 动画的 起始状态
    • 在离开过渡效果被触发时立即添加,在一帧后被移除
  • v-leave-active
    • 离开 动画的 生效状态,应用于整个离开动画阶段
    • 在离开过渡效果被触发时立即添加,在 过渡或动画完成之后移除
    • 这个 class 可以被用来定义离开动画的持续时间、延迟与速度曲线类型
  • v-leave-to
    • 离开 动画的 结束状态
    • 在一个离开动画被触发后的 下一帧 被添加 (即 v-leave-from 被移除的同时),在 过渡或动画完成之后移除

其中的 v 前缀是允许修改的,可以 <Transition> 组件传一个 name 的 prop 来声明一个过渡效果名,如下就是将 v 前缀修改为 modal 前缀:

javascript 复制代码
<div id="app">
    <button @click="show = !show">点我,我就渐渐的离开、渐渐的来。</button>
    <transition name="fade">
        <p v-show="show" :style="styleobj">动画实例</p>
    </transition>
</div>
<script src="js/vue.global.js"></script>
<script>
    const app = Vue.createApp({
        data() {
            return {
                show: true,
                styleobj: {
                    fontSize: '30px',
                    color: 'red'
                }
            }
        }
    })
    app.mount('#app')
</script>
<style>
    .fade-enter-active,
    .fade-leave-active {
        transition: opacity 2s ease;
    }
    .fade-enter-from,
    .fade-leave-to {
        opacity: 0;
    }
</style>

5.1.2 CSS 过渡

常用的过渡一般都是CSS过渡。CSS过渡,顾名思义也就是使用过渡class定义CSS实现过渡效果。

【例5-2】使用过渡class定义图片的进入和离开动画效果,运行效果如图5.2所示。

javascript 复制代码
<div id="app">
    <button @click="show = !show">
        切换显示图片
    </button>
    <transition name="slide-img">
        <p v-if="show">
            <img src="99.jpg"/>
        </p>
    </transition>
</div>
<script src="js/vue.global.js"></script>
<script>
    const app = Vue.createApp({
        data() {
            return {
                show: true
            }
        }
    })
    app.mount('#app')
</script>
<style>
    .slide-img-enter-active {	    
        transition: all 5s ease-out; //ease-out 规定以慢速结束的过渡,进入界面需要延迟5秒才会显示整个图片
    }
    .slide-img-leave-active {
        transition: all 0.8s cubic-bezier(1, 0.5, 0.8, 1);
    }
    .slide-img-enter-from,
    .slide-img-leave-to {  
        transform: rotateX(45deg);   //X轴45°旋转
        transform: rotateY(45deg);  
        transform: rotateZ(45deg);  
        transform: rotate3d(1, 1, 1, 45deg);
        opacity: 0;
    }
</style>

5.1.3 CSS 动画

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

  1. 基本概念
    CSS 动画由两个主要部分组成:

@keyframes:定义动画的关键帧,指定动画在不同时间点的状态。
animation:应用于元素的属性,控制动画的播放、持续时间、延迟等。
2. @keyframes 关键帧的定义
@keyframes 定义了动画的各个关键帧,控制元素的从一个状态到另一个状态的转换过程。

【例5-3】使用@keyframes定义图片的动画规则,运行效果如图5.3所示。

javascript 复制代码
<div id="app">
    <button @click="show = !show">
        切换图片动画
    </button>
    <transition name="bounce-img">
        <p v-if="show">
            <img src="99.jpg" />
        </p>
    </transition>
</div>
<script src="js/vue.global.js"></script>
<script>
    const app = Vue.createApp({
        data() {
            return {
                show: true
            }
        }
    })
    app.mount('#app')
</script>
<style>
    .bounce-img-enter-active {
        animation: bounce-in 0.8s;
    }
    .bounce-img-leave-active {
        animation: bounce-in 0.8s reverse;
    }
    @keyframes bounce-in {
        0% {
            transform: scale(0);
        }
        50% {
            transform: scale(1.25);
        }
        100% {
            transform: scale(1);
        }
    }
</style>

5.1.4 同时使用过渡和动画

在一些应用场景中,需要给一个元素同时设置过渡和动画,比如 animation 很快的被触发并完成,而 transition 效果还没结束。这时,需要使用 type属性并设置 animation 或 transition值来明确声明需要Vue监听的类型。

在<transition> 组件的 duration prop上可显性定义过渡持续时间(以毫秒计),比如,定制进入和移出的持续时间:<transition :duration="{ enter:1000, leave: 2000 }">...</transition>。还可以通过appear属性设置DOM节点在初始渲染的过渡(页面加载的初次过渡动画):<transition appear>...</transition>。

javascript 复制代码
<head>
    <!--使用第三方动画库-->
    <link rel="stylesheet" type="text/css" href="https://cdn.bootcss.com/animate.css/3.7.2/animate.min.css">
</head>
<!--使用type='transition'以transition过渡时长为准,
即fade-enter-active和fade-leave-active定义的时长,
或者绑定属性:duration="{enter:5000,leave:5000}"自定义时长,
type='transition'与:duration="5000" 二选一;
使用animate.css必须使用vue的 enter-active-class 和 leave-active-class,
后面紧跟animated类名和想使用的动画名称;
appear,appear-active-class实现页面加载的初次动画(初始渲染过渡)。-->
<div id="app">
    <transition name='fade' appear :duration="{enter:5000,leave:5000}"
    enter-active-class='animated swing fade-enter-active' 
    leave-active-class='animated shake fade-leave-active'
    appear-active-class='animated swing' 
   >
        <div v-if='show'>hello world</div>
    </transition>
    <button @click="handleClick">切换显示</button>
</div>
<script src="js/vue.global.js"></script>
<script>
    const app = Vue.createApp({
        data() {
            return {
                show: true
            }
        },
        methods: {
            handleClick() {
                this.show = !this.show
            }
        }
    })
    app.mount('#app')
</script>
<style type="text/css">
    .fade-enter,
    .fade-leave-to {
        opacity: 0;
    }
    .fade-enter-active,
    .fade-leave-active {
        transition: opacity 5s;
    }
    div {
        font-size: 40px;
        margin: 50px auto;
    }
</style>

5.1.5 JavaScript 钩子方法

javascript 复制代码
<div id="app">
    <button @click="show = !show" class="btn">
        添加到购物车
    </button>
    <transition @before-enter="beforeEnter" @enter="enter" @after-enter="afterEnter">
        <div v-if="show" class="ball"></div>
    </transition>
</div>
<script src="js/vue.global.js"></script>
<script>
    const app = Vue.createApp({
        data() {
            return {
                show: false
            }
        },
        methods: {
            //el表示将要执行动画的DOM元素,beforeEnter表示动画开始前
            beforeEnter(el) {
                //设置元素动画开始前的起始位置
                el.style.transform = "translate(0, 0)"
            },
            enter(el, done) {
                //返回元素的宽度,强制动画刷新
                el.offsetWidth
                //设置元素动画开始之后的样式,设置完成之后状态
                el.style.transform = "translate(200px, -200px)"
                el.style.transition = "all 3s cubic-bezier(0, 0.54, 0.55, 1.18)"
                //done这里代表afterEnter方法的引用
                done()
            },
            afterEnter(el) {
                //动画完成后,调用该方法
                this.show = !this.show
            }
        }
    })
    app.mount('#app')
</script>
<style>
   .ball {
       width: 30px;
       height: 30px;
       border-radius: 50%;
       background-color: green;
       position: absolute;
       z-index: 99;
       top: 200px;
       left: 100px;
   }
   .btn {
       position: absolute;
       top: 200px;
   }
</style>

5.2 多元素/组件过渡

5.2.1 多元素过渡

对于原生元素可以使用v-if/v-else实现多元素过渡。最常见的多元素过渡是一个列表和描述这个列表为空消息的元素:

<transition>

<table v-if="items.length > 0">

<!-- ... -->

</table>

<p v-else>没有列表内容</p>

</transition>

实际上,通过使用多个v-if或将单个元素绑定到一个动态属性上,可以在任意数量的元素之间进行过渡。

javascript 复制代码
<div id="app">
    <button @click="handleClick('saved')">
        显示Edit
    </button>&nbsp;&nbsp;
    <button @click="handleClick('edited')">
        显示Save
    </button>&nbsp;&nbsp;
    <button @click="handleClick('editing')">
        显示Cancel
    </button>
    <br><br>
    <transition name="fade" mode="out-in">
        <!--这里使用key让vue区分相同标签名元素,触发过渡-->
        <button :key="docState">
            {{ buttonMessage }}
        </button>
    </transition>
</div>
<script src="js/vue.global.js"></script>
<script>
    const app = Vue.createApp({
        data() {
            return {
                docState: ''
            }
        },
        methods: {
            handleClick(myVal) {
                this.docState = myVal
            }
        },
        computed: {
            buttonMessage() {
                switch (this.docState) {
                    case 'saved': return 'Edit'
                    case 'edited': return 'Save'
                    case 'editing': return 'Cancel'
                    default: return '初始按钮'
                }
            }
        }
    })
    app.mount('#app')
</script>
<style type="text/css">
    .fade-enter-from,
    .fade-leave-to {
        opacity: 0;
    }
    .fade-enter-active,
    .fade-leave-active {
        transition: opacity 5s ease;
    }
</style>

5.2.2 多组件过渡

可以使用多组件过渡将多个组件包装成动态组件的效果。

【例5-7】设计一个类似于选项卡的页面,单击"多组件过渡按钮"将 "登录子组件"和"注册子组件"进行切换。

javascript 复制代码
<div id="app">
    <button @click="show">多组件过渡按钮</button>
    <transition name="check" mode="out-in">
        <!-- is用来展示的template组件,mode组件切换的模式,name过渡的前缀,component占位符表示展示的组件 -->
        <component :is="view"></component>
    </transition>
</div>
<!-- 登录子组件 -->
<template id="login">
    <div>
        <h1>登录子组件</h1>
    </div>
</template>
<!-- 注册子组件 -->
<template id="register">
    <div>
        <h1>注册子组件</h1>
    </div>
</template>
<script src="js/vue.global.js"></script>
<script>
    const app = Vue.createApp({
        data() {
            return {
                view: 'login'
            }
        },
        methods: {
            show: function () {
                if (this.view == "login") {
                    this.view = "register"
                } else {
                    this.view = "login"
                }
            }
        },
        components: {
            login: {
                template: "#login"
            },
            register: {
                template: "#register"
            }
        }
    })
    app.mount('#app')
</script>
<style type="text/css">
    .check-enter-from,
    .check-leave-to {
        opacity: 0;
    }
    .check-enter-active,
    .check-leave-active {
        transition: opacity 5s ease;
    }
</style>

5.3 列表过渡

对于列表元素,可使用<transition-group>组件进行过渡。<transition-group>组件具有以下几个特点:

1、不同于 <transition>组件,它默认以<span>元素渲染。

2、过渡模式不可用,因为不再相互切换特有的元素。

3、内部元素需要提供唯一的key属性值。

4、CSS过渡类将会应用在组件内部的元素中,而不是组件本身。

5.3.1 列表的普通过渡

5.3.2 列表的平滑过渡

在例5-8中,当添加和移除元素时,周围元素将瞬间移动到它们的新布局位置,而不是平滑的过渡。

<transition-group> 组件不仅可以进入和离开列表过渡,还可以通过v-move特性改变定位,进行平滑过渡。v-move特性像之前的类名一样,可以通过name属性来自定义前缀。

javascript 复制代码
<div id="list-demo" class="demo">
    <button @click="add">添加元素</button>&nbsp;
    <button @click="remove">移除元素</button>
    <transition-group name="list" tag="p">
        <span v-for="item in items" :key="item" class="list-item">{{item}}</span>
    </transition-group>
</div>
<script src="js/vue.global.js"></script>
<script>
    const app = Vue.createApp({
        data() {
            return {
                items: [1, 2, 3, 4, 5, 6, 7, 8, 9],
                nextNum: 10
            }
        },
        methods: {
            randomIndex() {
                return Math.floor(Math.random() * this.items.length)
            },
            add() {
                this.items.splice(this.randomIndex(), 0, this.nextNum++)
            },
            remove() {
                this.items.splice(this.randomIndex(), 1)
            }
        }
    })
    app.mount('#list-demo')
</script>
<style>
    .list-item {
        display: inline-block;
        margin-right: 10px;
    }
    .list-enter-active,
    .list-leave-active {
        transition: all 3s;
    }
    .list-enter,
    .list-leave-to {
        opacity: 0;
        transform: translateY(30px);
    }
</style>

【例5-9】列表的平滑过渡,可以在例5-8的基础上,做出如下改进:1、增加.list-move样式,使元素在进入时实现过渡效果;2、在.list-leave-active中设置绝对定位,使元素在离开时实现过渡效果。

javascript 复制代码
<div id="list-demo" class="demo">
    <button @click="add">添加元素</button>&nbsp;
    <button @click="remove">移除元素</button>
    <transition-group name="list" tag="p">
        <span v-for="item in items" :key="item" class="list-item">{{item}}</span>
    </transition-group>
</div>
<script src="js/vue.global.js"></script>
<script>
    const app = Vue.createApp({
        data() {
            return {
                items: [1, 2, 3, 4, 5, 6, 7, 8, 9],
                nextNum: 10
            }
        },
        methods: {
            randomIndex() {
                return Math.floor(Math.random() * this.items.length)
            },
            add() {
                this.items.splice(this.randomIndex(), 0, this.nextNum++)
            },
            remove() {
                this.items.splice(this.randomIndex(), 1)
            }
        }
    })
    app.mount('#list-demo')
</script>
<style>
    .list-item {
        display: inline-block;
        margin-right: 10px;
    }
    .list-move,
    .list-enter-active,
    .list-leave-active {
        transition: 3s;
    }
    .list-leave-active {
        position: absolute;
    }
    .list-enter,
    .list-leave-to {
        opacity: 0;
        transform: translateY(30px);
    }
</style>

5.3.3 列表的变换过渡

利用move属性,进行变换过渡,即一个列表中的列表项既不增加也不减少,只是不断地变换其位置。

【例5-10】列表的变换过渡。

javascript 复制代码
<div id="list-demo" class="demo">
    <button @click="shuffle">变换过渡</button>
    <transition-group name="list" tag="ul">
        <li v-for="item in items" :key="item">{{item}}</li>
    </transition-group>
</div>
<script src="js/vue.global.js"></script>
<script>
    const app = Vue.createApp({
        data() {
            return {
                items: [1, 2, 3, 4, 5, 6, 7, 8, 9],
                nextNum: 10
            }
        },
        methods: {
            shuffle() {
                this.items = this.items.sort(() => { return Math.random() - 0.5; })
            }
        }
    })
    app.mount('#list-demo')
</script>
<style>
    .list-move {
        transition: transform 3s;
    }
</style>

5.3.4 多维列表的过渡

FLIP动画不仅可以实现单列表过渡,多维网格列表的过渡也同样简单。

【例5-11】多维网格列表的过渡。

javascript 复制代码
<div id="list-demo" class="demo">
    <button @click="shuffle">多维列表变换</button>
    <transition-group name="cell" tag="div" class="container">
        <span v-for="cell in cells" :key="cell.id" class="cell">{{ cell.number }}</span>
    </transition-group>
</div>
<script src="js/vue.global.js"></script>
<script>
    const app = Vue.createApp({
        data() {
            return {
                cells: Array.apply(null, { length: 81 })
                    .map(function (_, index) {
                        return {
                            id: index,
                            number: index % 9 + 1
                        }
                    })
            }
        },
        methods: {
            shuffle() {
                this.cells = this.cells.sort(() => { return Math.random() - 0.5; })
            }
        }
    })
    app.mount('#list-demo')
</script>
<style>
    .container {
        width: 270px;
        margin-top: 10px;
        line-height: 30px;
        text-align: center;
    }
    .cell {
        display: inline-block;
        width: 30px;
        height: 30px;
        outline: 1px solid #aaa;
    }
    .cell-move {
        transition: 3s;
    }
</style>
相关推荐
Liu.77417 分钟前
Vue3 中如何使用 GET 请求传递复杂参数
vue.js·vue3
定栓26 分钟前
Webpack迁到Vite第一节:模块系统迁移:require → import
运维·前端·webpack
小熊在忙fpga35 分钟前
N32G430芯片的flash表示
前端·javascript·html
Data analyse45641 分钟前
App埋点版本兼容的字段迁移校验
大数据·运维·服务器·前端·数据库·数据分析
NC 王炸1 小时前
AI 做的便签,换个浏览器就没了?加一个导出按钮,把数据带走
javascript·ai编程·数据备份·localstorage·txt导出
todoitbo1 小时前
本来只想给内网开个口子,Natapp 却多给了一道防护
前端·javascript·vue.js·内网穿透·网络防护·natapp
会灭火的程序员1 小时前
SuperMap AgentX Server智能体接入WEB应用技术文档
前端·supermap·agentx·agentx server
CRMEB1 小时前
商品 SKU 设计,从简单规格到多规格踩过什么
java·前端·数据库·开源
Chloe.Zz1 小时前
一篇入门Agent
开发语言·前端·javascript
java1234_小锋1 小时前
【技术专题】HTML5 网页技术 - HTML5 表格标签
前端·html·html5