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

Vue 3 HeadlessUI TransitionRoot中as="template"使用及数字显示问题

问题描述

我使用Vue 3、TailwindCSS和HeadlessUI实现老虎机样式动画,点击「Click to transition」按钮时,绿色方块已能实现顶部滑入、底部滑出的效果。resetIsShowing()函数负责随机处理1-10的连续数组,现在想让每次循环时绿色方块显示10个随机数中的一个。于是重构了显示方块的主div,添加列表渲染:

<div v-for="number in numbers" :key="number.id" class="h-full w-full rounded-md bg-green-500 shadow-lg">
 {{ number }}
</div>

但添加后出现以下错误:

The current component

return (<TransitionChild />)
is rendering a "template".
However we need to passthrough the following props:

  • ref

如果将TransitionRoot中的as="template"改为as="ref",又会丢失下滑动画效果。如何设置才能让数字在每次循环时显示在方块上?

原完整代码:

<template>
    <div class="flex flex-col items-center py-16">
      <div class="h-72 w-72">
        <div class="flex justify-center items-center w-full h-full p-4 text-9xl rounded-md shadow-lg border-solid border-2 border-sky-500">
            <TransitionRoot
              appear
              :show="isShowing"
              as="template"
              enter="transition ease-in-out duration-300 transform"
              enter-from="-translate-y-full opacity-0"
              enter-to="translate-y-0 opacity-100"
              leave="transition ease-in-out duration-300 transform"
              leave-from="translate-y-0 opacity-100"
              leave-to="translate-y-full opacity-0"
            >

              <div v-for="number in numbers" :key="number.id" class="h-full w-full rounded-md bg-green-500 shadow-lg">
                {{ number }}
              </div>
    
            </TransitionRoot>
        </div>
      </div>
      <button
        @click="resetIsShowing"
        class="mt-8 flex transform items-center rounded-full bg-black bg-opacity-20 px-3 py-2 text-sm font-medium text-white transition hover:scale-105 hover:bg-opacity-30 focus:outline-none active:bg-opacity-40"
      >
        <svg viewBox="0 0 20 20" fill="none" class="h-5 w-5 opacity-70">
          <path
            d="M14.9497 14.9498C12.2161 17.6835 7.78392 17.6835 5.05025 14.9498C2.31658 12.2162 2.31658 7.784 5.05025 5.05033C7.78392 2.31666 12.2161 2.31666 14.9497 5.05033C15.5333 5.63385 15.9922 6.29475 16.3266 7M16.9497 2L17 7H16.3266M12 7L16.3266 7"
            stroke="currentColor"
            stroke-width="1.5"
          />
        </svg>
  
        <span class="ml-3">Click to transition</span>
      </button>
    </div>
  </template>
  
  <script setup>
  import { ref } from 'vue'
  import { TransitionRoot } from '@headlessui/vue'
  
  const isShowing = ref(true)
  const numbers = ref([])
  const numberArray = ref(Array.from({length: 10}, (e, i)=> i + 1))

  const sleep = (milliseconds) => {
    return new Promise(resolve => setTimeout(resolve, milliseconds))
  }
  
  async function resetIsShowing() {
    for (let i = 0; i < numberArray.value.length; i++) {
        const index = Math.floor(Math.random() * numberArray.value.length)
        await sleep(800)

        isShowing.value = false
  
        setTimeout(() => {
            isShowing.value = true
        }, 500)

        numbers.value = numberArray.value.filter(r => r === (index + 1))
    }
  }
  </script>
解决方案

问题根源

HeadlessUI的TransitionRoot使用as="template"时,内部只能存在单个根元素。添加v-for后会生成多个div,导致Transition无法正确传递ref属性,触发错误。

修复步骤

  1. 移除不必要的v-for:每次仅需显示一个随机数,直接用单个div绑定当前随机值即可。
  2. 简化数据结构:无需维护numbers数组,改用单个ref存储当前显示的数字。
  3. 调整动画时序:将原setTimeout替换为await sleep,保证动画与数字更新的时序一致。

修改后的完整代码

<template>
  <div class="flex flex-col items-center py-16">
    <div class="h-72 w-72">
      <div class="flex justify-center items-center w-full h-full p-4 text-9xl rounded-md shadow-lg border-solid border-2 border-sky-500">
        <TransitionRoot
          appear
          :show="isShowing"
          as="template"
          enter="transition ease-in-out duration-300 transform"
          enter-from="-translate-y-full opacity-0"
          enter-to="translate-y-0 opacity-100"
          leave="transition ease-in-out duration-300 transform"
          leave-from="translate-y-0 opacity-100"
          leave-to="translate-y-full opacity-0"
        >
          <!-- 单个div显示当前随机数,无需v-for -->
          <div class="h-full w-full rounded-md bg-green-500 shadow-lg flex items-center justify-center">
            {{ currentNumber }}
          </div>
        </TransitionRoot>
      </div>
    </div>
    <button
      @click="resetIsShowing"
      class="mt-8 flex transform items-center rounded-full bg-black bg-opacity-20 px-3 py-2 text-sm font-medium text-white transition hover:scale-105 hover:bg-opacity-30 focus:outline-none active:bg-opacity-40"
    >
      <svg viewBox="0 0 20 20" fill="none" class="h-5 w-5 opacity-70">
        <path
          d="M14.9497 14.9498C12.2161 17.6835 7.78392 17.6835 5.05025 14.9498C2.31658 12.2162 2.31658 7.784 5.05025 5.05033C7.78392 2.31666 12.2161 2.31666 14.9497 5.05033C15.5333 5.63385 15.9922 6.29475 16.3266 7M16.9497 2L17 7H16.3266M12 7L16.3266 7"
          stroke="currentColor"
          stroke-width="1.5"
        />
      </svg>

      <span class="ml-3">Click to transition</span>
    </button>
  </div>
</template>

<script setup>
import { ref } from 'vue'
import { TransitionRoot } from '@headlessui/vue'

const isShowing = ref(true)
const currentNumber = ref(1) // 单个ref存储当前显示的数字
const numberArray = ref(Array.from({length: 10}, (e, i)=> i + 1))

const sleep = (milliseconds) => {
  return new Promise(resolve => setTimeout(resolve, milliseconds))
}

async function resetIsShowing() {
  for (let i = 0; i < numberArray.value.length; i++) {
    // 先生成随机数字并更新
    const randomNum = numberArray.value[Math.floor(Math.random() * numberArray.value.length)]
    currentNumber.value = randomNum
    
    await sleep(800)

    isShowing.value = false

    // 用await保证时序,避免异步混乱
    await sleep(500)
    isShowing.value = true
  }
}
</script>

关键修改说明

  • 用currentNumber单个ref替代numbers数组,直接绑定到div显示,避免多元素触发Transition的ref传递错误。
  • 将原setTimeout改为await sleep(500),确保动画隐藏完成后再显示新内容,时序更稳定。
  • 提前更新随机数字,保证动画显示时直接展示最新值,符合老虎机的视觉逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 08:20:31