Headless UI React Transition时长不生效问题求助
问题:Headless UI Transition长时长设置不生效,无法实现CheckIcon平滑过渡
我使用@headlessui/react": "^1.7.18,想要实现选中项目时CheckIcon平滑出现的过渡效果(类似SAP筛选框的选中动画)。目前已经实现了基础过渡,但时长过短,将duration-200改为duration-1000左右的长时长后完全不生效,不清楚原因。以下是我的JSX代码,也接受纯Tailwind实现方案:
<div> <div className="leading-6 text-gray-900">Pick one or more</div> {fields.map((option) => ( <div key={option} onClick={() => handleSelect(option)} className={`flex items-center p-2 border cursor-pointer ${ selected.includes(option) ? "border-blue-300 bg-blue-50 hover:border-blue-400" : "border-gray-300 bg-white hover:border-gray-400" }`} > <Transition show={selected.includes(option)} as={Fragment} enter="transition ease-out duration-1500" enterFrom="opacity-0 -translate-x-2" enterTo="opacity-100 translate-x-0" leave="transition ease-in duration-1200" leaveFrom="opacity-100 translate-x-0" leaveTo="opacity-0 -translate-x-2" > <CheckIcon className="h-4 w-4 text-blue-600" /> </Transition> <span className={`ml-2 ${ selected.includes(option) ? "text-blue-600" : "" }`} > {option} </span> </div> ))} </div>
原因分析
核心问题是Transition组件使用as={Fragment}时,过渡类无法正确应用到SVG图标上。Fragment不会生成实际DOM节点,Headless UI尝试将过渡样式直接绑定到子元素(CheckIcon即SVG),但SVG的样式优先级或过渡属性支持限制导致时长设置失效。
修复方案(Headless UI版本)
将Transition的as改为实际DOM元素(如<span>),并给容器添加inline-block确保和文字对齐,过渡类会正确应用到容器上,时长设置即可生效:
<div> <div className="leading-6 text-gray-900">Pick one or more</div> {fields.map((option) => ( <div key={option} onClick={() => handleSelect(option)} className={`flex items-center p-2 border cursor-pointer ${ selected.includes(option) ? "border-blue-300 bg-blue-50 hover:border-blue-400" : "border-gray-300 bg-white hover:border-gray-400" }`} > <Transition show={selected.includes(option)} as="span" className="inline-block" enter="transition ease-out duration-1000" enterFrom="opacity-0 -translate-x-2" enterTo="opacity-100 translate-x-0" leave="transition ease-in duration-1000" leaveFrom="opacity-100 translate-x-0" leaveTo="opacity-0 -translate-x-2" > <CheckIcon className="h-4 w-4 text-blue-600" /> </Transition> <span className={`ml-2 ${ selected.includes(option) ? "text-blue-600" : "" }`} > {option} </span> </div> ))} </div>
纯Tailwind实现方案(无需Headless UI)
如果不想依赖Headless UI,可通过条件类控制图标显示,不要用hidden(会立即卸载元素导致过渡失效),而是用透明度和位移的组合实现过渡:
<div> <div className="leading-6 text-gray-900">Pick one or more</div> {fields.map((option) => { const isSelected = selected.includes(option); return ( <div key={option} onClick={() => handleSelect(option)} className={`flex items-center p-2 border cursor-pointer ${ isSelected ? "border-blue-300 bg-blue-50 hover:border-blue-400" : "border-gray-300 bg-white hover:border-gray-400" }`} > <span className={`inline-block transition-all duration-1000 ${ isSelected ? "opacity-100 translate-x-0" : "opacity-0 -translate-x-2" }`}> <CheckIcon className="h-4 w-4 text-blue-600" /> </span> <span className={`ml-2 transition-colors duration-300 ${ isSelected ? "text-blue-600" : "" }`} > {option} </span> </div> ); })} </div>
关键注意点
- 必须用容器元素(如
<span>)包裹图标,确保过渡样式正确应用 - 避免使用
hidden或直接卸载元素,否则过渡动画会被中断 - Tailwind的
transition-all会自动处理opacity和transform属性的过渡
内容的提问来源于stack exchange,提问作者R2-D2
相关产品推荐
相关产品推荐

