Vue.js 动画(三)

1. Vue 中的动画

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

  • 使用过渡类名:使用 transition 元素包裹要设置动画的元素
  • 第三方 CSS 动画库:animate.css
  • 在过渡钩子函数中使用 JavaScript 直接操作 DOM
  • 列表排序过渡:transition-group

1.1 单元素过渡

Vue 提供了 transition 的封装组件,在下列情形中,可以给任何元素和组件添加进入/离开过渡

  • 条件渲染 (使用 v-if)
  • 条件展示 (使用 v-show)
  • 动态组件
  • 组件根节点

1.1.1 过渡类名

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

  • v-enter:定义进入过渡的开始状态。在元素被插入之前生效,在元素被插入之后的下一帧移除。
  • v-enter-active:定义进入过渡生效时的状态。在整个进入过渡的阶段中应用,在元素被插入之前生效,在过渡/动画完成之后移除。这个类可以被用来定义进入过渡的过程时间,延迟和曲线函数。
  • v-enter-to: 2.1.8版及以上 定义进入过渡的结束状态。在元素被插入之后下一帧生效 (与此同时 v-enter 被移除),在过渡/动画完成之后移除。
  • v-leave: 定义离开过渡的开始状态。在离开过渡被触发时立刻生效,下一帧被移除。
  • v-leave-active:定义离开过渡生效时的状态。在整个离开过渡的阶段中应用,在离开过渡被触发时立刻生效,在过渡/动画完成之后移除。这个类可以被用来定义离开过渡的过程时间,延迟和曲线函数。
  • v-leave-to: 2.1.8版及以上 定义离开过渡的结束状态。在离开过渡被触发之后下一帧生效 (与此同时 v-leave 被删除),在过渡/动画完成之后移除。

示例:

h3 元素设置进入退出动画:

<style>
    /* 自定义两组样式,来控制 transition 内部的元素实现动画 */
    /* v-enter 时间点,元素进入之前的其实状态,此时还未正式进入 */
    /* v-leave-to 时间点,动画离开之后,离开的终止状态,此时,元素动画已结束 */
    .v-enter,
    .v-levae-to {
        opacity: 0;     /*透明度*/
        transform: translateX(150px);       /*元素从 150px 处飘过来,x 轴*/
    }

    /* v-enter-active 入场动画的时间段 */
    /* v-levave-active 离场动画的时间段 */
    .v-enter-active,
    .v-levae-activue {
        transition: all 0.8s ease;
    }
</style>

<body>
    <div id="app">
        <input type="button" value="按钮" @click="flag=!flag">
        <transition>
            <h3 v-if="flag">标题三</h3>
        </transition>
    </div>

    <script src="./lib/vue-2.4.0.js"></script>
    <script>
        var vm = new Vue({
            el: '#app',
            data: {
                flag: false
            },
            methods: {}
        })
    </script>
</body>

1.1.2 修改过渡类名前缀给多个元素设置动画

若有多个元素需要设置不同的动画,如果使用同一种过渡类名,那么动画将是一致的,这时就需要修改过渡类名的前缀 + transitionname 属性来实现。

<style>
    /* 自定义 v 前缀 */
    .my-enter,
    .my-leave-to {
        opacity: 0;
        transform: translateY(70px);        /*从上飞入*/
    }

    .my-enter-active,
    .my-leave-active {
        transition: all 0.8s ease;
    }
</style>

<body>
    <div id="app">
        <input type="button" value="按钮" @click="flag=!flag">
        <transition>
            <h3 v-if="flag">标题三</h3>
        </transition>

        <input type="button" value="按钮2" @click="flag2=!flag2">
        <transition name="my">
            <h1 v-if="flag2">标题一</h3>
        </transition>
    </div>

    <script src="./lib/vue-2.4.0.js"></script>
    <script>
        var vm = new Vue({
            el: '#app',
            data: {
                flag: false,
                flag2: false
            },
            methods: {}
        })
    </script>
</body>

1.2 第三方 CSS 动画库 animate

1、导入动画类库:

<link rel="stylesheet" type="text/css" href="./lib/animate.css">

2、定义 transition 及属性:

<transition
	enter-active-class="fadeInRight"
    leave-active-class="fadeOutRight"
    :duration="{ enter: 500, leave: 800 }">
  	<div class="animated" v-show="isshow">动画哦</div>
</transition>

示例:

<!DOCTYPE html>
<html lang="">

<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Title Page</title>
    <link rel="stylesheet" href="./lib/animate.css">
</head>

<body>
    <div id="app">
        <input type="button" value="按钮" @click="flag=!flag">
        <!-- 需求: 点击按钮,让 h3 显示,再点击,让 h3 隐藏 -->
        <!-- <transition enter-active-class="animated bounceIn" leave-active-class="animated bounceOut">
      <h3 v-if="flag">这是一个H3</h3>
    </transition> -->

        <!-- 使用 :duration="毫秒值" 来统一设置 入场 和 离场 时候的动画时长 -->
        <!-- <transition enter-active-class="bounceIn" leave-active-class="bounceOut" :duration="200">
      <h3 v-if="flag" class="animated">这是一个H3</h3>
    </transition> -->

        <!-- 使用  :duration="{ enter: 200, leave: 400 }"  来分别设置 入场的时长 和 离场的时长  -->
        <transition enter-active-class="bounceIn" leave-active-class="bounceOut" :duration="{ enter: 200, leave: 400 }">
            <h3 v-if="flag" class="animated">标题三</h3>
        </transition>
    </div>

    <script src="./lib/vue-2.4.0.js"></script>
    <script>
        var vm = new Vue({
            el: '#app',
            data: {
                flag: false,
            },
            methods: {}
        })
    </script>
</body>

</html>

参考文档:

1.3 JavaScript 钩子函数

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

JS 中调用:

// ...
methods: {
  // --------
  // 进入中
  // --------

  beforeEnter: function (el) {
    // ...
  },
  // 当与 CSS 结合使用时
  // 回调函数 done 是可选的
  enter: function (el, done) {
    // ...
    done()
  },
  afterEnter: function (el) {
    // ...
  },
  enterCancelled: function (el) {
    // ...
  },

  // --------
  // 离开时
  // --------

  beforeLeave: function (el) {
    // ...
  },
  // 当与 CSS 结合使用时
  // 回调函数 done 是可选的
  leave: function (el, done) {
    // ...
    done()
  },
  afterLeave: function (el) {
    // ...
  },
  // leaveCancelled 只用于 v-show 中
  leaveCancelled: function (el) {
    // ...
  }
}

示例:

利用 JavaScript 钩子函数实现小球半场动画,模拟加入购物车场景:

<!DOCTYPE html>
<html lang="">

<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Title Page</title>
    <style>
        .ball {
             15px;
            height: 15px;
            border-radius: 50%;
            background-color: red;
        }
    </style>
</head>

<body>
    <div id="app">
        <input type="button" value="按钮" @click="flag=!flag">
        <!-- 使用 transition 元素将小球包裹起来 -->
        <transition 
            @before-enter="beforeEnter" 
            @enter="enter" 
            @after-enter="afterEnter">
            <div class="ball" v-show="flag"></div>
        </transition>
    </div>

    <script src="./lib/vue-2.4.0.js"></script>
    <script>
        var vm = new Vue({
            el: '#app',
            data: {
                flag: false,
            },
            methods: {
                beforeEnter(el) {
                    // 动画入场之前,动画尚未开始,用于设置元素开始动画之前的起始样式
                    // 设置小球开始动画之前的起始位置
                    el.style.transform = "translate(0, 0)"      // 起始坐标
                },
                enter(el, done) {
                    el.offsetWidth      // el.offsetWidth 会强制动画刷新
                    
                    // 动画开始之后的样式,设置小球动画之后的,结束状态
                    el.style.transform = "translate(150px, 450px)"      // 结束坐标
                    el.style.transition = "all 1s ease"

                    done()      // 起始就是 afterEnter 这个函数,也就是说:done 是 afterEnter 函数的引用
                },
                afterEnter(el) {
                    // 动画完成后,隐藏小球,flag 初始值为 false,点击按钮后为 true,!flag 即为 false
                    this.flag = !this.flag
                }
            }
        })
    </script>
</body>

</html>

Tips:当只用 JavaScript 过渡的时候,在 enter 和 leave 中必须使用 done 进行回调。否则,它们将被同步调用,过渡会立即完成。

参考文章:

1.4 列表过渡

目前为止,关于过渡动画我们已经了解了:

  • 单个节点
  • 同一时间渲染多个节点中的一个

若要渲染整个列表,使用 <transition-group> 组件,组件特点:

  • <transition-group> 会模块渲染为一个 <span> 标签,若想渲染为其他标签,可以使用 tag 属性更换,如:tag="ul"
  • 内部元素需要提供唯一的 key 属性值
  • CSS 过渡类将会应用在内部元素中,而不是这个组/容器本身

示例:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link rel="stylesheet" href="./lib/bootstrap-3.3.7.css">
    <style>
        li {
            border: 1px dashed #999;
            margin: 5px;
            line-height: 35px;
            padding-left: 5px;
            font-size: 12px;
             100%;
        }

        li:hover {
            background-color: hotpink;
            transition: all 0.8s ease;
        }

        /* 过渡开始状态,以及进入过渡的结束状态 */
        .v-enter,
        .v-leave-to {
            opacity: 0;
            transform: translateY(80px);
        }

        /* 进入过渡生效时的状态,离开过渡生效时的状态 */
        .v-enter-active,
        .v-leave-active {
            transition: all 0.6s ease;
        }

        /* 下面的 .v-move 和 .v-leave-active 配合使用,能够实现列表后续的元素,渐渐地漂上来的效果 */
        .v-move {
            transition: all 0.6s ease;
        }

        .v-leave-active {
            position: absolute;
        }
    </style>
</head>

<body>
    <div id="app">
        <div class="col-md-8">
            <div>
                <label>
                    Id:
                    <input type="text" v-model="id">
                </label>

                <label>
                    Name:
                    <input type="text" v-model="name" @keyup.enter="add">
                </label>

                <input type="button" value="添加" @click="add">
            </div>

            <!-- 使用 transition-group 包裹要过渡的列表 -->
            <!-- v-for 循环创建元素,设置动画,且必须为每一个元素设置 :key 属性 -->
            <!-- appear 属性,实现页面展示时,入场时的结果 -->
            <transition-group appear tag="ul">
                <li v-for="(item, i) in list" :key="item.id" @click="del(i)">
                    {{ item.id }} --- {{ item.name }}
                </li>
            </transition-group>

        </div>

    </div>

    <script src="./lib/vue-2.4.0.js"></script>
    <script>
        var vm = new Vue({
            el: '#app',
            data: {
                id: "",
                name: "",
                list: [
                    { id: 1, name: '赵高' },
                    { id: 2, name: '秦桧' },
                    { id: 3, name: '严嵩' },
                    { id: 4, name: '魏忠贤' }
                ]
            },
            methods: {
                add() {
                    this.list.push({ id: this.id, name: this.name })
                    this.id = this.name = ''
                },
                del(i) {
                    this.list.splice(i, 1)
                }
            }
        })
    </script>
</body>

</html>

参考文章:

原文地址:https://www.cnblogs.com/midworld/p/13611037.html