前面的話
本文將詳細介紹Vue列表過渡
概述
前面分別介紹了單元素CSS過渡和JS過渡,以及多元素過渡。如何同時渲染整個列表呢?在這種情景中,需要使用<transition-group>組件
【<transition-group>】
<transition-group>不同于 <transition>
, 它會以一個真實元素呈現:默認為一個 <span>
。也可以通過 tag
特性更換為其他元素。而且其內部元素總是需要提供唯一的 key
屬性值
<transition-group name="list" tag="p"> <!-- ... --> </transition-group>
普通過渡
下面是一個添加和刪除列表項的例子
<style> .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);} </style>
<div id="list-demo" class="demo"> <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>
<script> new Vue({ el: '#list-demo', data: { items: [1,2,3,4,5,6,7,8,9], nextNum: 10 }, methods: { randomIndex() { return Math.floor(Math.random() * this.items.length) }, add() { this.items.splice(this.randomIndex(), 0, this.nextNum++) }, remove() { this.items.splice(this.randomIndex(), 1) }, } }) </script>
平滑過渡
上面這個例子有個問題,當添加和移除元素的時候,周圍的元素會瞬間移動到他們的新布局的位置,而不是平滑的過渡
【v-move】
<transition-group>
組件還有一個特殊之處。不僅可以進入和離開動畫,還可以改變定位。要使用這個新功能只需了解新增的 v-move
特性,它會在元素的改變定位的過程中應用。像之前的類名一樣,可以通過 name
屬性來自定義前綴,也可以通過 move-class
屬性手動設置
在上面代碼中基礎上,做出如下改進:
1、增加.list-move的樣式,使元素在進入時實現過渡效果
2、在.list-leave-active中設置絕對定位,使元素在離開時實現過渡效果
<style> .list-item {display: inline-block;margin-right: 10px;} .list-move,.list-enter-active, .list-leave-active {transition: 1s;} .list-leave-active{position:absolute;} .list-enter, .list-leave-to{opacity: 0;transform: translateY(30px);} </style>
變換過渡
下面接著利用move屬性,進行變換過渡,即一個列表中的列表項既不增加也不減少,只是不斷地變換其位置
<style> .list-move{transition: transform 1s;} </style>
<div id="list-demo" class="demo"> <button @click="shuffle">shuffle</button> <transition-group name="list" tag="ul"> <li v-for="item in items" :key="item">{{item}}</li> </transition-group> </div>
<script> new Vue({ el: '#list-demo', data: { items: [1,2,3,4,5,6,7,8,9], }, methods: { shuffle(){ this.items = this.items.sort(()=>{return Math.random() - 0.5;}) }, } }) </script>
下面的效果看起來很神奇,內部的實現,Vue 使用了一個叫 FLIP 簡單的動畫隊列,使用 transforms 將元素從之前的位置平滑過渡新的位置
下面將進入離開的例子和這個技術結合, 使列表的一切變動都會有動畫過渡
[注意]使用 FLIP 過渡的元素不能設置為 display: inline
。作為替代方案,可以設置為 display: inline-block
或者放置于 flex 中
<style> .list-item {display: inline-block;margin-right: 10px;} .list-move,.list-enter-active, .list-leave-active {transition: 1s;} .list-leave-active{position:absolute;} .list-enter, .list-leave-to{opacity: 0;transform: translateY(30px);} </style>
以上代碼中,由于move、enter和leave都需要設置transition。因此,直接在元素上設置transition即可
<style> .list-item {display: inline-block;margin-right: 10px;transition: 1s;} .list-leave-active{position:absolute;} .list-enter, .list-leave-to{opacity: 0;transform: translateY(30px);} </style>
下面是完整代碼
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> <style> .list-item {display: inline-block;margin-right: 10px;transition: 1s;} .list-leave-active{position:absolute;} .list-enter, .list-leave-to{opacity: 0;transform: translateY(30px);} </style> </head> <body> <div id="list-demo" class="demo"> <button @click="shuffle">shuffle</button> <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> <script type="text/javascript" src="http://sandbox.runjs.cn/uploads/rs/26/ddzmgynp/vue.js"></script> <script> new Vue({ el: '#list-demo', data: { items: [1,2,3,4,5,6,7,8,9], nextNum: 10 }, methods: { randomIndex() { return Math.floor(Math.random() * this.items.length) }, add() { this.items.splice(this.randomIndex(), 0, this.nextNum++) }, remove() { this.items.splice(this.randomIndex(), 1) }, shuffle(){ this.items = this.items.sort(()=>{return Math.random() - 0.5;}) }, } }) </script> </body> </html>
多維列表
FLIP 動畫不僅可以實現單列過渡,多維網格的過渡也同樣簡單
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> <style> .container {width: 270px;margin-top: 10px;line-height:30px;text-align:center;} .cell {display: inline-block;width: 30px;height: 30px;outline: 1px solid #aaa;} .cell-move {transition:1s;} </style> </head> <body> <div id="list-demo" class="demo"> <button @click="shuffle">shuffle</button> <transition-group name="cell" tag="div" class="container"> <span v-for="cell in cells" :key="cell.id" class="cell">{{ cell.number }}</span> </transition-group> </div> <script type="text/javascript" src="http://sandbox.runjs.cn/uploads/rs/26/ddzmgynp/vue.js"></script> <script> new Vue({ el: '#list-demo', data: { cells: Array.apply(null, { length: 81 }) .map(function (_, index) { return { id: index, number: index % 9 + 1 } }) }, methods: { shuffle(){ this.cells = this.cells.sort(()=>{return Math.random() - 0.5;}) }, } }) </script> </body> </html>
漸進過渡
通過 data 屬性與 JavaScript 通信 ,就可以實現列表的漸進過渡
下面是使用CSS過渡實現的一個例子
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> <style> .list-move,.list-enter-active, .list-leave-active {transition: 1s;} .list-leave-active{position:absolute;} .list-enter,.list-leave-to{opacity: 0;height:0;} </style> </head> <body> <div id="list-demo" class="demo"> <input v-model="query"> <transition-group name="list" tag="ul"> <li v-for="(item, index) in computedList" :key="item" :data-index="index">{{item}}</li> </transition-group> </div> <script type="text/javascript" src="http://sandbox.runjs.cn/uploads/rs/26/ddzmgynp/vue.js"></script> <script> new Vue({ el: '#list-demo', data: { query: '', list: ['HTML','CSS','Javascript','jQuery','Vue'] }, computed: { computedList() { return this.list.filter((item)=>{ return item.toLowerCase().indexOf(this.query.toLowerCase()) !== -1 }) } }, }) </script> </body> </html>
上面的效果中,列表項是一齊運動的。如果要實現依次運動的效果,則需要使用JS過渡來實現
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <div id="list-demo" class="demo"> <input v-model="query"> <transition-group name="list" tag="ul" :css="false" @before-enter="beforeEnter" @enter="enter" @leave="leave"> <li v-for="(item, index) in computedList" :key="item" :data-index="index">{{item}}</li> </transition-group> </div> <script type="text/javascript" src="http://sandbox.runjs.cn/uploads/rs/26/ddzmgynp/velocity.min.js"></script> <script type="text/javascript" src="http://sandbox.runjs.cn/uploads/rs/26/ddzmgynp/vue.js"></script> <script> new Vue({ el: '#list-demo', data: { query: '', list: ['HTML','CSS','Javascript','jQuery','Vue'] }, computed: { computedList() { return this.list.filter((item)=>{ return item.toLowerCase().indexOf(this.query.toLowerCase()) !== -1 }) } }, methods: { beforeEnter(el) { el.style.opacity = el.style.height = 0 }, enter(el, done) { setTimeout(()=>{ Velocity(el,{ opacity: 1, height: '1.6em' },{ complete: done }) }, el.dataset.index * 150) }, leave(el, done) { setTimeout(()=>{ Velocity(el,{ opacity: 0, height: 0 },{ complete: done }) }, el.dataset.index * 150) } }, }) </script> </body> </html>
文章列表