vue基础:过渡动画、列表动画、案例

目录

一、过渡动画

1.1 初识过渡动画

1.2 通过 class 添加动画

1.3 通过钩子函数添加动画

1.4 使用第三方库--Velocity.js

1.5 使用自定义类名添加动画

1.6 配合使用第三方库--自定义类名结合Animate.css

二、列表动画

2.1 给多个元素添加动画效果

三、单元素、单组件的入场出场动画

3.1 初识单元素、单组件的入场出场动画

3.2 单元素、单组件的入场出场动画示例

3.2 单元素、单组件的入场出场动画2


 

一、过渡动画

1.1 初识过渡动画

i.Vue 在插入、更新或者移除 DOM 时,提供多种不同方式的应用过渡效果。包括以下工具:

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

ii.如何给Vue控制的元素添加过渡动画?

  • 将需要执行的动画元素放到transition组件中,一个transition组件中只能放一个元素
  • 当transition组件中的元素显示时会自动查找:.v-enter/.v-enter-active/.v-enter-to类名
  • 当transition组件中的元素隐藏时会自动查找:.v-leave/.v-leave-active/.v-leave-to类名
  • 我们只需要在:.v-enter和 .v-leave-to中指定动画开始的状态;在.v-enter-active和.v-leave-active中指定动画执行的状态即可完成动画

iii.注意点:

  1. 如果想给多个元素添加过渡动画,那么就必须创建多个transition组件
  2. 默认情况下第一次进入时是没有动画的,若想一进来就有动画,我们可以通过给transition添加appear属性来告诉Vue第一次进来就需要动画
  3. 如果有多个不同的元素需要执行不同的过渡动画,那么我们可以通过给transition指定name的方式来指定“进入之前/进入之后/进入过程中,离开之前/离开之后/离开过程中”对应的类名以此来实现不同元素执行不同的过渡动画

1.2 通过 class 添加动画

         * {
            margin: 0;
            padding: 0;
        }
        
        .box {
            width: 150px;
            height: 150px;
            background: #f40;
        }
        /* .v-enter {
            opacity: 0;
        }
        .v-enter-active {
            transition: all 3s;
        }
        .v-enter-to {
            opacity: 1;
        }
        .v-leave-active {
            transition: all 3s;
        }
        .v-leave {
            opacity: 1;
        }
        .v-leave-to {
            opacity: 0;
        } */
        
        .one-enter {
            /* 显示之前 */
            opacity: 0;
        }
        
        .one-enter-active {
            /* 显示过程中 */
            transition: all 3s;
        }
        
        .one-enter-to {
            /* 显示之后 */
            opacity: 1;
        }
        
        .two-leave-active {
            /* 显示过程中 */
            transition: all 3s;
        }
        
        .two-leave {
            opacity: 1;
        }
        
        .two-leave-to {
            opacity: 0;
        }
<script src="js/vue.js"></script>
<div id="app">
        <button @click="toggle">按钮</button>
        <transition appear name="one">
            <div class="box" v-show="isShow"></div>
        </transition>
        <transition appear name="two">
            <div class="box" v-show="isShow"></div>
        </transition>
    </div>
        let vue = new Vue({
            el: '#app',
            // 这里就是MVVM中的Model
            data: {
                isShow: true
            },
            // 专门用于存储监听事件回调函数
            methods: {
                toggle() {
                    this.isShow = !this.isShow;
                }
            },
            // template: `
            // <p>{{time | dateFormate("yyyy-MM-dd")}}</p>
            // `
        });


1.3 通过钩子函数添加动画

可以在 attribute 中声明 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>

注意点:

虽然我们是通过钩子函数来实现过渡动画,但是默认Vue还是会去查找类名,因此我们可以给transition添加 v-bind:css="false" 来阻止其默认行为

如果是通过钩子函数来实现过渡动画,那么必须在动画执行过程中的回调函数中写上el.offsetWidth/el.offsetHeight

如果想让元素一进来就有动画,那么最好延迟一下再调用done方法

        * {
            margin: 0;
            padding: 0;
        }
        
        .box {
            width: 150px;
            height: 150px;
            background: #f40;
        }
<script src="js/vue.js"></script>
 <div id="app">
        <button @click="toggle">按钮</button>
        <transition appear v-bind:css="false" v-on:before-enter="beforeEnter" v-on:enter="enter" v-on:after-enter="afterEnter">
            <div class="box" v-show="isShow"></div>
        </transition>
    </div>
        let vue = new Vue({
            el: '#app',
            // 这里就是MVVM中的Model
            data: {
                isShow: true
            },
            // 专门用于存储监听事件回调函数
            methods: {
                toggle() {
                    this.isShow = !this.isShow;
                },
                beforeEnter(el) {
                    //进入动画开始之前
                    console.log("beforeEnter");
                    el.style.opacity = "0";
                },
                enter(el, done) {
                    //进入动画执行过程中
                    console.log("enter");
                    el.offsetWidth;
                    el.style.transition = "all 3s";

                    setTimeout(function() {
                        /*
                         动画执行完毕之后一定要调用done回调函数,否则后面的afterEnter钩子函数不会被执行
                         */
                        done();
                    }, 0);
                },
                afterEnter(el) {
                    //进入动画执行完毕之后
                    console.log("afterEnter");
                    el.style.opacity = "1";
                    el.style.marginLeft = "500px";
                }
            },
            // template: `
            // <p>{{time | dateFormate("yyyy-MM-dd")}}</p>
            // `
        });


1.4 使用第三方库--Velocity.js

使用方式:

1.引入:

<script src="https://cdnjs.cloudflare.com/ajax/libs/velocity/1.5.0/velocity.min.js"></script>

2.给transition绑定对应的钩子函数:

v-on:before-enter="beforeEnter"
v-on:enter="enter"
v-on:after-enter="afterEnter"
......

3.找到过渡动画执行过程中的函数即enter(),在该函数里直接通过Velocity()实现动画:

enter(el, done) {
          //进入动画执行过程中
          console.log("enter");
          el.offsetWidth;
          el.style.transition = "all 3s";

           Velocity();

          setTimeout(function() {
            /*
           动画执行完毕之后一定要调用done回调函数,否则后面的afterEnter钩子函数不会被执行
           */
        done();
   }, 0);
},

示例:

        * {
            margin: 0;
            padding: 0;
        }
        
        .box {
            width: 150px;
            height: 150px;
            background: #f40;
        }
<script src="js/vue.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/velocity/1.5.0/velocity.min.js"></script>
 <div id="app">
        <button @click="toggle">按钮</button>
        <transition appear v-bind:css="false" v-on:before-enter="beforeEnter" v-on:enter="enter" v-on:after-enter="afterEnter">
            <div class="box" v-show="isShow"></div>
        </transition>
    </div>
        let vue = new Vue({
            el: '#app',
            // 这里就是MVVM中的Model
            data: {
                isShow: true
            },
            // 专门用于存储监听事件回调函数
            methods: {
                toggle() {
                    this.isShow = !this.isShow;
                },
                beforeEnter(el) {
                    //进入动画开始之前
                    console.log("beforeEnter");
                },
                enter(el, done) {
                    //进入动画执行过程中
                    console.log("enter");

                    Velocity(el, {
                        opacity: 1,
                        marginLeft: "500px"
                    }, 3000);

                    setTimeout(function() {
                        /*
                         动画执行完毕之后一定要调用done回调函数,否则后面的afterEnter钩子函数不会被执行
                         */
                        done();
                    }, 0);
                },
                afterEnter(el) {
                    //进入动画执行完毕之后
                    console.log("afterEnter");
                }
            },
            // template: `
            // <p>{{time | dateFormate("yyyy-MM-dd")}}</p>
            // `
        });


1.5 使用自定义类名添加动画

我们可以通过以下 attribute 来自定义过渡类名:

  • enter-class
  • enter-active-class
  • enter-to-class (2.1.8+)
  • leave-class
  • leave-active-class
  • leave-to-class (2.1.8+)

他们的优先级高于普通的类名,这对于 Vue 的过渡系统和其他第三方 CSS 动画库,如 Animate.css  结合使用十分有用。

示例:

         * {
            margin: 0;
            padding: 0;
        }
        
        .box {
            width: 150px;
            height: 150px;
            background: #f40;
        }
        
        .custom-enter-nj {
            opacity: 0;
        }
        
        .custom-enter-to-nj {
            opacity: 1;
            margin-left: 500px;
        }
        
        .custom-enter-active-nj {
            transition: all 3s;
        }
<script src="js/vue.js"></script>
<div id="app">
        <button @click="toggle">按钮</button>
        <transition appear enter-class="custom-enter-nj" enter-active-class="custom-enter-active-nj" enter-to-class="custom-enter-to-nj">
            <div class="box" v-show="isShow"></div>
        </transition>
    </div>
        let vue = new Vue({
            el: '#app',
            // 这里就是MVVM中的Model
            data: {
                isShow: true
            },
            // 专门用于存储监听事件回调函数
            methods: {
                toggle() {
                    this.isShow = !this.isShow;
                },
            },
            // template: `
            // <p>{{time | dateFormate("yyyy-MM-dd")}}</p>
            // `
        });


1.6 配合使用第三方库--自定义类名结合Animate.css

         * {
            margin: 0;
            padding: 0;
        }
        
        .box {
            width: 150px;
            height: 150px;
            background: #f40;
        }
<link href="https://cdn.jsdelivr.net/npm/animate.css@3.5.1" rel="stylesheet" type="text/css">
<script src="js/vue.js"></script>
<div id="app">
        <button @click="toggle">按钮</button>
        <transition appear enter-class="" enter-active-class="animated bounceInRight" enter-to-class="">
            <div class="box" v-show="isShow"></div>
        </transition>
    </div>
        let vue = new Vue({
            el: '#app',
            // 这里就是MVVM中的Model
            data: {
                isShow: true
            },
            // 专门用于存储监听事件回调函数
            methods: {
                toggle() {
                    this.isShow = !this.isShow;
                },
            },
            // template: `
            // <p>{{time | dateFormate("yyyy-MM-dd")}}</p>
            // `
        });


二、列表动画

2.1 给多个元素添加动画效果

示例:

        .v-enter {
            opacity: 0;
        }
        
        .v-enter-active {
            transition: all 3s;
        }
        
        .v-enter-to {
            opacity: 1;
        }
        
        .v-leave-active {
            transition: all 3s;
        }
        
        .v-leave {
            opacity: 1;
        }
        
        .v-leave-to {
            opacity: 0;
        }
<script src="js/vue.js"></script>
<div id="app">
        <form>
            <input type="text" v-model="name">
            <input type="submit" value="添加" @click.prevent="add">
        </form>
        <transition-group appear tag="ul">
            <li v-for="(person,index) in persons" :key="person.id" @click="del(index)">
                <input type="checkbox">
                <span>{{index}} --- {{person.name}}</span>
            </li>
        </transition-group>
    </div>
let vue = new Vue({
            el: '#app',
            // 这里就是MVVM中的Model
            data: {
                persons: [{
                    name: "zs",
                    id: 1
                }, {
                    name: "ls",
                    id: 2
                }, {
                    name: "ww",
                    id: 3
                }],
                name: "",
                id: 3
            },
            // 专门用于存储监听事件回调函数
            methods: {
                add() {
                    this.id++;
                    let lastPerson = this.persons[this.persons.length - 1];
                    let newPerson = {
                        name: this.name,
                        id: lastPerson.id + this.id
                            // id: lastPerson.id + 1
                    };
                    // 在尾部添加
                    // this.persons.push(newPerson);

                    //在头部添加
                    this.persons.unshift(newPerson);
                    this.name = "";
                },
                del(index) {
                    this.persons.splice(index, 1);
                }
            },
            // template: `
            // <p>{{time | dateFormate("yyyy-MM-dd")}}</p>
            // `
        });

注意点:

默认情况下 transition-group 会将所有执行动画的元素放入到 span 中,可通过 tag 属性解决

在我们不断添加数据时这里可能会发生key重复的情况:

此时,我们可以通过如下解决,具体看示例


三、单元素、单组件的入场出场动画

3.1 初识单元素、单组件的入场出场动画

  1.  v-enter:定义进入过渡的开始状态。在元素被插入之前生效,在元素被插入之后的下一帧移除。

  2. v-enter-active:定义进入过渡生效时的状态。在整个进入过渡的阶段中应用,在元素被插入之前生效,在过渡/动画完成之后移除。这个类可以被用来定义进入过渡的过程时间,延迟和曲线函数。

  3. v-enter-to2.1.8 版及以上定义进入过渡的结束状态。在元素被插入之后下一帧生效 (与此同时 v-enter 被移除),在过渡/动画完成之后移除。

  4. v-leave:定义离开过渡的开始状态。在离开过渡被触发时立刻生效,下一帧被移除。

  5. v-leave-active:定义离开过渡生效时的状态。在整个离开过渡的阶段中应用,在离开过渡被触发时立刻生效,在过渡/动画完成之后移除。这个类可以被用来定义离开过渡的过程时间,延迟和曲线函数。

  6. v-leave-to2.1.8 版及以上定义离开过渡的结束状态。在离开过渡被触发之后下一帧生效 (与此同时 v-leave 被删除),在过渡/动画完成之后移除。


 

3.2 单元素、单组件的入场出场动画示例

         @keyframes shake {
            0% {
                transform: translateX(-100px);
            }
            50% {
                transform: translateX(-50px);
            }
            100% {
                transform: translateX(50px);
            }
        }
        
        .v-leave-active {
            animation: shake 3s;
        }
        
        .v-enter-active {
            animation: shake 3s;
        }
<script src="js/vue.js"></script>
    <div id="app">
        <div>
            <transition>
                <div v-if="show">hello world </div>
            </transition>
            <button @click="handleClick">切换</button>
        </div>
    </div>
        let vue = new Vue({
            el: '#app',
            // 这里就是MVVM中的Model
            data: {
                show: false
            },
            // 专门用于存储监听事件回调函数
            methods: {
                handleClick() {
                    this.show = !this.show;
                }
            },
            // template: `
            // <p>{{time | dateFormate("yyyy-MM-dd")}}</p>
            // `
        });


3.2 单元素、单组件的入场出场动画2

公共部分:

<script src="https://unpkg.com/vue@next"></script>
<div id="root"></div>


JS实现动画:

<script>
        const app = Vue.createApp({
            data() {
                return {
                    show: false
                }
            },
            methods: {
                handleClick() {
                    this.show = !this.show;
                },
                handleBeforeEnter(el) {
                    el.style.color = "red";
                },
                handleEnterActive(el, done) {
                    const animation = setInterval(() => {
                        const color = el.style.color;
                        if (color === 'red') {
                            el.style.color = 'green';
                        } else {
                            el.style.color = 'red';
                        }
                    }, 1000);
                    setTimeout(() => {
                        clearInterval(animation);
                        done();
                    }, 3000);
                },
                handleEnterEnd() {
                    alert(123);
                }
            },
            /*
            type="transition"  执行动画和过渡的过程中,如果过渡和动画的时间不一致,以过渡时间为主
            :duration="2000"  过渡和动画的持续时间都为2s
            :duration="{enter:1000,leave:3000}"  入场1s,出场3s
            */
            template: `
                <div>
                   <transition 
                    :css="false"
                    @before-enter="handleBeforeEnter"
                    @enter="handleEnterActive"
                    @after-enter="handleEnterEnd"
                    @before-leave=""
                    @leave=""
                    @leave-after=""
                   >
                    <div v-if="show">hello world </div>
                   </transition>
                   <button @click="handleClick">切换</button>
                </div>
            `
                // <button @click="handleClick">切换</button>
                // class="transition" :style="styleObj"
        });

        //将组件挂到root节点里,即只作用在id等于root的div元素里
        //vm 代表的就是 vue 应用的根组件
        const vm = app.mount('#root');

        //vm.$data 获得数据
        // vm.$data.message = 'root';
</script>


公共部分CSS:

.v-leave-to {
            opacity: 0;
        }
        
        .v-enter-from {
            opacity: 0;
        }
        
        .v-enter-active,
        .v-leave-active {
            transition: opacity 3s ease-in;
        }
        
        .v-leave-from,
        .v-enter-to {
            opacity: 1;
        }

多个单元素标签之间的动画切换:

<script>
        const app = Vue.createApp({
            data() {
                return {
                    show: false
                }
            },
            methods: {
                handleClick() {
                    this.show = !this.show;
                },
            },
            template: `
                <div>
                   <transition mode="out-in" appear>
                    <div v-if="show">hello world </div>
                    <div v-else="show">bye world </div>
                   </transition>
                   <button @click="handleClick">切换</button>
                </div>
            `
        });

        //将组件挂到root节点里,即只作用在id等于root的div元素里
        //vm 代表的就是 vue 应用的根组件
        const vm = app.mount('#root');

        //vm.$data 获得数据
        // vm.$data.message = 'root';
</script>

多个单组件之间的动画切换:

<script>
        const ComponentA = {
            template: '<div>bye world</div>'
        }

        const ComponentB = {
            template: '<div>bye</div>'
        }

        const app = Vue.createApp({
            data() {
                return {
                    show: false
                }
            },
            methods: {
                handleClick() {
                    this.show = !this.show;
                },
            },
            components: {
                'component-a': ComponentA,
                'component-b': ComponentB,
            },
            template: `
                <div>
                   <transition mode="out-in" appear>
                    <component-a v-if="show" />
                    <component-b v-else="show" />
                   </transition>
                   <button @click="handleClick">切换</button>
                </div>
            `
        });

        //将组件挂到root节点里,即只作用在id等于root的div元素里
        //vm 代表的就是 vue 应用的根组件
        const vm = app.mount('#root');

        //vm.$data 获得数据
        // vm.$data.message = 'root';
    </script>

或,

<script>
        const ComponentA = {
            template: '<div>bye world</div>'
        }

        const ComponentB = {
            template: '<div>bye</div>'
        }

        const app = Vue.createApp({
            data() {
                return {
                    component: 'component-a'
                }
            },
            methods: {
                handleClick() {
                    if (this.component === 'component-a') {
                        this.component = 'component-b';
                    } else {
                        this.component = 'component-a';
                    }
                },
            },
            components: {
                'component-a': ComponentA,
                'component-b': ComponentB,
            },
            template: `
                <div>
                   <transition mode="out-in" appear>
                    <component :is="component" />
                   </transition>
                   <button @click="handleClick">切换</button>
                </div>
            `
        });

        //将组件挂到root节点里,即只作用在id等于root的div元素里
        //vm 代表的就是 vue 应用的根组件
        const vm = app.mount('#root');

        //vm.$data 获得数据
        // vm.$data.message = 'root';
    </script>

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小白小白从不日白

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值