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

如何为Headless UI Vue Tabs添加过渡动画?

为Headless UI Vue Tabs添加过渡动画的正确方式

你的写法无效的核心原因是:未选中的TabPanel会被自动添加hidden属性(对应CSS的display: none),这会阻断内部TransitionRoot的动画渲染——父元素不可见时,内部元素的过渡动画不会被浏览器执行。

下面是两种可行的解决方案:

方案一:利用TabPanel的卸载机制实现动画

通过给TabGroup添加unmount="true"属性,让未选中的TabPanel完全从DOM中卸载,这样TransitionRoot会在组件挂载/卸载时触发过渡动画:

<template>
  <TabGroup unmount="true">
    <TabList class="flex space-x-1 bg-gray-100 p-1 rounded-md">
      <Tab class="px-3 py-2 rounded-md focus:outline-none focus:ring-2 focus:ring-gray-500">Tab 1</Tab>
      <Tab class="px-3 py-2 rounded-md focus:outline-none focus:ring-2 focus:ring-gray-500">Tab 2</Tab>
      <Tab class="px-3 py-2 rounded-md focus:outline-none focus:ring-2 focus:ring-gray-500">Tab 3</Tab>
    </TabList>
    <TabPanels class="mt-2">
      <TabPanel v-for="p in panels" :key="p.id">
        <TransitionRoot
          enter="transition duration-300 ease-out"
          enter-from="opacity-0 translate-y-4"
          enter-to="opacity-100 translate-y-0"
          leave="transition duration-200 ease-in"
          leave-from="opacity-100 translate-y-0"
          leave-to="opacity-0 translate-y-4"
        >
          <div class="p-4 bg-white rounded-md shadow-sm">
            {{ p.content }}
          </div>
        </TransitionRoot>
      </TabPanel>
    </TabPanels>
  </TabGroup>
</template>

<script setup>
import { TabGroup, TabList, Tab, TabPanels, TabPanel, TransitionRoot } from '@headlessui/vue'

const panels = [
  { id: 1, content: 'Tab 1 内容区域' },
  { id: 2, content: 'Tab 2 内容区域' },
  { id: 3, content: 'Tab 3 内容区域' },
]
</script>

方案二:保留TabPanel在DOM中,手动控制显示状态

如果你希望所有TabPanel始终保留在DOM中,可以覆盖TabPanel的默认隐藏样式,用TransitionRoot的show属性控制内容的显示隐藏:

<template>
  <TabGroup>
    <TabList class="flex space-x-1 bg-gray-100 p-1 rounded-md">
      <Tab class="px-3 py-2 rounded-md focus:outline-none focus:ring-2 focus:ring-gray-500">Tab 1</Tab>
      <Tab class="px-3 py-2 rounded-md focus:outline-none focus:ring-2 focus:ring-gray-500">Tab 2</Tab>
      <Tab class="px-3 py-2 rounded-md focus:outline-none focus:ring-2 focus:ring-gray-500">Tab 3</Tab>
    </TabList>
    <TabPanels class="mt-2">
      <TabPanel v-slot="{ selected }" v-for="p in panels" :key="p.id">
        <!-- 覆盖默认hidden样式,让TabPanel始终占据空间 -->
        <div :class="selected ? '' : 'invisible h-0 overflow-hidden'">
          <TransitionRoot
            :show="selected"
            enter="transition-all duration-300 ease-out"
            enter-from="opacity-0 transform translate-y-4"
            enter-to="opacity-100 transform translate-y-0"
            leave="transition-all duration-200 ease-in"
            leave-from="opacity-100 transform translate-y-0"
            leave-to="opacity-0 transform translate-y-4"
          >
            <div class="p-4 bg-white rounded-md shadow-sm">
              {{ p.content }}
            </div>
          </TransitionRoot>
        </div>
      </TabPanel>
    </TabPanels>
  </TabGroup>
</template>

<script setup>
import { TabGroup, TabList, Tab, TabPanels, TabPanel, TransitionRoot } from '@headlessui/vue'

const panels = [
  { id: 1, content: 'Tab 1 内容区域' },
  { id: 2, content: 'Tab 2 内容区域' },
  { id: 3, content: 'Tab 3 内容区域' },
]
</script>

<style>
/* 移除TabPanel的默认hidden样式 */
[data-headlessui-panel][hidden] {
  display: block !important;
}
</style>

注意:两种方案都需要确保你正确导入了TransitionRoot组件,并且配置了完整的过渡类(示例中使用Tailwind CSS类,你可以根据自己的CSS方案调整)。

内容的提问来源于stack exchange,提问作者clueless

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 12:39:24