You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 02:46:04