在 Headless UI 中,Transition
是一个专门用于处理组件进入和离开动画的工具。它本身不负责具体的 UI 功能,而是为其他组件(如 Menu
、Dialog
或自定义元素)提供平滑的过渡效果。通过结合 CSS 动画或 Tailwind CSS 类名,Transition
可以让你轻松实现淡入淡出、滑动、缩放等动画效果。
Transition 的作用
Transition
的主要作用是管理元素在“显示”和“隐藏”时的动画状态。它会自动处理以下内容:
- 进入动画(Enter):当组件从隐藏变为可见时的效果。
- 离开动画(Leave):当组件从可见变为隐藏时的效果。
- 状态同步:确保动画与组件的挂载/卸载状态保持一致,避免闪烁或不自然的跳跃。
它特别适合与 Headless UI 的其他组件(如 Menu.Items
或 Dialog
)搭配使用,让用户体验更流畅。
Transition 的基本用法
Transition
组件需要配合 show
属性来控制显示状态,并通过 enter
、enterFrom
、enterTo
、leave
、leaveFrom
、leaveTo
等属性定义动画的类名。
示例:为下拉菜单添加淡入动画
以下是基于之前 Menu
示例的改进版,添加了 Transition
:
"use client";
import { Transition } from '@headlessui/react'
import { useState } from 'react'
export default function Example() {
const [isOpen, setIsOpen] = useState(false)
return (
<div className="p-5">
<button
onClick={() => setIsOpen(!isOpen)}
className="px-4 py-2 bg-blue-500 text-white rounded"
>
切换显示
</button>
<Transition
show={isOpen} // 控制显示或隐藏
enter="transition-opacity duration-300"
enterFrom="opacity-0"
enterTo="opacity-100"
leave="transition-opacity duration-300"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<div className="mt-4 p-4 bg-gray-100 rounded">
这是一个带有渐变动画的 div
</div>
</Transition>
</div>
)
}
Transition
组件的关键属性
属性 | 作用 |
---|---|
show |
控制元素是否可见(true =显示,false =隐藏) |
enter |
进入动画的整体类(可以是 Tailwind 的 transition-* ) |
enterFrom |
进入动画的初始状态 |
enterTo |
进入动画的最终状态 |
leave |
离开动画的整体类 |
leaveFrom |
离开动画的初始状态 |
leaveTo |
离开动画的最终状态 |
进阶:Transition.Child
处理多个动画元素
Transition.Child
可以用于更复杂的 UI 组件,例如 模态框 中,背景和内容可能需要不同的动画。
<Transition show={isOpen}>
{/* 背景遮罩层动画 */}
<Transition.Child
enter="transition-opacity duration-300"
enterFrom="opacity-0"
enterTo="opacity-100"
leave="transition-opacity duration-300"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<div className="fixed inset-0 bg-black bg-opacity-50"></div>
</Transition.Child>
{/* 内容框动画 */}
<Transition.Child
enter="transition-transform duration-300"
enterFrom="scale-95"
enterTo="scale-100"
leave="transition-transform duration-300"
leaveFrom="scale-100"
leaveTo="scale-95"
>
<div className="fixed inset-0 flex items-center justify-center">
<div className="bg-white p-6 rounded shadow-lg">
<p>模态框内容</p>
</div>
</div>
</Transition.Child>
</Transition>
示例:为下拉菜单添加淡入动画
以下是基于之前 Menu
示例的改进版,添加了 Transition
:
import { Menu, Transition } from '@headlessui/react';
import { Fragment } from 'react';
function DropdownExample() {
return (
<Menu as="div" className="relative inline-block text-left">
<Menu.Button className="px-4 py-2 text-white bg-blue-600 rounded hover:bg-blue-700">
选项
</Menu.Button>
{/* 使用 Transition 包裹 Menu.Items */}
<Transition
as={Fragment} // 使用 Fragment,避免额外 DOM 节点
enter="transition ease-out duration-100" // 进入动画
enterFrom="transform opacity-0 scale-95" // 初始状态
enterTo="transform opacity-100 scale-100" // 结束状态
leave="transition ease-in duration-75" // 离开动画
leaveFrom="transform opacity-100 scale-100" // 初始状态
leaveTo="transform opacity-0 scale-95" // 结束状态
>
<Menu.Items className="absolute mt-2 w-56 rounded-md shadow-lg bg-white ring-1 ring-black ring-opacity-5">
<div className="py-1">
<Menu.Item>
{({ active }) => (
<a
href="#"
className={`block px-4 py-2 text-sm ${
active ? 'bg-gray-100 text-gray-900' : 'text-gray-700'
}`}
>
编辑
</a>
)}
</Menu.Item>
<Menu.Item>
{({ active }) => (
<a
href="#"
className={`block px-4 py-2 text-sm ${
active ? 'bg-gray-100 text-gray-900' : 'text-gray-700'
}`}
>
删除
</a>
)}
</Menu.Item>
</div>
</Menu.Items>
</Transition>
</Menu>
);
}
export default DropdownExample;
代码解释
导入 Transition:
- 从
@headlessui/react
导入Transition
。
- 从
包裹 Menu.Items:
- 将
Menu.Items
放入Transition
中,使其在显示和隐藏时有动画效果。
- 将
动画属性:
enter
:定义进入动画的过渡规则(如ease-out
和duration-100
表示 100 毫秒的缓出效果)。enterFrom
:进入动画的起始状态(透明度 0,缩放 95%)。enterTo
:进入动画的结束状态(透明度 100%,缩放 100%)。leave
、leaveFrom
、leaveTo
:类似地定义离开动画。
as={Fragment}:
- 使用 React 的
Fragment
,避免在 DOM 中添加额外的包裹元素。如果需要特定的 HTML 标签,可以用as="div"
。
- 使用 React 的
效果:
- 点击按钮时,菜单会从透明且略微缩小的状态淡入并放大。
- 关闭菜单时,会淡出并缩小。
更复杂的例子:滑动动画
如果你想要菜单从顶部滑入,可以调整类名:
<Transition
as={Fragment}
enter="transition ease-out duration-200"
enterFrom="transform opacity-0 -translate-y-4"
enterTo="transform opacity-100 translate-y-0"
leave="transition ease-in duration-150"
leaveFrom="transform opacity-100 translate-y-0"
leaveTo="transform opacity-0 -translate-y-4"
>
<Menu.Items>...</Menu.Items>
</Transition>
- 这里使用了
translate-y
来实现垂直滑动效果。
注意事项
- Tailwind CSS 支持:示例中的类名(如
opacity-0
、scale-95
)依赖 Tailwind CSS。如果不用 Tailwind,可以直接写 CSS(如style={{ opacity: 0 }}
)。 - 与状态绑定:
Transition
自动与Menu
的显示状态绑定,无需手动控制show
属性。如果是自定义组件,则需要显式传递show={boolean}
。 - 性能:动画使用 CSS 过渡,避免了 JavaScript 的性能开销。