Vue(基础:过渡&动画)

196 阅读4分钟

过渡

过渡可以增强用户体验,我们不仅可以通过Css3来实现过渡效果,当然Vue也给我们提供了实现过渡效果的方式;Vue在实现元素过渡效果的时候会给元素默认添加6个Class类,结合V-ifV-show来实现过渡(视觉上我们感觉这六个class类是一次性添加的,实际上是分阶段添加的);

过渡的过程:进入:隐藏→显示离开:显示→隐藏

图解: 1.png 6个类名

  • v-enter:效果进入之前的初始状态
  • v-enter-active:效果进入正在进行时
  • v-enter-to:效果进入之后的结束状态
  • v-leave:效果离开之前的初始状态
  • v-leave-active:效果离开的正在进行时
  • v-leave-to:效果离开之后的结束状态

实现步骤

  1. 创建Vue实例;
  2. 在HTML结构中添加transition标签,并为过渡的元素节点绑定V-showV-if(如果我们想要一打开页面就应用过渡效果可以给transition标签添加appear属性);
  3. 设置值6个类对应的style样式.

例子

<style>
    /* 效果离开后的状态和进入前的状态写在一起 */
    .v-enter,
    .v-leave-to {
      opacity: 0;
    }

    /* 效果进入后的状态和离开前的状态写在一起  */
    .v-enter-to,
    .v-leave {
      opacity: 1;
    }

    /* 进行时的状态写在一起 */
    .v-enter-active,
    .v-leave-active {
      transition: all 1.5s ease;
    }
  </style>
  <div id="box">
    <!-- 每点击一次按钮flag的值取反 -->
    <button @click="flag=!flag">切换按钮</button>
    <!-- transition这个标签是必须要添加的,里面包裹要实现过渡效果的元素节点 -->
    <transition appear>
      <h1 v-show="flag">你好,Vue过渡</h1>
    </transition>
  </div>
  <script>
    var vm =new Vue({
      el: "#box",
      data: {
        // 定义flag用来切换元素不同的状态(显示or隐藏)
        flag: "true"
      },
      methods: {
      }
    })
  </script>

上面是一个简单的例子,我们发现一个问题,如果页面中有很多个过渡的元素节点,每个节点实现的过渡效果又不相同,我们没有办法区分怎么办,总不能给每一个元素都添加一个transition吧?或者给6个类名后边加上css选择器?这样肯定都是不太稳妥的。那么Vue就给我们提供了自定义的命名方式,便于我们识别过渡的元素节点。其实很简单请看下面↓

  • 首先,需要在transition标签上添加name属性,属性值(比如其中一个名字叫:onion)就是我们给某个元素节点添加过渡的名字
  • 然后,我们在使用的时候就不需要在V-enter\......而是onion-enter、.......

自定义transition名字

<style>
    h1 {
      background-color: #cfa;
      width: 600px;
      text-align: center;
      margin: 10px auto;
    }

    /* 效果离开后的状态和进入前的状态写在一起 */
    .v-enter,
    .v-leave-to {
      opacity: 0;
      transform: translateX(-150px);
    }

    /* 效果进入后的状态和离开前的状态写在一起  */
    .v-enter-to,
    .v-leave {
      opacity: 1;
    }

    /* 进行时的状态写在一起 */
    .v-enter-active,
    .v-leave-active {
      transition: all 1.5s ease;
    }

    /* 自定义的过渡类名 */
    .onion-enter,
    .onion-leave-to {
      opacity: 0;
      transform: translateY(50px);
    }
    .onion-enter-to,
    .onion-leave {
      opacity: 1;
    }
    .onion-enter-active,
    .onion-leave-active {
      transition: all 1.5s ease;
    }
  </style>
   <div id="box">
    <!-- 每点击一次按钮flag的值取反 -->
    <button @click="flag=!flag">切换按钮</button>
    <!-- transition这个标签是必须要添加的,里面包裹要实现过渡效果的元素节点 -->
    <transition appear>
      <h1 v-show="flag">你好,Vue过渡</h1>
    </transition>
    <transition name="onion">
      <h1 v-show="flag">这个是我自定义名字的Vue过渡</h1>
    </transition>
  </div>
  <script>
    var vm = new Vue({
      el: "#box",
      data: {
        // 定义flag用来切换元素不同的状态(显示or隐藏)
        flag: "true"
      },
      methods: {
      }
    })
  </script>

下载.gif

过渡+第三方animate实现炫酷效果

下载.gif 上面的效果特别炫酷,当然不是我徒手写的了,是我们利用第三方的插件结合Vue来实现的。如何实现呢?非常简单

步骤(我们采用本地引入animate.min.css文件的方式)

  1. 官方下载(animate.style/)animate.mi…
  2. 然后在transition标签中指定两个属性enter-active-classleave-active-class(规定的名字别乱改)
  3. 最后分别给这两个类的值设置为animate__animated 入场动画animate__animated 出场动画

image.png

image.png

动画的钩子函数(了解)

transition组件为我们提供了8个钩子函数可以实现在动画呈现的不同时机做不同的事情。这八个钩子函数可以理解成是动画的生命周期:

  • before-enter=“beforeEnter“ :进入过渡运行前
  • enter=“enter“ :进入过渡运行时
  • after-enter=“afterEnter“ :进入过渡运行后
  • @enter-cancelled=“enterCancelled“ :进入过渡被打断时
  • before-leave=“beforeLeave“ :离开过渡运行前
  • leave=“leave“ :离开过渡运行时
  • after-leave=“afterLeave“ :离开过渡运行后
  • leave-cancelled=“leaveCancelled“ :离开过渡被打断时 v-on绑定指定的钩子事件,执行自己定义的事件方法
<style>
    .mybtn {
        width: 100px;
        height: 100px;
        background-color: red;
        transform: translateX(50px);
    }
    .fade-enter-active,
    .fade-leave-active {
        transition: all 2s;
    }

    .fade-enter {
        opacity: 0;
        transform: translateX(0px);
    }

    .fade-enter-to {
        opacity: 1;
        transform: translateX(50px);
    }

    .fade-leave {
        opacity: 1;
        transform: translateX(50px);
    }

    .fade-leave-to {
        opacity: 0;
        transform: translateX(0px);
    }
</style>
<div>
    <button @click="flag=!flag">显示/隐藏</button>
    <transition name='fade'
                @before-enter="beforeEnter"
                @enter="enter"
                @after-enter="afterEnter"
                @enter-cancelled="enterCancelled"

                @before-leave="beforeLeave"
                @leave="leave"
                @after-leave="afterLeave"
                @leave-cancelled="leaveCancelled">
        <div v-show="flag" class="mybtn"></div>
    </transition>
</div>
let vm=    new Vue({
    data:{
        flag:false
    },
    methods:{
        beforeEnter(){
            alert("beforeEnter 进入过渡开始前 " );
        },
        enter(){
            alert("enter 进入过渡状态开始");
        },
        afterEnter(){
            alert("afterEnter 进入过渡状态结束");
        },
        enterCancelled(){
            alert("enterCancelled 进入过渡状态 被打断");
        },
        beforeLeave(){
            alert("beforeLeave 离开过渡开始前 " );
        },
        leave(){
            alert("leave 离开过渡状态开始");
        },
        afterLeave(){
            alert("afterLeave 离开过渡状态结束");
        },
        leaveCancelled(){
            alert("leaveCancelled 离开过渡状态 被打断");
        }
    }
}).$mount('div');


methods:{
    //动画钩子函数的第一个参数:el,表示 要执行动画的那个DOM元素,是个原生的 JS DOM对象
    afterEnter(el) {
        el.style.background='blue';
    },
    afterLeave(el){
        el.style.background='red';
    }
}

单向动画(半场动画)

如果要定义半场动画,做法是:直接在methods中写入场动画的函数,不写离场动画的函数即可。

例如:加购一件物品,会有一个小图标飞入购物车

<div id="app">
    <input type="button" value="加入购物车" @click="flag=!flag">
    <!-- 1. 使用 transition 元素把 小球包裹起来 -->
    <transition @before-enter="beforeEnter" @enter="enter" @after-enter="afterEnter">
        <div class="ball" v-show="flag"></div>
    </transition>
</div>
<script>
    var vm = new Vue({
        el: '#app',
        data: {
            flag: false
        },
        methods: {
            beforeEnter(el) {
                // beforeEnter 表示动画入场之前,此时,动画尚未开始,可以 在 beforeEnter 中,设置元素开始动画之前的起始样式
                // 设置小球开始动画之前的 起始位置
                el.style.transform = "translate(0, 0)"   // 一开始的时候,让小球处于(0,0)的位置
            },
            enter(el, done) {
                // 【注意1】el.offsetWidth 这句话,没有实际的作用,但是,如果不写,出不来动画效果。可以认为 el.offsetWidth 会强制动画刷新 
                el.offsetWidth
                // enter 表示动画 开始之后的样式,这里,可以设置小球完成动画之后的,结束状态
                el.style.transform = "translate(150px, 300px)" // smyhvae 提示:让小球从(0,0)移动到 (150px, 300px)
                el.style.transition = 'all 1s ease'

                // 【注意2】这里的 done, 起始就是 afterEnter 这个函数,也就是说:done 是 afterEnter 函数的引用
                done()
            },
            afterEnter(el) {
                // 动画完成之后,会调用 afterEnter
                // 动画结束后,让小球消失(直接让 flag 取反即可)
                this.flag = !this.flag  // 因为最开始的时候,小球就是处于消失的状态,这行代码可以让小球的动画重新开始
            }
        }
    });
</script>

下载.gif

多个元素的联动过渡

需求:点击添加按钮后,给新增的 item 加个动画,其他的item也要是过渡的效果。 在实现列表过渡的时候,如果需要过渡的元素是v-for循环出来的,不能使用transition组件,需要使用transition-group组件。transition-group组件包裹的列表,必须声明key属性。

<style>
    .v-enter,.v-leave-to{
        opacity: 0;
        transform: translateY(80px);
    }

    .v-enter-active,.v-leave-active{
        transition: all 0.6s linear;
    }
</style>
<!-- 被transition-group包裹的元素,会默认套上一层span,可以通过tag属性来指定外层包裹的标签-->
<div id ="app">
        <label>ID:<input type="text" v-model="id"></label>
        <label>name:<input type="text" v-model="name"></label>
        <button @click="add">添加</button>
        <transition-group>
            <li v-for="(item,i) in list" :key="item.id">{{item.id}}---{{item.name}}---<button @click="del(i)">删除</button></li>
        </transition-group>
    </div>
 <script>
        //创建Vue实例,得到 ViewModel
        var vm = new Vue({
            el: '#app',
            data: {
                id:"",
                name:"",
                list:[
                    {id:1,name:"宋江"},
                    {id:2,name:"卢俊义"},
                    {id:3,name:"公孙胜"},
                    {id:4,name:"关胜"}
                ]
            },
            methods: {
                add(){
                    //将用户输入的值添加到list列表中
                    this.list.push({id:this.id,name:this.name});
                    //将输入框清空
                    this.id = this.name = ""
                },
                //删除函数:将元素从list中删去。
                del(i){
                    this.list.splice(i,1);
                }
            }
        });
    </script>

当我删除第2个item时,第3、第4个item在往上移动的过程比会较突兀

/* 能够实现列表后续的元素,渐渐地漂上来的效果 */
.v-leave-active {
    position: absolute;
}

改进transition-grouptag属性

上面的代码中,我们审查一下代码元素会发现,用transition-group包裹的元素,会被默认套上一层<span>: 这个<span>虽然没有太大副作用,但是不符合代码规范。为了解决这个问题,我们可以通过tag属性给transition-group包谷的元素套上一层<ul>,然后把现有的<ul>注释掉,就可以了。