Vue學(xué)習(xí)筆記進階篇——列表過渡及其他

本文為轉(zhuǎn)載,原文:Vue學(xué)習(xí)筆記進階篇——列表過渡及其他
本文將介紹Vue中的列表過渡,動態(tài)過渡, 以及可復(fù)用過渡是實現(xiàn)。

列表過渡

目前為止,關(guān)于過渡我們已經(jīng)講到:

  1. 單個節(jié)點
  2. 同一時間渲染多個節(jié)點中的一個

那么怎么同時渲染整個列表,比如使用 v-for ?在這種場景中,使用 <transition-group>組件。在我們深入例子之前,先了解關(guān)于這個組件的幾個特點:

  1. 不同于 <transition>, 它會以一個真實元素呈現(xiàn):默認為一個<span>。你也可以通過 tag 特性更換為其他元素。
  2. 內(nèi)部元素 總是需要 提供唯一的 key屬性值.

列表的進入和離開過渡

現(xiàn)在讓我們由一個簡單的例子深入,進入和離開的過渡使用之前一樣的 CSS 類名。

<div id="app1">
    <button @click="add">Add</button>
    <button @click="remove">Remove</button>
    <transition-group name="list" tag="p">
        <span v-for="item in items" :key="item" class="list-item">
            {{item}}
        </span>
    </transition-group>
</div>
.list-item{
            display: inline-block;
            margin-right: 10px;
        }
        .list-enter-active, .list-leave-active{
            transition: all 1s;
        }
        .list-enter, .list-leave-to{
            opacity: 0;
            transform: translateY(30px);
        }
var app1 = new Vue({
    el:'#app1',
    data:{
        items:[1,2,3,4,5,6,7,8,9],
        nextNum:10
    },
    methods:{
        randomIndex:function () {
            return Math.floor(Math.random() * this.items.length)
        },
        add:function () {
            this.items.splice(this.randomIndex(), 0, this.nextNum++)
        },
        remove:function () {
            this.items.splice(this.randomIndex(), 1)
        }
    }
})

運行結(jié)果:



這個例子有個問題,當添加和移除元素的時候,周圍的元素會瞬間移動到他們的新布局的位置,而不是平滑的過渡,我們下面會解決這個問題。

列表的位移過渡

<transition-group> 組件還有一個特殊之處。不僅可以進入和離開動畫,還可以改變定位。要使用這個新功能只需了解新增的v-move 特性,它會在元素的改變定位的過程中應(yīng)用。像之前的類名一樣,可以通過 name 屬性來自定義前綴,也可以通過 move-class 屬性手動設(shè)置。
v-move對于設(shè)置過渡的切換時機和過渡曲線非常有用,你會看到如下的例子:

<div id="app2">
    <button @click="shuffle">Shuffle</button>
    <transition-group name="flip-list" tag="ul">
        <li v-for="item in items" :key="item">
            {{item}}
        </li>
    </transition-group>
</div>
.flip-list-move {
     transition: transform 1s;
}
var app2 = new Vue({
    el:'#app2',
    data:{
        items:[1,2,3,4,5,6,7,8,9]
    },
    methods:{
        shuffle:function () {
            this.items = _.shuffle(this.items)
        }
    }
})

這個例子需要添加以下引用

<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.14.1/lodash.min.js"></script>

運行結(jié)果:



這個看起來很神奇,內(nèi)部的實現(xiàn),Vue 使用了一個叫 FLIP 簡單的動畫隊列
使用 transforms 將元素從之前的位置平滑過渡新的位置。
我們將之前實現(xiàn)的例子和這個技術(shù)結(jié)合,使我們列表的一切變動都會有動畫過渡。

<div id="app3" class="demo">
    <button @click="shuffle">Shuffle</button>
    <button @click="add">Add</button>
    <button @click="remove">Remove</button>
    <transition-group name="list-complete" tag="p">
        <span v-for="item in items" :key="item" class="list-complete-item">
            {{item}}
        </span>
    </transition-group>
</div>
.list-complete-item{
    transition: all 1s;
    display: inline-block;
    margin-right: 10px;
}
.list-complete-enter, .list-complete-leave-to{
    opacity: 0;
    transform: translateY(30px);
}
.list-complete-leave-active{
    position: absolute;
}
var app3 = new Vue({
    el:'#app3',
    data:{
        items:[1,2,3,4,5,6,7,8,9],
        nextNum:10
    },
    methods:{
        shuffle:function () {
            this.items = _.shuffle(this.items)
        },
        randomIndex:function () {
            return Math.floor(Math.random() * this.items.length)
        },
        add:function () {
            this.items.splice(this.randomIndex(), 0, this.nextNum++)
        },
        remove:function () {
            this.items.splice(this.randomIndex(), 1)
        }
    }
})

運行結(jié)果:


列表的漸進過渡

通過 data 屬性與 JavaScript 通信 ,就可以實現(xiàn)列表的漸進過渡:

<div id="app4">
    <input v-model="query">
    <transition-group
        name="staggered-fade"
        tag="ul"
        :css="false"
        @before-enter="beforeEnter"
        @enter="enter"
        @leave="leave">
        <li v-for="(item, index) in computedList"
            :key="item.msg"
            :data-index="index">
            {{item.msg}}
        </li>
    </transition-group>
</div>
var app4 = new Vue({
    el:'#app4',
    data:{
        query:'',
        list:[
            {msg:'Bruce Lee'},
            {msg:'Jackie Chan'},
            {msg:'Chuck Norris'},
            {msg:'Jet Li'},
            {msg:'Kung Furry'},
            {msg:'Chain Zhang'},
            {msg:'Iris Zhao'},
        ]
    },
    computed:{
        computedList:function () {
            var vm = this
            return this.list.filter(function (item) {
                return item.msg.toLowerCase().indexOf(vm.query.toLowerCase()) !== -1
            })
        }
    },
    methods:{
        beforeEnter:function (el) {
            el.style.opacity = 0
            el.style.height = 0
        },
        enter:function (el, done) {
            var delay = el.dataset.index * 150
            setTimeout(function () {
                Velocity(el, {opacity:1, height:'1.6em'},{complete:done})
            }, delay)
        },
        leave:function (el, done) {
            var delay = el.dataset.index * 150
            setTimeout(function () {
                Velocity(el, {opacity:0, height:0}, {complete:done})
            }, delay)
        }
    }
})

上述js代碼需要添加對Velocity引用:

<script src="https://cdnjs.cloudflare.com/ajax/libs/velocity/1.2.3/velocity.min.js"></script>

運行結(jié)果如下:


可復(fù)用的過渡

過渡可以通過 Vue 的組件系統(tǒng)實現(xiàn)復(fù)用。要創(chuàng)建一個可復(fù)用過渡組件,你需要做的就是將<transition>或者 <transition-group>作為根組件,然后將任何子組件放置在其中就可以了。
下面的例子是將上一個列表漸進過渡的例子改為可復(fù)用的過渡的源碼:

<div id="app5">
    <input v-model="query">
    <my-transition :query="query" :list="list">
        <li v-for="(item, index) in computedList"
            :key="item.msg"
            :data-index="index">
            {{item.msg}}
        </li>
    </my-transition>
</div>
Vue.component('my-transition', {
    template:`
    <transition-group
        name="staggered-fade"
        tag="ul"
        :css="false"
        @before-enter="beforeEnter"
        @enter="enter"
        @leave="leave">
        <slot></slot>
    </transition-group>`,
    props:['query', 'list'],
    methods:{
        beforeEnter:function (el) {
            el.style.opacity = 0
            el.style.height = 0
        },
        enter:function (el, done) {
            var delay = el.dataset.index * 150
            setTimeout(function () {
                Velocity(el, {opacity:1, height:'1.6em'},{complete:done})
            }, delay)
        },
        leave:function (el, done) {
            var delay = el.dataset.index * 150
            setTimeout(function () {
                Velocity(el, {opacity:0, height:0}, {complete:done})
            }, delay)
        }
    }
})

var app5 = new Vue({
    el:'#app5',
    data:{
        query:'',
        list:[
            {msg:'Bruce Lee'},
            {msg:'Jackie Chan'},
            {msg:'Chuck Norris'},
            {msg:'Jet Li'},
            {msg:'Kung Furry'},
            {msg:'Chain Zhang'},
            {msg:'Iris Zhao'},
        ]
    },
    computed:{
        computedList:function () {
            var vm = this
            return this.list.filter(function (item) {
                return item.msg.toLowerCase().indexOf(vm.query.toLowerCase()) !== -1
            })
        }
    },
})

效果與上一個例子一致:



但是函數(shù)組件更適合完成這個任務(wù)。由于暫時還沒有學(xué)到render函數(shù),所以暫時先不實現(xiàn)render函數(shù)組件。后面學(xué)到的時候再做打算。

動態(tài)過渡

在 Vue 中即使是過渡也是數(shù)據(jù)驅(qū)動的!動態(tài)過渡最基本的例子是通過 name 特性來綁定動態(tài)值。

<transition v-bind:name="transitionName">
  <!-- ... -->
</transition>

當你想用 Vue 的過渡系統(tǒng)來定義的 CSS 過渡/動畫 在不同過渡間切換會非常有用。
所有的過渡特性都是動態(tài)綁定。它不僅是簡單的特性,通過事件的鉤子函數(shù)方法,可以在獲取到相應(yīng)上下文數(shù)據(jù)。這意味著,可以根據(jù)組件的狀態(tài)通過 JavaScript 過渡設(shè)置不同的過渡效果。

<div id="app6">
    Fade In:
    <input type="range" v-model="fadeInDuration" min="0" :max="maxFadeDuration">
    Fade Out:
    <input type="range" v-model="fadeOutDuration" min="0" :max="maxFadeDuration">
    <transition
        v-bind:css="false"
        @before-enter="beforeEnter"
        @enter="enter"
        @leave="leave">
        <p v-if="show">hello chain</p>
    </transition>
    <button @click="stop = true">Stop it</button>
</div>
var app6 = new Vue({
    el: '#app6',
    data: {
        show: true,
        fadeInDuration: 1000,
        fadeOutDuration: 1000,
        maxFadeDuration: 1500,
        stop: false
    },
    mounted: function () {
        this.show = false
    },
    methods: {
        beforeEnter: function (el) {
            el.style.opacity = 0
        },
        enter: function (el, done) {
            var vm = this
            Velocity(el,
                { opacity: 1 },
                {
                    duration: this.fadeInDuration,
                    complete: function () {
                        done()
                        if (!vm.stop) vm.show = false
                    }
                }
            )
        },
        leave: function (el, done) {
            var vm = this
            Velocity(el,
                { opacity: 0 },
                {
                    duration: this.fadeOutDuration,
                    complete: function () {
                        done()
                        vm.show = true
                    }
                }
            )
        }
    }
})

運行結(jié)果:


其中例子里的mounted是在Vue掛載完成,也就是模板中的html渲染到html頁面中時的一個鉤子函數(shù),只會執(zhí)行一次。具體內(nèi)容可以理解下Vue的生命周期,這里就不贅述了。
但是如果這里不使用mounted的話,也是可以用初始渲染來實現(xiàn),只不過比較麻煩。實現(xiàn)的方法是:
在transition中加入appear鉤子函數(shù):@appear="appear",然后在vue實例的methods中添加appear方法:

        appear: function (el, done) {
            var vm = this
            Velocity(el,
                { opacity: 1 },
                {
                    duration: this.fadeInDuration,
                    complete: function () {
                        done()
                        if (!vm.stop) vm.show = false
                    }
                }
            )
        }

本文為原創(chuàng),轉(zhuǎn)載請注明出處
上一節(jié):Vue學(xué)習(xí)筆記進階篇——多元素及多組件過渡
返回目錄
下一節(jié):Vue學(xué)習(xí)筆記進階篇——過渡狀態(tài)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,923評論 6 535
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,740評論 3 420
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,856評論 0 380
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,175評論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,931評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,321評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,383評論 3 443
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,533評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,082評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,891評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,067評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,618評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,319評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,732評論 0 27
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,987評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,794評論 3 394
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,076評論 2 375

推薦閱讀更多精彩內(nèi)容