当前位置: 首页 > article >正文

Vue 入门到实战 五

5章 过渡与动画

目录

5.1 单元素/组件过渡

5.1.1 过渡class

5.1.2 CSS 过渡

5.1.3 CSS 动画

5.1.4 同时使用过渡和动画

5.1.5 JavaScript 钩子方法

5.2 多元素/组件过渡

5.2.1 多元素过渡

5.2.2 多组件过渡

5.3 列表过渡

5.3.1 列表的普通过渡

5.3.2 列表的平滑过渡

5.3.3 列表的变换过渡

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 前缀:

<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所示。

 

<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所示。

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

<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 钩子方法

 

<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或将单个元素绑定到一个动态属性上,可以在任意数量的元素之间进行过渡。

<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】设计一个类似于选项卡的页面,单击“多组件过渡按钮”将 “登录子组件”和“注册子组件”进行切换。

<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属性值。

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

5.3.1 列表的普通过渡

5.3.2 列表的平滑过渡

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

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

<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中设置绝对定位,使元素在离开时实现过渡效果。

<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】列表的变换过渡。

<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】多维网格列表的过渡。

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


http://www.kler.cn/a/598109.html

相关文章:

  • Java算法队列和栈经常用到的ArrayDeque
  • 刷新页面pinia数据会不会消失
  • 从扩展黎曼泽塔函数构造物质和时空的结构-1
  • 【行驶证识别】批量咕嘎OCR识别行驶证照片复印件图片里的文字信息保存表格或改名字,基于QT和腾讯云api_ocr的实现方式
  • Java 安装开发环境(Mac Apple M1 Pro)
  • 多传感器融合 SLAM LVI-SAM
  • 单链表中的递归算法
  • 【C++教程】bool类型
  • Rust语言的集成测试
  • 数据库数值函数详解
  • 浅谈布隆过滤器(Bloom Filter)
  • 基于CNN-LSTM联合网络的主瓣干扰辨识
  • java开发人工智能问答小项目
  • 关于大数据的基础知识(三)——数据安全与合规
  • 平芯微PW5012应用电路
  • Codeforces Round 1012 (Div. 2) 3.23
  • 个人学习编程(3-23) leetcode刷题
  • 音频变压器技术白皮书
  • uniapp的app产物如何打成apk
  • Spring Boot(十六):拦截器Interceptor