过渡动画 #
概述 #
Alpine.js 提供了内置的过渡动画系统,可以轻松地为元素添加进入和离开动画。支持 CSS 过渡、CSS 动画和 JavaScript 钩子。
x-transition 基本用法 #
默认过渡 #
html
<div x-data="{ show: false }">
<button @click="show = !show">切换</button>
<div x-show="show" x-transition>
带过渡动画的内容
</div>
</div>
默认过渡使用 CSS 的 opacity 和 transform 属性。
过渡方向 #
html
<div x-data="{ show: false }">
<button @click="show = !show">切换</button>
<div x-show="show" x-transition.opacity>透明度过渡</div>
<div x-show="show" x-transition.scale>缩放过渡</div>
<div x-show="show" x-transition.scale.opacity>缩放+透明度过渡</div>
</div>
自定义 CSS 过渡 #
使用 Tailwind CSS #
html
<div x-data="{ show: false }">
<button @click="show = !show">切换</button>
<div
x-show="show"
x-transition:enter="transition ease-out duration-300"
x-transition:enter-start="opacity-0 transform scale-90"
x-transition:enter-end="opacity-100 transform scale-100"
x-transition:leave="transition ease-in duration-200"
x-transition:leave-start="opacity-100 transform scale-100"
x-transition:leave-end="opacity-0 transform scale-90"
>
自定义过渡效果
</div>
</div>
使用自定义 CSS #
html
<style>
.fade-enter {
opacity: 0;
transform: translateY(-10px);
}
.fade-enter-active {
transition: opacity 0.3s, transform 0.3s;
}
.fade-leave {
opacity: 1;
transform: translateY(0);
}
.fade-leave-active {
transition: opacity 0.3s, transform 0.3s;
opacity: 0;
transform: translateY(-10px);
}
</style>
<div x-data="{ show: false }">
<button @click="show = !show">切换</button>
<div
x-show="show"
x-transition:enter="fade-enter"
x-transition:enter-active="fade-enter-active"
x-transition:leave="fade-leave"
x-transition:leave-active="fade-leave-active"
>
内容
</div>
</div>
过渡类名 #
类名说明 #
| 类名 | 说明 |
|---|---|
x-transition:enter |
进入过渡开始时添加 |
x-transition:enter-start |
进入过渡开始状态 |
x-transition:enter-end |
进入过渡结束状态 |
x-transition:leave |
离开过渡开始时添加 |
x-transition:leave-start |
离开过渡开始状态 |
x-transition:leave-end |
离开过渡结束状态 |
过渡流程 #
text
进入:
1. 添加 enter 和 enter-start 类
2. 下一帧移除 enter-start,添加 enter-end
3. 过渡完成后移除所有类
离开:
1. 添加 leave 和 leave-start 类
2. 下一帧移除 leave-start,添加 leave-end
3. 过渡完成后移除所有类并隐藏元素
实用示例 #
模态框动画 #
html
<div x-data="{ open: false }">
<button @click="open = true">打开模态框</button>
<div
x-show="open"
x-transition:enter="transition ease-out duration-300"
x-transition:enter-start="opacity-0"
x-transition:enter-end="opacity-100"
x-transition:leave="transition ease-in duration-200"
x-transition:leave-start="opacity-100"
x-transition:leave-end="opacity-0"
class="fixed inset-0 bg-black/50 flex items-center justify-center"
@click.self="open = false"
>
<div
x-show="open"
x-transition:enter="transition ease-out duration-300"
x-transition:enter-start="opacity-0 transform scale-95"
x-transition:enter-end="opacity-100 transform scale-100"
x-transition:leave="transition ease-in duration-200"
x-transition:leave-start="opacity-100 transform scale-100"
x-transition:leave-end="opacity-0 transform scale-95"
class="bg-white rounded-lg p-6 max-w-md"
@click.stop
>
<h2 class="text-xl font-bold mb-4">模态框标题</h2>
<p class="mb-4">模态框内容</p>
<button @click="open = false" class="btn">关闭</button>
</div>
</div>
</div>
下拉菜单动画 #
html
<div x-data="{ open: false }" class="relative">
<button @click="open = !open" class="btn">
菜单
<span x-show="!open">▼</span>
<span x-show="open">▲</span>
</button>
<div
x-show="open"
@click.away="open = false"
x-transition:enter="transition ease-out duration-100"
x-transition:enter-start="opacity-0 transform scale-95"
x-transition:enter-end="opacity-100 transform scale-100"
x-transition:leave="transition ease-in duration-75"
x-transition:leave-start="opacity-100 transform scale-100"
x-transition:leave-end="opacity-0 transform scale-95"
class="absolute mt-2 bg-white rounded-md shadow-lg"
>
<a href="#" class="block px-4 py-2 hover:bg-gray-100">选项 1</a>
<a href="#" class="block px-4 py-2 hover:bg-gray-100">选项 2</a>
<a href="#" class="block px-4 py-2 hover:bg-gray-100">选项 3</a>
</div>
</div>
通知动画 #
html
<div x-data="{
notifications: [],
add(message) {
const id = Date.now()
this.notifications.push({ id, message })
setTimeout(() => this.remove(id), 3000)
},
remove(id) {
this.notifications = this.notifications.filter(n => n.id !== id)
}
}">
<button @click="add('新消息 ' + Date.now())">添加通知</button>
<div class="fixed top-4 right-4 space-y-2">
<template x-for="notification in notifications" :key="notification.id">
<div
x-transition:enter="transition ease-out duration-300"
x-transition:enter-start="opacity-0 transform translate-x-full"
x-transition:enter-end="opacity-100 transform translate-x-0"
x-transition:leave="transition ease-in duration-200"
x-transition:leave-start="opacity-100 transform translate-x-0"
x-transition:leave-end="opacity-0 transform translate-x-full"
class="bg-blue-500 text-white px-4 py-2 rounded shadow"
x-text="notification.message"
></div>
</template>
</div>
</div>
手风琴动画 #
html
<div x-data="{
items: [
{ title: '标题 1', content: '内容 1', open: false },
{ title: '标题 2', content: '内容 2', open: false },
{ title: '标题 3', content: '内容 3', open: false }
]
}">
<template x-for="item in items">
<div class="border rounded mb-2">
<button
@click="item.open = !item.open"
class="w-full px-4 py-2 text-left font-medium"
>
<span x-text="item.title"></span>
<span x-show="!item.open">+</span>
<span x-show="item.open">-</span>
</button>
<div
x-show="item.open"
x-transition:enter="transition ease-out duration-200"
x-transition:enter-start="opacity-0 max-h-0"
x-transition:enter-end="opacity-100 max-h-40"
x-transition:leave="transition ease-in duration-200"
x-transition:leave-start="opacity-100 max-h-40"
x-transition:leave-end="opacity-0 max-h-0"
class="overflow-hidden px-4"
>
<p class="py-2" x-text="item.content"></p>
</div>
</div>
</template>
</div>
列表动画 #
html
<div x-data="{
items: ['Item 1', 'Item 2', 'Item 3'],
addItem() {
this.items.push('Item ' + (this.items.length + 1))
},
removeItem(index) {
this.items.splice(index, 1)
}
}">
<button @click="addItem()">添加</button>
<ul class="space-y-2">
<template x-for="(item, index) in items" :key="item">
<li
x-transition:enter="transition ease-out duration-200"
x-transition:enter-start="opacity-0 transform -translate-y-2"
x-transition:enter-end="opacity-100 transform translate-y-0"
x-transition:leave="transition ease-in duration-200"
x-transition:leave-start="opacity-100 transform translate-y-0"
x-transition:leave-end="opacity-0 transform -translate-y-2"
class="flex justify-between items-center p-2 bg-gray-100 rounded"
>
<span x-text="item"></span>
<button @click="removeItem(index)" class="text-red-500">删除</button>
</li>
</template>
</ul>
</div>
JavaScript 钩子 #
使用钩子函数 #
html
<div x-data="{ show: false }">
<button @click="show = !show">切换</button>
<div
x-show="show"
x-transition:enter="enterHandler"
x-transition:leave="leaveHandler"
>
内容
</div>
</div>
<script>
function enterHandler(el, done) {
el.style.opacity = 0
el.style.transform = 'translateY(-20px)'
requestAnimationFrame(() => {
el.style.transition = 'all 0.3s ease'
el.style.opacity = 1
el.style.transform = 'translateY(0)'
})
el.addEventListener('transitionend', done, { once: true })
}
function leaveHandler(el, done) {
el.style.transition = 'all 0.3s ease'
el.style.opacity = 0
el.style.transform = 'translateY(-20px)'
el.addEventListener('transitionend', done, { once: true })
}
</script>
在组件中定义钩子 #
html
<div x-data="{
show: false,
beforeEnter(el) {
el.style.opacity = 0
},
enter(el, done) {
Velocity(el, { opacity: 1 }, { duration: 300, complete: done })
},
leave(el, done) {
Velocity(el, { opacity: 0 }, { duration: 300, complete: done })
}
}">
<button @click="show = !show">切换</button>
<div
x-show="show"
x-transition:enter="enter"
x-transition:leave="leave"
>
内容
</div>
</div>
x-if 与过渡 #
x-if 也支持过渡动画:
html
<div x-data="{ show: false }">
<button @click="show = !show">切换</button>
<template x-if="show">
<div x-transition>
带过渡的内容
</div>
</template>
</div>
注意事项 #
1. 过渡类冲突 #
确保过渡类不会与现有样式冲突:
html
<div x-show="show" x-transition class="my-class">
</div>
2. 性能考虑 #
使用 transform 和 opacity 进行动画,避免触发重排:
html
<div
x-transition:enter-start="opacity-0 transform scale-95"
x-transition:enter-end="opacity-100 transform scale-100"
>
</div>
3. 过渡时间 #
确保 CSS 过渡时间与预期一致:
css
.transition-300 {
transition: all 0.3s ease;
}
小结 #
过渡动画要点:
- 使用
x-transition添加过渡效果 - 支持自定义 CSS 类
- 支持 JavaScript 钩子
x-show和x-if都支持过渡- 使用 transform 和 opacity 优化性能
下一章,我们将学习插件开发。
最后更新:2026-03-28