第4章 Vue 過渡和動畫

Earnest~發表於2020-11-29

第4章 Vue 過渡和動畫

4.1 過渡和動畫基礎

transition 元件

注 意   t r a n s i t i o n   同 一 時 間 只 能 顯 示 一 個 元 素 注意\ transition\ 同一時間只能顯示一個元素  transition 

把需要新增過渡的 div 放入其中,用 name 設定字首(預設字首是 v)

<transition name='box'>
	<div class='chart' v-if='isShow'></div>
</transition>

初始狀態

<style>
    .chart {
        width: 200px;
        height: 50px;
        background-color: orangered;
    }
    .box-enter-active, .box-leave-active {
        transition: width 3s;
    }
    .box-enter, .box-leave-to {
        width: 0px;
    }
    .box-enter-to, .box-leave {
        width: 200px;
    }
</style>

自定義類名

通 過 屬 性 來 設 置 ( 自 定 義 類 名 結 合   a n i m a t e . c s s   動 畫 庫 ) 通過屬性來設定(自定義類名結合\ animate.css\ 動畫庫)  animate.css 

<transition enter-active-class='animated bounceInLeft'
    leave-active-class='animated bounceOutLeft'>
    <p v-if='isShow'>hello world</p>
</transition>

a p p e a r   初 始 渲 染 動 畫 ( 每 次 刷 新 頁 面 ) appear\ 初始渲染動畫(每次重新整理頁面) appear 

<link rel="stylesheet" href="animate.css">
<transition appear appear-class='animated swing'
appear-to-class='animated bounceIn'
appear-active-class='animated bounceOut'>
    <div v-if='isShow's>過渡文字</div>
</transition>

使用@keyframes 建立 CSS 動畫

動 畫 和 過 渡 的 區 別 就 在 於 , 動 畫 可 以 改 中 間 內 容 , 一 幀 一 幀 的 改 動畫和過渡的區別就在於,動畫可以改中間內容,一幀一幀的改

<style>
    /* .bounce */
    .box-enter-active {         
        animation: Ami 2.5s;	/* 動畫時間 */
    }
    .box-leave-active {
        animation: Ami 2.5s;
    }
    @keyframes Ami {
        0% {transform: scale(0); background: orangered;}
        20% {transform: scale(1); background: burlywood;}
        40% {transform: scale(1.25); background: blue;}
        60% {transform: scale(1.3); background: burlywood;}
        80% {transform: scale(1.25); background: orangered;}
        100% {transform: scale(1); background: orange;}
    }

Vue 結合 Velocity.js 實現動畫

用 了 鉤 子 函 數 和   V e l o c i t y . j s 用了鉤子函式 和\ Velocity.js  Velocity.js

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

<transition @before-enter='beforeEnter' @enter='enter' 
@leave='leave' v-bind:css='false'>
    <p v-if='isShow'>demo 05</p>
</transition>

<script>
methods: {
    beforeEnter(el) {
        el.style.opacity = 0
        el.style.transformOrigin = 'left'    // 旋轉元素的基點位置
        el.style.color = 'red'
    },
    enter(el, done) {
        Velocity(el, {opacity: 1, fontSize: '1.4em'}, {duration: 300})  // duration 執行時間
        Velocity(el, {fontSize: '1em'}, {complete: done})
    },
    leave(el, done) {
        Velocity(el, {translateX: '15px', rotateZ: '50deg'}, {duration: 3000})
        Velocity(el, {rotateZ: '100deg'}, {loop: 2})    // 重複 2 次
        Velocity(el, {rotateZ: '45deg', translateY: '30px', translateX: '30px',
            opacity: 0}, {complete: done})
    }
}	// 哦,天爺,自己琢磨吧。。(這個 velocity 用的是老版本的)

使 Vue 跳過 CSS 的檢測

v-bind:css="false"

4.2 多個元素過渡

不同簽名元素的過渡

用   v − i f   和   v − e l s e 用\ v-if\ 和\ v-else  vif  velse

<transition>
    <div v-if='1 > 0'>div</div>
    <p v-else>p</p>
</transition>

相同簽名元素的過渡

需 要   k e y , 不 然 無 法 區 分 相 同 籤 名 的 元 素 需要\ key,不然無法區分相同簽名的元素  key

<button @click='isShow = !isShow'>change isShow</button>
<transition name='fade'>
    <button v-if='isShow' key='save'>save</button>
    <button v-else key='edit'>edit</button>
</transition>

通 過 改   k e y   的 值 , 實 現 按 鈕 切 換 通過改\ key\ 的值,實現 按鈕切換  key 

<button v-bind:key='isShow'>{{isShow ? 'true' : 'false'}} </button>

還 可 以 這 樣 用   v − i f 還可以這樣用\ v-if  vif

<div class="red" v-if="show == 'A'" key="A"></div>
<div class="blue" v-if="show == 'B'" key="B"></div>
<div class="yellow" v-if="show == 'C'" key="C"></div>
<script>
    showNum () {
        if (this.show == 'A') { return this.show = 'B' }
    } // 切換 show

還 可 以 用   c o m p u t e d   計 算 屬 性 還可以用\ computed\ 計算屬性  computed 

{{showNum}}
computed: {
    showNum() {
        switch(this.isShow) {
            case 'A': return 'a'
// 不再贅述

過渡模式

為 實 現 有 序 過 渡 , 加 了   m o d e   屬 性 為實現有序過渡,加了\ mode\ 屬性  mode 

mode 屬性有兩個值:in-out 和 out-in,一般用 out-in,先出後進(即當前元素先過渡,然後新元素過渡進入)
<transition name='fade' mode='out-in'>
    <button v-bind:key='isOff' @click='isOff = !isOff'>
        {{isOff ? 'Off' : 'On'}}
    </button>
</transition>
// 用了 mode 和 out-in 以後,很絲滑

4.3 多個元件過渡

不 需 要 用   k e y   特 性 , 用 動 態 組 件 即 可 ( 即   i s   屬 性 ) 不需要用\ key\ 特性,用動態元件即可(即\ is\ 屬性)  key  is 

// 主要就是用的 is 切換的元件
<style>
    .fade-enter-active, .fade-leave-active {
        transition: opacity .5s ease;
    }
    .fade-enter, .fade-leave-to {
        opacity: 0;
    }
</style>
<body>
    <div id='app'>
        <a href="javascript:;" @click='compontentName="example1"'>login</a>
        <a href="javascript:;" @click='compontentName="example2"'>register</a>
        <transition name='fade' mode='in-out'>
            <component v-bind:is='compontentName'></component>
        </transition>
    </div>
    <template id='ex1'> <span>login component</span> </template>
    <template id='ex2'> <span>register component</span> </template>
    <script>
        Vue.component('example1', {template: '#ex1'})
        Vue.component('example2', {template: '#ex2'})
        var vm = new Vue({
            el: '#app',
            data: { compontentName: '' }
        })
    </script>
</body>

4.4 列表過渡

前面的都是 渲染單個元素或同時渲染多個元素中的一個,不適用於列表過渡

列表的進入和離開過渡

列 表 過 渡 用   v − f o r   和   t r a n s i t i o n − g r o u p 列表過渡用\ v-for\ 和\ transition-group  vfor  transitiongroup

.list-item {
    display: inline-block; margin-right: 10px; background-color: orangered;
    border-radius: 50%; width: 25px; height: 25px; text-align: center;
    line-height: 25px; color: #fff;
}
.list-enter-active, .list-leave-active {
    transition: all 1s;
}
.list-enter, .list-leave-to {
    opacity: 0;
    transform: translateY(30px)
}

<transition-group name='list' tag='div'>
    <span v-for='item in items' v-bind:key='item' class='list-item'>
        {{item}}
    </span>
</transition-group>

data: {
    items: [1, 2, 3, 4, 5],
    nextNum: 6
},
methods: {
    randomIndex() {
        return Math.floor(Math.random() * this.items.length)    // 0-1 之間的隨機數
    },
    add() {
        this.items.splice(this.randomIndex(), 0, this.nextNum++)    // 刪除 0 個
    },
    remove() {
        this.items.splice(this.randomIndex(), 1)
    }
}

列表的排序過渡

為 了 實 現 平 滑 過 渡 , 用   v − m o v e   ( V u e   採 用 了   F L I P   技 術 , 動 畫 更 流 暢 ) 為了實現平滑過渡,用\ v-move\ (Vue\ 採用了\ FLIP\ 技術,動畫更流暢)  vmove (Vue  FLIP )

.list-leave-active {
    transition: all 1s;
    position: absolute;		// leave 這裡要加一個絕對定位,否則不平滑
}
.list-move {
    transition: transform 1s;
}
  • 效果圖:
test

利用 lodash.js 實現洗牌功能

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

<script>
shuffle() {
    this.items = _.shuffle(this.items)
}
  • 效果圖:
test2

列表的交錯過渡

鉤 子 函 數 結 合   V e l o c i t y . j s   實 現 搜 索 功 能 鉤子函式結合\ Velocity.js\ 實現搜尋功能  Velocity.js 

<input type="text" placeholder="please enter what you are looking for:" v-model='query'>
<transition-group name='item' tag='ul' @before-enter='beforeEnter' @enter='enter'
@leave='leave' :css='false'>
    <li v-for='(item, index) in ComputedList' :key='item.msg' :data-index='index'>
        {{item.msg}}
    </li>
</transition-group>

<script>
data: {
    query: '',
    items: [
        {msg: 'zhangsan'}, {msg: 'lisi'}, {msg: 'zhangfangfang'},
        {msg: 'wanglinlin'}, {msg: 'fengyuan'}
    ]
},
computed: {
    ComputedList() {
        var content = this.query
        var nameList = this.items
        return nameList.filter(function (item) {
            return item.msg.toLowerCase().indexOf(content.toLowerCase()) !== -1
        })		// 利用計算屬性 返回過濾後的列表(研究下這個 return)
    }
},
methods: {
    beforeEnter(el) {
        el.style.opacity = 0
        el.style.height = 0
    },
    enter(el, done) {
        var delay = el.dataset.index * 150
        setTimeout(function() {
            Velocity(el, {opacity: 1, height: '1.6em'}, {complete: done})
        }, delay)
    },
    leave(el, done) {
        var delay = el.dataset.index * 150
        setTimeout(function() {
            Velocity(el, {opacity: 0, height: 0}, {complete: done})
        }, delay)
    }
}
// 這樣看來,寫的時候可以只用三個鉤子函式,就可以把進出的過渡寫完(無 css 參與,用的 Velocity.js)
// 還是用 css 吧,只需給 v-enter-active & v-leave-active 寫個 transition,
// 以及給 v-enter & v-leave-to 寫個 opacity 即可
  • 效果圖:
test

可複用的過渡

t e m p l a t e   方 式 實 現 過 渡 的 封 裝 template\ 方式實現過渡的封裝 template 

就是寫元件模板裡面,用外掛 slot 顯示列表

<fade :query='query' :items='items'>
    <li v-for='(item, index) in ComputedList' :key='item.msg' :data-index='index'>
        {{item.msg}}
    </li>
</fade>

<template id="temp">
    <transition-group name='item' tag='ul' @before-enter='beforeEnter' @enter='enter'
    @leave='leave' :css='false'>
        <!-- <li v-for='(item, index) in ComputedList' :key='item.msg' :data-index='index'>
            {{item.msg}}
        </li> 用 slot -->
        <slot></slot>
    </transition-group>
</template>

<script>
Vue.component('fade', {
    props: ['query', 'items'],
    template: '#temp',
    methods: {
        beforeEnter(el) {
            el.style.opacity = 0
            el.style.height = 0
        },
        enter(el, done) {
            var delay = el.dataset.index * 150
            setTimeout(function() {
                Velocity(el, {opacity: 1, height: '1.6em'}, {complete: done})
            }, delay)
        },
        leave(el, done) {
            var delay = el.dataset.index * 150
            setTimeout(function() {
                Velocity(el, {opacity: 0, height: 0}, {complete: done})
            }, delay)
        }
    }
})

函 數 式 組 件 方 式 , 就 是 用   r a n d e r   函 數 來 實 現 函式式元件方式,就是用\ rander\ 函式來實現  rander 

改一下 script 的內容為如下即可,效果如前(用了 render)

<script>
Vue.component('fade', {
    functional: true,       // 標記 fade 元件為函式式元件
    props: ['query', 'items'],
    render(h, ctx) {
        var data = {
            props: {
                tag: 'ul',      // 預設 span
                css: false
            },
            on: {
                beforeEnter(el) {
                    el.style.opacity = 0
                    el.style.height = 0
                },
                enter(el, done) {
                    var delay = el.dataset.index * 150
                    setTimeout(function() {
                        Velocity(el, {opacity: 1, height: '1.6em'}, {complete: done})
                    }, delay)
                },
                leave(el, done) {
                    var delay = el.dataset.index * 150
                    setTimeout(function() {
                        Velocity(el, {opacity: 0, height: 0}, {complete: done})
                    }, delay)
                }
            }
        }
        return h('transition-group', data, ctx.children)
    }
})

E n d . End. End.

相關文章