本文為轉(zhuǎn)載,原文:Vue學(xué)習(xí)筆記進階篇——列表過渡及其他
本文將介紹Vue中的列表過渡,動態(tài)過渡, 以及可復(fù)用過渡是實現(xiàn)。
列表過渡
目前為止,關(guān)于過渡我們已經(jīng)講到:
- 單個節(jié)點
- 同一時間渲染多個節(jié)點中的一個
那么怎么同時渲染整個列表,比如使用 v-for
?在這種場景中,使用 <transition-group>
組件。在我們深入例子之前,先了解關(guān)于這個組件的幾個特點:
- 不同于
<transition>
, 它會以一個真實元素呈現(xiàn):默認為一個<span>
。你也可以通過tag
特性更換為其他元素。 - 內(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)