在之前的文章中我们都是使用 <transition> 组件来实现过渡, 其主要用于单个节点、或同一时间渲染多个节点中的一个这两种情况。而对于整个列表(比如使用 v-for)的过渡,则需要使用本文介绍的 <transition-group> 组件。
四、列表过渡
1,<transition-group> 说明
(1)不同于 <transition>
,<transition-group>
会以一个真实元素呈现:默认为一个 <span>
(我们可以通过 tag
特性更换为其他元素。)
(2)过渡模式不可用,因为我们不再相互切换特有的元素。
(3)<transition-group>
的内部元素总是需要提供唯一的 key
属性值。
2,列表的进入、离开过渡
(1)效果图
- 点击“插入一个元素”按钮,会在下方随机位置插入一个新的数字方块,新方块在插入过程中会有过渡动画。
- 点击“移除一个元素”按钮,会随机删除下方的一个数字方块,该方块在移除过程中会有过渡动画。
(2)样例代码
<template>
<div id="app">
<div id="list-demo" class="demo">
<button v-on:click="add">插入一个元素</button>
<button v-on:click="remove">移除一个元素</button>
<transition-group name="list" tag="p">
<span v-for="item in items" v-bind:key="item" class="list-item">
{{ item }}
</span>
</transition-group>
</div>
</div>
</template>
<script>
export default {
name: 'App',
data: function(){
return {
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)
},
}
}
</script>
<style>
/** 方块元素的样式 **/
.list-item {
display: inline-block;
margin-right: 10px;
background-color: orange;
width: 30px;
height: 30px;
line-height: 30px;
text-align: center;
color: #ffffff;
}
/** 插入过程 **/
.list-enter-active{
transition: all 1s;
}
/** 移除过程 **/
.list-leave-active {
transition: all 1s;
}
/*** 开始插入、移除结束的位置变化 ***/
.list-enter, .list-leave-to {
opacity: 0;
transform: translateY(30px);
}
</style>
3,列表的排序过渡
(1)上面的样例有个问题:虽然新插入的元素或者被移除的元素有动画效果,但它周围的元素会瞬间移动到他们新布局的位置,而不是平滑的过渡。要解决这个问题则需要借助新增的 v-move
特性。
v-move 特性会在元素改变定位的过程中应用,它像之前的类名一样:
可以通过 name 属性来自定义前缀(比如 name="xxxx",那么对应的类名便是 xxx-move)
也可以通过 move-class 属性手动设置自定义类名。
(2)这里对之前样例的 css 部分稍作修改,可以发现在插入或移出过程中,其它元素也会从原来的位置平滑过渡新的位置。
<style>
/** 方块元素的样式 **/
.list-item {
display: inline-block;
margin-right: 10px;
background-color: orange;
width: 30px;
height: 30px;
line-height: 30px;
text-align: center;
color: #ffffff;
}
/** 插入过程 **/
.list-enter-active{
transition: all 1s;
}
/** 移除过程 **/
.list-leave-active {
transition: all 1s;
position: absolute;
}
/*** 开始插入、移除结束的位置变化 ***/
.list-enter, .list-leave-to {
opacity: 0;
transform: translateY(30px);
}
/*** 元素定位改变时动画 ***/
.list-move {
transition: transform 1s;
}
</style>
(3)Vue
使用了一个叫 FLIP 简单的动画队列实现排序过渡。所以即使没有插入或删除元素,对于元素顺序的变化,也是支持过渡动画的。
<template>
<div id="app">
<div id="list-demo" class="demo">
<button v-on:click="shuffle">乱序</button>
<transition-group name="list" tag="p">
<span v-for="item in items" v-bind:key="item" class="list-item">
{{ item }}
</span>
</transition-group>
</div>
</div>
</template>
<script>
export default {
name: 'App',
data: function(){
return {
items: [1,2,3,4,5,6,7,8,9]
}
},
methods: {
shuffle: function () {
return this.items.sort(function(a,b){ return Math.random()>.5 ? -1 : 1;})
}
}
}
</script>
<style>
/** 方块元素的样式 **/
.list-item {
display: inline-block;
margin-right: 10px;
background-color: orange;
width: 30px;
height: 30px;
line-height: 30px;
text-align: center;
color: #ffffff;
}
/*** 元素定位改变时动画 ***/
.list-move {
transition: transform 1s;
}
</style>
(4)FLIP 动画不仅可以实现单列过渡,多维网格也同样可以过渡:
<template>
<div id="app">
<div id="list-demo" class="demo">
<button v-on:click="shuffle">乱序</button>
<transition-group name="cell" tag="div" class="container">
<div v-for="cell in cells" :key="cell.id" class="cell">
{{ cell.number }}
</div>
</transition-group>
</div>
</div>
</template>
<script>
export default {
name: 'App',
data: function(){
return {
cells: Array.apply(null, { length: 81 })
.map(function (_, index) {
return {
id: index,
number: index % 9 + 1
}
})
}
},
methods: {
shuffle: function () {
this.cells.sort(function(a,b){ return Math.random()>.5 ? -1 : 1;})
}
}
}
</script>
<style>
.container {
display: flex;
flex-wrap: wrap;
width: 238px;
margin-top: 10px;
}
.cell {
display: flex;
justify-content: space-around;
align-items: center;
width: 25px;
height: 25px;
border: 1px solid #aaa;
margin-right: -1px;
margin-bottom: -1px;
}
.cell:nth-child(3n) {
margin-right: 0;
}
.cell:nth-child(27n) {
margin-bottom: 0;
}
.cell-move {
transition: transform 20s;
}
</style>
附:使用 js 钩子函数实现列表的交错过渡
我们也可以通过 data
属性与 JavaScript
通信,实现列表的交错过渡。
1,效果图
(1)在上方输入框中输入内容时,下方的列表会实时筛选并显示出包含该文字的条目。
(2)同时在列表条目的显示或者移出过程中,会有相应的过渡动画。
2,样例代码
<template>
<div id="app">
<div id="staggered-list-demo">
<input v-model="query">
<transition-group
name="staggered-fade"
tag="ul"
v-bind:css="false"
v-on:before-enter="beforeEnter"
v-on:enter="enter"
v-on:leave="leave">
<li
v-for="(item, index) in computedList"
v-bind:key="item.msg"
v-bind:data-index="index"
>{{ item.msg }}</li>
</transition-group>
</div>
</div>
</template>
<script>
import Velocity from 'velocity-animate'
export default {
name: 'App',
data: function(){
return {
query: '',
list: [
{ msg: 'Bruce Lee' },
{ msg: 'Jackie Chan' },
{ msg: 'Chuck Norris' },
{ msg: 'Jet Li' },
{ msg: 'Kung Fury' }
]
}
},
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 , duration: 20000 }
)
}, delay)
},
leave: function (el, done) {
var delay = el.dataset.index * 150
setTimeout(function () {
Velocity(
el,
{ opacity: 0, height: 0 },
{ complete: done , duration: 20000 }
)
}, delay)
}
}
}
</script>
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。