Vue系列

第二十八节:Vue过渡与动画

2022-06-17  本文已影响0人  Dylan_Cao

前言:

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

那么接下来就让我们好好Vue中的过度与动画.

1. 过渡或动画的了解

Vue 提供了 transition 的封装组件,来处理过渡以及动画

1.1 使用过渡或动画的场景

在下列情形中,可以给任何元素和组件添加进入/离开过渡

  1. 条件渲染(使用v-if)
  2. 条件展示(使用v-show)
  3. 动态组件
  4. 组件的根节点
1.2 使用说明
  1. 只有dom从隐藏到显示,或从显示到隐藏, 才能使用vue的过渡或动画

  2. 动画的元素需要被Vue所提供的组件transition所包裹

  3. 在插入或删除transition包裹元素时,Vue会在恰当的实际添加或删除类名

  4. 如果过渡的组件提供了JavaScript钩子函数,则Vue会在恰当的时机调用这些钩子函数

  5. 如果没有找到JavaScrip钩子函数,并且也没有检测到CSS过渡/动画,则会在下一帧中立即执行

2. Vue 过渡的使用

2.1 认识过渡

Vue过渡/动画两个过程,分别为进入和离开.

Vue会在不同的需要过渡的元素在进入/离开的不同时机给元素添加不同的类名

不同阶段的类名:

Enter Enter Leave Leave
Opacity:0 Opacity:1 Opacity:1 Opacity:0
v-enter v-enter-to v-leave v-leave-to
v-enter-active v-enter-active v-leave-active v-leave-active
image
2.2 过度类名的了解

在进入/离开的过渡中,会有 6 个 class 切换。

  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 被删除),在过渡/动画完成之后移除。

现在我们对于这些类名有了一定的了解之后,我们通过案例来加深对于这些类名的理解和记忆

2.3 示例:
<style>
    div{
        font-size:30px;
        font-weight: bold;
    }
    .v-enter{
        color:red;
    }
    .v-enter-to{
        color:blue;
    }
    .v-enter-active{
        transition:all 5s;
    }
    .v-leave{
        color: blue;
    }
    .v-leave-to{
        color: purple;
    }
    .v-leave-active{
        transition:all 3s;
    }
</style>

<div id="app">
    <!-- 按钮,点击切换元素的显示和隐藏 -->
    <button @click="isShow = !isShow">点击切换</button>

    <!-- 显示元素 -->
    <transition>
        <div v-if="isShow">需要动画的元素</div>
    </transition>

</div>

<script>

    //  实例
    const vm = new Vue({
        el:"#app",
        data:{
            isShow:true
        }
    })
</script>

2.4 过渡前缀

过渡前缀说明

  1. 如果你使用一个没有名字的 <transition>,则 v- 是这些类名的默认前缀。
  2. 如果使用了 <transition name="myname">,那么 v-enter 会替换为 myname-enter

示例:

<style>
    div{
        font-size:30px;
        font-weight: bold;
    }
    .fade-enter,
    .fade-leave-to{
        color:red;
    }
    .fade-enter-to{
        color:blue;
    }
    .fade-enter-active,
    .fade-leave-active{
        transition:all 3s;
    }

</style>

<div id="app">
    <!-- 按钮,点击切换元素的显示和隐藏 -->
    <button @click="isShow = !isShow">点击切换</button>

    <!-- 显示元素 -->
    <transition name="fade">
        <div v-if="isShow">需要动画的元素</div>
    </transition>

</div>

<script>

    //  实例中注册组件
    const vm = new Vue({
        el:"#app",
        data:{
            isShow:true
        }
    })
</script>

2.5 在过渡中使用贝塞尔曲线
<style>
    div{
        position: fixed;
        font-size:30px;
        font-weight: bold;
    }
    .fade-enter,
    .fade-leave-to{
        left:200px;
    }
    .fade-enter-to{
        left:10px;
    }
    .fade-leave{

        left:10px;
    }
    .fade-enter-active{
        transition:all 3s linear;
    }

    .fade-leave-active{
        transition:all 3s cubic-bezier(.94,.1,.93,.08);
    }

</style>

<div id="app">
    <!-- 按钮,点击切换元素的显示和隐藏 -->
    <button @click="isShow = !isShow">点击切换</button>

    <!-- 显示元素 -->
    <transition name="fade">
        <div v-if="isShow">需要动画的元素</div>
    </transition>

</div>

<script>

    //  实例中注册组件
    const vm = new Vue({
        el:"#app",
        data:{
            isShow:true
        }
    })
</script>

3. CSS动画

我们处理可以在Vue中使用过渡transition,还可以使用CSS动画animation

<style>
    div{
        font-size:30px;
        font-weight: bold;
    }
    @keyframes fade-in{
        0%{
            transform: scale(0);
        }
        50%{
            transform: scale(1.5);
        }
        100%{
            transform: scale(1);
        }
    }
    .fade-enter-active{
        animation: fade-in 10s linear;
    }

    .fade-leave-active{
        animation: fade-in 10s linear reverse;
    }

</style>

<div id="app">
    <!-- 按钮,点击切换元素的显示和隐藏 -->
    <button @click="isShow = !isShow">点击切换</button>

    <!-- 显示元素 -->
    <transition name="fade">
        <div v-if="isShow">需要动画的元素</div>
    </transition>

</div>

<script>

    const vm = new Vue({
        el:"#app",
        data:{
            isShow:true
        }
    })
</script>

3. 自定义过度动画的类名

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

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

示例:

<style>
    div{
        font-size:30px;
        font-weight: bold;
    }
    @keyframes fade-in{
        0%{transform: scale(0);}
        50%{transform: scale(1.5);}
        100%{transform: scale(1);}
    }
    .enter{
        transform-origin:left center;
        animation: fade-in 3s linear;
    }

    .leave{
        transform-origin:left center;
        animation: fade-in 3s linear reverse;
    }

</style>

<div id="app">
    <!-- 按钮,点击切换元素的显示和隐藏 -->
    <button @click="isShow = !isShow">点击切换</button>

    <!-- 显示元素 -->
    <transition 
                name="fade"
                enter-active-class="enter"
                leave-active-class="leave"
                >
        <div v-if="isShow">需要动画的元素</div>
    </transition>

</div>

<script>

    const vm = new Vue({
        el:"#app",
        data:{
            isShow:true
        }
    })
</script>

4.动画插件库

animated.css 使用的是keyframes 的动画

4.1 安装

安装

npm install animate.css --save-dev

引入

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

4.2 使用cdn
<link href="https://cdn.jsdelivr.net/npm/animate.css@3.5.1" rel="stylesheet" type="text/css">

4.3 使用
<transition enter-active-class="animated rotateIn" leave-active-class="animated rotateIn">
    <div v-show="show" class='box'>Hello world</div>
</transition>

使用animation动画库的好处就是不需要我们自己去写负责的动画

4.4 最开始入场动画
<transition
            name="fade"
            enter-active-class="animated swing"
            leave-active-class="animated flipInX"
            >
  <div v-show="show">Hello world</div>
</transition>

但是你会发现最开始是没有入场动画的,如果我也希望网页一打开最开始就有入场动画

应该如下处理,添加appear属性

<transition
            name="fade"
            appear
            enter-active-class="animated swing"
            leave-active-class="animated flipInX"
            appear-active-class="animated swing"
            >
  <div v-show="show">Hello wold</div>
</transition>

4.5 animation动画和transition过渡一起使用
4.5.1 动画过渡一起使用

示例:

<style>
  .fade-enter,
  .fade-leave-to{
    opacity:0;
  }
  .fade-enter-active,
  .fade-leave-active{
    transition: opacity 3s
  }
</style>
<div id="app">
  <transition

              name="fade"
              appear
              enter-active-class="animated swing fade-enter-active"
              leave-active-class="animated flipInX fade-leave-active"
              appear-active-class="animated swing"
              >
    <div v-show="show">Hello wold</div>
  </transition>

此时你会发现animated.css的animation动画的时长和transition的时长不一样,那么要以哪个为准呢,其实vue也不知道,所以我们可以添加一个type属性来制定动画时长以谁为准

<transition
            type="transition"   
            name="fade"
            appear
            enter-active-class="animated swing fade-enter-active"
            leave-active-class="animated flipInX fade-leave-active"
            appear-active-class="animated swing"
            >
  <div v-show="show">Hello wold</div>
</transition>

4.5.2 自定义过渡/动画时间

同时我们也可以自定义动画时长,通过绑定duration,5秒后

<transition
            :duration="5000"  
            name="fade"
            appear
            enter-active-class="animated swing fade-enter-active"
            leave-active-class="animated flipInX fade-leave-active"
            appear-active-class="animated swing"
            >
  <div v-show="show">Hello wold</div>
</transition>

我们也可以单独定义入场动画时长和出场动画时长

<transition
            :duration="{enter:5000,leave:10000}"  
            name="fade"
            appear
            enter-active-class="animated swing fade-enter-active"
            leave-active-class="animated flipInX fade-leave-active"
            appear-active-class="animated swing"
            >
  <div v-show="show">Hello wold</div>
</transition>

5. Vue中的JS动画

vue中的js动画是通过vue提供给的动画钩子函数来绑定事件,然后在事件函数中处理对应的动画

5.1 动画入场的钩子函数

before-enter 动画入场运动前一刻执行

enter 动画运动时执行

after-enter 在动画enter函数中运行完毕并调用回调done时执行

<div id="app">
  <transition
              name="fade"
              @before-enter="handleBeforeEnter"
              @enter="handleEnter"
              @after-enter="handleAfterEnter"
              >
    <div v-show="show">Hello wold</div>
  </transition>
  <button @click="handleChange">点击</button>
</div>

<script>
  var app = new Vue({
    el:'#app',
    data:{
      show: true
    },
    methods:{
      handleChange(){
        this.show= !this.show
      },
      handleBeforeEnter(el){
        // 动画入场的前一刻执行before-enter动画钩子函数
        el.style.color="red"
      },
      handleEnter(el,done){
        // 动画运行的钩子函数
        // done是动画运行完成后的回调函数
        // 当动画运行完成以后执行done函数会触发after-enter动画钩子函数的运行
        setTimeout(() => {
          el.style.color = "blue";
        },2000);
        setTimeout(() => {
          done()
        },4000)
      },
      handleAfterEnter(el){
        // 动画完成后执行的动画钩子函数
        // 只用当动画钩子函数enter运行完毕后并且调用done时才会触发次函数
        el.style.color="#000"
      }
    }
  })
</script>

5.2 动画出场的钩子函数

同样动画出场的钩子函数与动画入场的钩子函数使用是一致的

before-leave

leave

after-leave

我们发现js动画没有动画时长需要我么自己调整,我们可以借助Velocity.js处理js动画

注意:

  1. 使用JavaScript 过渡的时候,在 enter 和 leave 中必须使用 done 进行回调。否则过渡会立即完成。
  2. 对于仅使用 JavaScript 过渡的元素添加 v-bind:css="false",Vue 会跳过 CSS 的检测。这也可以避免过渡过程中 CSS 的影响。

例如:

<transition
            :css="false"
            name="fade"
            @before-enter="handleBeforeEnter"
            @enter="handleEnter"
            @after-enter="handleAfterEnter"
            >
    <div v-show="show">Hello wold</div>
</transition>

5.3 Vue中JS动画与Velocity.js的结合

首先进入官网下载Velocity.js

然后引入

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

再次使用

<div id="app">
  <transition
              name="fade"
              @before-enter="handleBeforeEnter"
              @enter="handleEnter"
              @after-enter="handleAfterEnter"
              >
    <div v-show="show">Hello wold</div>
  </transition>
  <button @click="handleChange">点击</button>
</div>
<script src="./vue.js"></script>
<script>
  var app = new Vue({
    el:'#app',
    data:{
      show: true
    },
    methods:{
      handleChange(){
        this.show= !this.show
      },
      handleBeforeEnter(el){

        el.style.opacity=0  // 动画开始样式
      },
      handleEnter(el,done){

        Velocity(el,{
          opacity: 1   // 动画结束后的样式
        },{
          duration: 2000,  // 动画运行的时间
          complete: done   // 动画运动完成后执行的函数
        })

      },
      handleAfterEnter(el){
        el.style.fontSize="50px"
      }
    }
  })
</script>

6.Vue中多个元素之间的动画

6.1 多个元素之前切换动画

多元素过渡,就是利用v-if或者v-show等指令,切换元素的显示, 需要显示的元素就会有入场动画,需要隐藏的就有出场动画.

过渡示例:

<style>
    div{
        font-size:30px;
        font-weight: bold;
        opacity:1;
    }
    .fade-enter,.fade-leave-to{
        opacity: 0;
    }
    .fade-enter-active,.fade-leave-active{
        transition:all 3s;
    }

</style>

<div id="app">
    <!-- 按钮,点击切换元素的显示和隐藏 -->
    <button @click="isShow = !isShow">点击切换</button>

    <!-- 显示元素 -->
    <transition 
                name="fade"
                >
        <div v-if="isShow" key="hello">Hello wold</div>
        <div v-else key="bye">Bye wold</div>
    </transition>
</div>

<script>

    const vm = new Vue({
        el:"#app",
        data:{
            isShow:true
        }

    })
</script>

一定要注意动画内部两个div的标签复用,如果不加key,只会看到内容改变了,透明度样式并没有发生变化,这就是vue中的dom复用的机制,就是为了提升效率, 所以我们要加可以表示 唯一的dom

注意:

  1. 当有相同标签名的元素切换时,需要通过 key 属性设置唯一的值来标记以让 Vue 区分它们
  2. key属性的作用,在讲v-for已经介绍过来, 为了不让DOM元素复用
  3. DOM元素复用是Vue提升性能的手段,但是因为DOM元素复用了,也就没有动画效果了,
  4. 因此通过key 属性阻止DOM元素复用
6.2 出场入场动画先后顺如

默认出场入场动画是同时进行的,同时生效的进入和离开的过渡不能满足所有要求,所以Vue提供了过渡模式

mode属性说明

  1. Vue在transition标签上提供了一个mode属性,来设置出场入场动画的先后顺序

  2. mode的属性值为in-out,则为先执行入场动画,等入场动画执行完毕后在执行出场动画,

  3. mode属性值为out-in 则先执行出场动画,在执行入场动画

示例:

<style>
    div{
        font-size:30px;
        font-weight: bold;
        opacity:1;
    }
    .fade-enter,.fade-leave-to{
        opacity: 0;
    }
    .fade-enter-active,.fade-leave-active{
        transition:all 3s;
    }
</style>

<div id="app">
    <!-- 按钮,点击切换元素的显示和隐藏 -->
    <button @click="isShow = !isShow">点击切换</button>

    <!-- 显示元素 -->
    <transition 
                name="fade"
                mode="in-out"
                >
        <div v-if="isShow" key="hello">Hello wold</div>
        <div v-else key="bye">Bye wold</div>
    </transition>

</div>

<script>

    const vm = new Vue({
        el:"#app",
        data:{
            isShow:true
        }

    })
</script>

7. Vue中组件之间的动画

7.1. 不利用动态组件的写法

示例:

<style>
    .fade-enter,.fade-leave-to{
        opacity:0
    }
    .fade-enter-active,.fade-leave-active{
        transition: opacity 1s;
    }
</style>
<div id="app">
    <transition
                name="fade"
                mode="out-in"
                >
        <child v-if="show"></child>
        <child-one v-else></child-one>
    </transition>
    <button @click="handleChange">点击切换</button>
</div>

<script>
    Vue.component("child",{
        template:"<div>chile</div>"
    })
    Vue.component("child-one",{
        template:"<div>chile-on</div>"
    })

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

7.2. 利用动态组件的方式切换组件
<style>
    .fade-enter,.fade-leave-to{
        opacity:0
    }
    .fade-enter-active,.fade-leave-active{
        transition: opacity 1s;
    }
</style>
<div id="app">
    <transition name="fade" mode="out-in">
        <component :is="type"></component> 
    </transition>
    <button @click="handleChange">点击切换</button>
</div>

<script>
    Vue.component("child",{
        template:"<div>chile</div>"
    })
    Vue.component("child-one",{
        template:"<div>chile-on</div>"
    })

    var app = new Vue({
        el:'#app',
        data:{
            type: "child"
        },
        methods:{
            handleChange(){
                this.type = this.type=="child"?"child-one":"child"
            }
        }
    })
</script>

8. Vue中的列表过度

目前为止,关于过渡我们已经讲到:

  1. 单个节点
  2. 同一时间渲染多个节点中的一个

那么怎么同时渲染整个列表,比如使用 v-for?在这种场景中,使用 <transition-group> 组件。

特点:

  1. <transition>标签会以一个真实元素呈现:默认是 <span>。也可以通过 tag 属性 更换为其他元素。
  2. 过渡模式不可用,因为我们不再相互切换特有的元素。
  3. 内部元素总是需要提供唯一的 key 属性 值。
  4. CSS 过渡的类将会应用在内部的元素中,而不是这个组/容器本身。

示例如下:

vue中的列表过渡使用的是transition-group标签嵌套

<style>
    .fade-enter,.fade-leave-to{
        opacity:0
    }
    .fade-enter-active,.fade-leave-active{
        transition: opacity 1s;
    }
</style>
<div id="app">
    <transition-group name="fade">
        <div v-for="item in list" :key="item.id">
            {{item.title}}
        </div>
    </transition-group>

    <button @click="handleChange">点击新增</button>
</div>

<script>

    var app = new Vue({
        el:'#app',
        data:{
            count: 0,
            list: []
        },
        methods:{
            handleChange(){
                this.list.push({
                    id: this.count++,
                    title: "Hello World"
                })
            }
        }
    })
</script>

其实transition-group的原理就是给每一个列表循环的单独的元素套了一层transition标签,说白了,还是单个元素的过渡动画

就像这样

<transition>
  <div>Hello World</div>
</transition>
<transition>
  <div>Hello World</div>
</transition>
<transition>
  <div>Hello World</div>
</transition>
....

注意,for循环的key值尽量不要用index索引,会导致性能降低,其次项目功能上也会造成一定的影响,如果能不用index作为key值,尽量不要用index作为key值

9. 封装Vue动画

利用组件和slot的方式,将动画封装成一个组件,这样可以通过组件调用发方式复用相同动画

<style>
    .box{
        font-size:30px;
        font-weight:bold;
    }
    .fade-enter,.fade-leave-to{
        opacity:0
    }
    .fade-enter-active,.fade-leave-active{
        transition: all 3s
    }
</style>

<div id="app">
    <button @click="handleChange">点击切换</button>
    <fade :show="show" name="fade">
        <div class="box">Hello wold</div>
    </fade>
</div>

<!-- 组件模板 -->
<template id="myTransition">
    <transition 
                :name="name"
                >
        <slot v-if="show"></slot>
    </transition>
</template>

<script>
    // 定义全局组件
    Vue.component("fade",{
        props:["show","name"],
        template:`#myTransition`,
    })

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

当然你还可以继续进一步封装完善, 达到你想要的目的

上一篇 下一篇

猜你喜欢

热点阅读