1. 程式人生 > 實用技巧 >Vue.js 動畫(三)

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 {
            width: 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;
            width: 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>

參考文章: