第5章:过渡与动画
第13节:JavaScript钩子与列表过渡
5.13.1 JavaScript钩子函数
Vue的过渡系统提供了一系列JavaScript钩子函数,可以在过渡的不同阶段执行自定义逻辑。这些钩子函数可以与CSS过渡结合使用,也可以单独使用。
钩子函数列表
<transition
@before-enter="beforeEnter"
@enter="enter"
@after-enter="afterEnter"
@enter-cancelled="enterCancelled"
@before-leave="beforeLeave"
@leave="leave"
@after-leave="afterLeave"
@leave-cancelled="leaveCancelled"
>
<p v-if="show">hello</p>
</transition>钩子函数详解
**
before-enter**:进入过渡前触发- 此时元素尚未插入到DOM
- 用途:可以设置元素的初始状态
**
enter**:进入过渡开始时触发- 此时元素已插入到DOM
- 必须调用
done回调函数来结束过渡,否则过渡将永远不会结束 - 用途:可以使用JavaScript动画库来实现复杂的动画效果
**
after-enter**:进入过渡完成后触发- 此时过渡已结束
- 用途:可以执行动画完成后的清理工作
**
enter-cancelled**:进入过渡被取消时触发- 仅在进入过渡尚未完成时触发
- 用途:可以执行取消动画的清理工作
**
before-leave**:离开过渡前触发- 此时元素仍在DOM中
- 用途:可以设置元素的初始状态
**
leave**:离开过渡开始时触发- 必须调用
done回调函数来结束过渡 - 用途:可以使用JavaScript动画库来实现复杂的动画效果
- 必须调用
**
after-leave**:离开过渡完成后触发- 此时元素已从DOM中移除
- 用途:可以执行动画完成后的清理工作
**
leave-cancelled**:离开过渡被取消时触发- 仅在
v-show过渡中可用 - 用途:可以执行取消动画的清理工作
- 仅在
示例:使用JavaScript钩子实现动画
<template>
<div>
<button @click="show = !show">
切换显示
</button>
<transition
@before-enter="beforeEnter"
@enter="enter"
@leave="leave"
>
<p v-if="show" ref="el">hello</p>
</transition>
</div>
</template>
<script>
export default {
data() {
return {
show: true
}
},
methods: {
beforeEnter(el) {
// 初始状态:透明且向上偏移
el.style.opacity = 0
el.style.transform = 'translateY(-20px)'
},
enter(el, done) {
// 动画过程:使用requestAnimationFrame实现平滑动画
let duration = 500 // 动画持续时间
let start = null
function animate(timestamp) {
if (!start) start = timestamp
let progress = timestamp - start
let percent = Math.min(progress / duration, 1)
// 使用easeOutQuad缓动函数
let eased = 1 - Math.pow(1 - percent, 2)
el.style.opacity = eased
el.style.transform = `translateY(${(1 - eased) * -20}px)`
if (percent < 1) {
requestAnimationFrame(animate)
} else {
done() // 动画完成,调用done回调
}
}
requestAnimationFrame(animate)
},
leave(el, done) {
// 离开动画:使用setTimeout实现简单动画
el.style.transition = 'opacity 0.5s, transform 0.5s'
el.style.opacity = 0
el.style.transform = 'translateY(20px)'
setTimeout(() => {
done() // 动画完成,调用done回调
}, 500)
}
}
}
</script>5.13.2 使用Velocity.js或GSAP库
Vue的JavaScript钩子可以与第三方动画库(如Velocity.js或GSAP)结合使用,实现更复杂的动画效果。
示例:使用GSAP实现动画
<template>
<div>
<button @click="show = !show">
切换显示
</button>
<transition
@enter="enter"
@leave="leave"
>
<p v-if="show">hello</p>
</transition>
</div>
</template>
<script>
import gsap from 'gsap'
export default {
data() {
return {
show: true
}
},
methods: {
enter(el, done) {
// 使用GSAP实现进入动画
gsap.fromTo(el,
{ opacity: 0, y: -20 }, // 初始状态
{ opacity: 1, y: 0, duration: 0.5, onComplete: done } // 结束状态
)
},
leave(el, done) {
// 使用GSAP实现离开动画
gsap.to(el,
{ opacity: 0, y: 20, duration: 0.5, onComplete: done } // 结束状态
)
}
}
}
</script>示例:使用Velocity.js实现动画
<template>
<div>
<button @click="show = !show">
切换显示
</button>
<transition
@enter="enter"
@leave="leave"
>
<p v-if="show">hello</p>
</transition>
</div>
</template>
<script>
import Velocity from 'velocity-animate'
export default {
data() {
return {
show: true
}
},
methods: {
enter(el, done) {
// 使用Velocity.js实现进入动画
Velocity(el, {
opacity: [1, 0],
translateY: [0, -20]
}, {
duration: 500,
complete: done
})
},
leave(el, done) {
// 使用Velocity.js实现离开动画
Velocity(el, {
opacity: 0,
translateY: 20
}, {
duration: 500,
complete: done
})
}
}
}
</script>5.13.3 列表过渡<transition-group>
<transition-group>组件用于实现列表项的过渡效果,它与<transition>组件的用法类似,但有一些特殊的注意事项。
基本用法
<template>
<div>
<button @click="addItem">添加项目</button>
<button @click="removeItem">移除项目</button>
<transition-group name="list" tag="ul">
<li v-for="item in items" :key="item.id">
{{ item.text }}
</li>
</transition-group>
</div>
</template>
<script>
export default {
data() {
return {
items: [
{ id: 1, text: '项目1' },
{ id: 2, text: '项目2' },
{ id: 3, text: '项目3' }
],
nextId: 4
}
},
methods: {
addItem() {
this.items.push({
id: this.nextId++,
text: `项目${this.nextId - 1}`
})
},
removeItem() {
this.items.shift()
}
}
}
</script>
<style>
.list-enter-active, .list-leave-active {
transition: all 0.5s ease;
}
.list-enter-from, .list-leave-to {
opacity: 0;
transform: translateX(30px);
}
</style><transition-group>的特点
- 必须提供
key属性:每个列表项都必须有唯一的key属性 - **默认渲染为
<span>**:可以使用tag属性指定渲染的元素 - 支持
move过渡:可以添加v-move类名来实现列表项移动时的过渡效果 - 不支持
mode属性:列表过渡中没有过渡模式
添加move过渡
<transition-group name="list" tag="ul">
<li v-for="item in items" :key="item.id">
{{ item.text }}
</li>
</transition-group>
<style>
.list-enter-active, .list-leave-active, .list-move {
transition: all 0.5s ease;
}
.list-enter-from, .list-leave-to {
opacity: 0;
transform: translateX(30px);
}
.list-leave-active {
position: absolute;
}
</style>5.13.4 列表的排序过渡与FLIP技术
当列表项的顺序发生变化时,Vue使用FLIP技术来实现平滑的过渡效果。FLIP是"First, Last, Invert, Play"的缩写,是一种实现高性能动画的技术。
FLIP技术原理
- First:记录元素的初始位置和大小
- Last:执行DOM操作,记录元素的最终位置和大小
- Invert:计算初始状态和最终状态之间的差异,反转元素的位置和大小
- Play:使用过渡动画将元素从反转状态恢复到最终状态
示例:实现可排序列表
<template>
<div>
<h3>可排序列表</h3>
<transition-group name="flip-list" tag="ul">
<li
v-for="item in items"
:key="item.id"
@click="moveItem(item)"
>
{{ item.text }}
</li>
</transition-group>
</div>
</template>
<script>
export default {
data() {
return {
items: [
{ id: 1, text: '项目1' },
{ id: 2, text: '项目2' },
{ id: 3, text: '项目3' },
{ id: 4, text: '项目4' },
{ id: 5, text: '项目5' }
]
}
},
methods: {
moveItem(item) {
// 随机移动项目到新位置
const currentIndex = this.items.indexOf(item)
const newIndex = Math.floor(Math.random() * this.items.length)
// 从原位置移除
this.items.splice(currentIndex, 1)
// 添加到新位置
this.items.splice(newIndex, 0, item)
}
}
}
</script>
<style>
.flip-list-move {
transition: transform 0.5s ease;
}
.flip-list-enter-active, .flip-list-leave-active {
transition: opacity 0.5s ease;
}
.flip-list-enter-from, .flip-list-leave-to {
opacity: 0;
}
.flip-list-leave-active {
position: absolute;
}
</style>使用Sortable.js实现拖拽排序
<template>
<div>
<h3>拖拽排序列表</h3>
<transition-group name="flip-list" tag="ul" ref="sortableList">
<li
v-for="item in items"
:key="item.id"
class="sortable-item"
>
{{ item.text }}
</li>
</transition-group>
</div>
</template>
<script>
import Sortable from 'sortablejs'
export default {
data() {
return {
items: [
{ id: 1, text: '项目1' },
{ id: 2, text: '项目2' },
{ id: 3, text: '项目3' }
]
}
},
mounted() {
// 初始化Sortable.js
Sortable.create(this.$refs.sortableList, {
animation: 150,
onEnd: (evt) => {
// 更新数据顺序
const item = this.items.splice(evt.oldIndex, 1)[0]
this.items.splice(evt.newIndex, 0, item)
}
})
}
}
</script>
<style>
.flip-list-move {
transition: transform 0.5s ease;
}
.sortable-item {
cursor: move;
padding: 10px;
margin: 5px 0;
background-color: #f0f0f0;
border-radius: 4px;
}
</style>总结
Vue的JavaScript钩子函数和列表过渡功能为我们提供了实现复杂动画效果的能力。通过结合JavaScript钩子和第三方动画库,我们可以实现各种精美的动画效果。
列表过渡使用<transition-group>组件,结合FLIP技术,可以实现列表项添加、移除和排序时的平滑过渡效果。这对于创建交互式列表非常有用,如可排序列表、动态添加/移除项目等。
在下一章中,我们将学习Vue 3的组合式API,这是Vue 3的核心特性之一。