Vue3+Nuxt3手风琴组件:实现面板显隐的高度平滑动画
Vue3/Nuxt3 手风琴组件实现平滑高度过渡动画
你的手风琴功能正常但缺少高度过渡效果,核心问题是直接用v-if会瞬间添加/移除DOM元素,无法触发CSS过渡。下面是修改后的完整组件代码,可实现平滑的展开/收起动画:
修改后的组件SFC代码
<script setup lang="ts"> const props = defineProps({ panels: { type: Array, required: true }, }) const current = ref(1) // 存储每个面板的内容实际高度 const panelHeights = ref<Record<number, number>>({}) function toggleAccordion(index: number) { current.value = current.value === index ? 0 : index } // 获取面板内容的真实高度 const setPanelHeight = (panelId: number, el: HTMLElement) => { if (el) { panelHeights.value[panelId] = el.scrollHeight } } </script> <template> <div v-for="panel in panels" :key="panel.id" class="accordion-item"> <!-- 手风琴标题栏 --> <div class="d-flex flex-row p-4 cursor-pointer bg-gray-200" @click.prevent="toggleAccordion(panel.id)" > <div v-if="panel.icon">{{ panel.icon }}</div> <div class="flex-grow-1"> <h4 class="mb-0">{{ panel.title }}</h4> </div> <div> <i v-if="panel.id === current" class="ki-outline ki-minus fs-4 text-gray-800"></i> <i v-else class="ki-outline ki-plus fs-4 text-gray-800"></i> </div> </div> <!-- 带过渡效果的面板内容 --> <Transition name="accordion"> <div class="accordion-content p-4 overflow-hidden" :style="{ height: panel.id === current ? `${panelHeights[panel.id]}px` : '0' }" ref="el" @update:model-value="setPanelHeight(panel.id, $el)" > <slot :name="panel.id"></slot> </div> </Transition> </div> </template> <style scoped> .accordion-content { transition: height 0.3s ease; } /* 过渡状态类,强化动画逻辑 */ .accordion-enter-from, .accordion-leave-to { height: 0 !important; } .accordion-enter-active, .accordion-leave-active { transition: height 0.3s ease; } </style>
关键修改说明
- 替换
v-if为动态高度绑定:不再直接销毁/创建DOM,而是通过修改height属性控制显示隐藏,配合CSS过渡实现平滑动画。 - 获取内容真实高度:通过
scrollHeight获取每个面板内容的实际高度,确保展开时完全显示内容,避免截断。 - Vue Transition组件:用
<Transition>包裹内容,结合自定义CSS过渡类,精准控制动画的进入和离开状态。 - CSS过渡配置:给
.accordion-content添加transition: height 0.3s ease,可调整0.3s来修改动画速度。
组件使用方式不变
<accordion :panels="panels"> <template #1> this is one </template> <template #2> this is two </template> <template #3> this is three </template> </accordion>
面板数据保持不变
const panels = [ {id:1,title:'Test Accordion 1',icon:'',}, {id:2,title:'Test Accordion 2',icon:'',}, {id:3,title:'Test Accordion 3',icon:'',}, ]
内容的提问来源于stack exchange,提问作者Prof Nami
相关产品推荐
相关产品推荐

