使用
<transition-group>组件可以为v-for渲染的列表添加过渡效果,包括添加、删除和重排动画。
<transition-group> 与 <transition> 的区别:
<span>,可通过 tag 属性指定key 属性<div id="app">
<button @click="add">添加</button>
<button @click="remove">删除</button>
<transition-group name="list" tag="ul">
<li v-for="item in items" :key="item.id" class="list-item">
{{ item.text }}
</li>
</transition-group>
</div>
<style>
.list-item {
display: block;
padding: 10px;
margin: 5px 0;
background: #f0f0f0;
border-radius: 4px;
}
.list-enter, .list-leave-to {
opacity: 0;
transform: translateX(30px);
}
.list-enter-active, .list-leave-active {
transition: all 0.5s;
}
.list-leave-active {
position: absolute;
}
.list-move {
transition: transform 0.5s;
}
</style>
<script>
new Vue({
el: '#app',
data: {
items: [
{ id: 1, text: '项目一' },
{ id: 2, text: '项目二' },
{ id: 3, text: '项目三' }
],
nextId: 4
},
methods: {
add() {
this.items.push({
id: this.nextId++,
text: '项目 ' + this.nextId
})
},
remove() {
this.items.splice(Math.random() * this.items.length, 1)
}
}
})
</script>
当列表项位置变化时,使用 v-move 类名:
.list-move {
transition: transform 0.5s;
}
Vue 使用 FLIP 动画技术实现平滑的位置过渡。
删除时需要设置 position: absolute:
.list-leave-active {
position: absolute;
}
这样其他元素才能平滑移动到新位置。
<div id="app">
<input
v-model="newTodo"
@keyup.enter="addTodo"
placeholder="添加待办事项"
>
<transition-group name="todo" tag="ul" class="todo-list">
<li v-for="todo in todos" :key="todo.id" class="todo-item">
<span>{{ todo.text }}</span>
<button @click="removeTodo(todo.id)">删除</button>
</li>
</transition-group>
<p v-if="!todos.length">暂无待办事项</p>
</div>
<style>
.todo-list {
list-style: none;
padding: 0;
position: relative;
}
.todo-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 16px;
margin: 8px 0;
background: white;
border-radius: 4px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.todo-enter {
opacity: 0;
transform: translateY(-20px);
}
.todo-enter-active {
transition: all 0.3s ease;
}
.todo-leave {
opacity: 1;
transform: translateX(0);
}
.todo-leave-active {
transition: all 0.3s ease;
position: absolute;
width: 100%;
}
.todo-leave-to {
opacity: 0;
transform: translateX(50px);
}
.todo-move {
transition: transform 0.3s ease;
}
</style>
<script>
new Vue({
el: '#app',
data: {
newTodo: '',
todos: [
{ id: 1, text: '学习 Vue 基础' },
{ id: 2, text: '掌握组件化开发' },
{ id: 3, text: '实践项目开发' }
],
nextId: 4
},
methods: {
addTodo() {
if (!this.newTodo.trim()) return
this.todos.push({
id: this.nextId++,
text: this.newTodo
})
this.newTodo = ''
},
removeTodo(id) {
const index = this.todos.findIndex(t => t.id === id)
if (index !== -1) {
this.todos.splice(index, 1)
}
}
}
})
</script>
结合计算属性实现排序动画:
<div id="app">
<button @click="shuffle">随机排序</button>
<button @click="sort">按数字排序</button>
<transition-group name="flip-list" tag="div" class="container">
<div v-for="item in items" :key="item.id" class="item">
{{ item.value }}
</div>
</transition-group>
</div>
<style>
.container {
display: flex;
flex-wrap: wrap;
position: relative;
}
.item {
width: 80px;
height: 80px;
margin: 5px;
background: #42b983;
color: white;
display: flex;
align-items: center;
justify-content: center;
font-size: 24px;
border-radius: 8px;
}
.flip-list-move {
transition: transform 0.5s;
}
</style>
<script>
new Vue({
el: '#app',
data: {
items: [
{ id: 1, value: 1 },
{ id: 2, value: 2 },
{ id: 3, value: 3 },
{ id: 4, value: 4 },
{ id: 5, value: 5 },
{ id: 6, value: 6 }
]
},
methods: {
shuffle() {
this.items = this.items.sort(() => Math.random() - 0.5)
},
sort() {
this.items = this.items.sort((a, b) => a.value - b.value)
}
}
})
</script>
使用 JavaScript 钩子实现交错效果:
<div id="app">
<button @click="add">添加项目</button>
<transition-group
tag="ul"
@before-enter="beforeEnter"
@enter="enter"
@leave="leave"
>
<li
v-for="(item, index) in items"
:key="item.id"
:data-index="index"
>
{{ item.text }}
</li>
</transition-group>
</div>
<script>
new Vue({
el: '#app',
data: {
items: [],
nextId: 1
},
methods: {
add() {
for (let i = 0; i < 5; i++) {
this.items.push({
id: this.nextId++,
text: '项目 ' + this.nextId
})
}
},
beforeEnter(el) {
el.style.opacity = 0
el.style.transform = 'translateY(20px)'
},
enter(el, done) {
const delay = el.dataset.index * 100
setTimeout(() => {
el.style.transition = 'all 0.3s ease'
el.style.opacity = 1
el.style.transform = 'translateY(0)'
el.addEventListener('transitionend', done)
}, delay)
},
leave(el, done) {
el.style.transition = 'all 0.3s ease'
el.style.opacity = 0
el.style.transform = 'translateY(-20px)'
el.addEventListener('transitionend', done)
}
}
})
</script>
<div id="app">
<button @click="shuffle">打乱</button>
<transition-group name="grid" tag="div" class="grid">
<div
v-for="cell in cells"
:key="cell.id"
class="cell"
:style="{ background: cell.color }"
>
{{ cell.number }}
</div>
</transition-group>
</div>
<style>
.grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 10px;
width: 400px;
}
.cell {
height: 80px;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 24px;
border-radius: 8px;
}
.grid-move {
transition: transform 0.5s ease;
}
.grid-enter-active, .grid-leave-active {
transition: all 0.3s ease;
}
.grid-enter, .grid-leave-to {
opacity: 0;
transform: scale(0.5);
}
.grid-leave-active {
position: absolute;
}
</style>
<script>
new Vue({
el: '#app',
data: {
cells: Array.from({ length: 16 }, (_, i) => ({
id: i + 1,
number: i + 1,
color: `hsl(${(i * 22.5)}, 70%, 60%)`
}))
},
methods: {
shuffle() {
this.cells = this.cells.sort(() => Math.random() - 0.5)
}
}
})
</script>
Vue 的列表移动动画基于 FLIP 技术:
这就是为什么只需要添加 .list-move 类就能实现平滑的位置过渡。
列表过渡让应用的交互更加流畅自然。掌握这些技巧,你可以创建出色的列表动画效果。