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

Vue 3 TransitionGroup中scaleY正常但scale/scaleX异常问题求助

问题:Vue TransitionGroup使用scaleX/scale时自动添加translateX导致异常过渡

我使用Vue 3.4.27版本,将TransitionGroup组件用作无序列表。当设置CSS使用scaleY(0)时过渡效果正常,但使用scale(0)或scaleX(0)时,Vue会自动添加translateX(),导致元素在缩放的同时从右侧滑入。我原以为scaleX()应该让元素处于列表的最终位置,然后逐渐横向展开到完整尺寸,但实际效果并非如此。

组件代码

<TransitionGroup
  name="grow-y"
  tag="ul"
  class="container"
  appear
>
  <li v-for="(name, index) in names" :key="index">
    {{name}}
    <button @click="names.splice(index, 1);">-</button>
  </li>
</TransitionGroup>

<hr>
<div>Using scaleX()</div>
<transition-group
  name="grow-x"
  tag="ul"
  class="container"
  appear
>
  <li v-for="(name, index) in names" :key="index">
    {{name}}
    <button @click="names.splice(index, 1);">-</button>
  </li>
</transition-group>

CSS代码

.grow-x-enter,
.grow-x-leave-to,
.grow-x-enter-from {
  transform: scaleX(0);
  opacity: 1;
  position: absolute;
  left: 0;
}

.grow-y-enter,
.grow-y-leave-to,
.grow-y-enter-from {
  transform: scaleY(0);
  opacity: 1;
}

/**
  all transitions will share the animation types
 */
.grow-x-enter-active,
.grow-x-leave-active,
.grow-x-move,
.grow-y-enter-active,
.grow-y-leave-active,
.grow-y-move {
  transition: all 5s cubic-bezier(0.55, 0, 0.1, 1);
}

解决思路

问题根源

TransitionGroup的move过渡会自动计算元素的位置偏移并添加translate变换。当元素初始状态为scaleX(0)时,宽度被压缩为0,Vue计算位置时会错误认为元素需要从右侧移动到目标位置,因此自动添加translateX。而scaleY(0)不影响元素宽度,所以位置计算不受干扰。

方案1:禁用move过渡(最简单)

如果不需要列表项删除/排序时的位置移动过渡,可以去掉.grow-x-move的transform过渡:

.grow-x-move {
  /* 仅过渡opacity,避免干扰scaleX动画 */
  transition: opacity 5s cubic-bezier(0.55, 0, 0.1, 1);
}

方案2:调整变换原点+移除绝对定位

默认transform-origin是元素中心,scaleX(0)会让元素向两侧收缩,导致宽度为0时位置偏移。将变换原点设为左侧,同时移除破坏布局的绝对定位:

.grow-x-enter,
.grow-x-leave-to,
.grow-x-enter-from {
  transform: scaleX(0);
  opacity: 1;
  transform-origin: left center; /* 从左侧开始缩放 */
  /* 移除position:absolute,避免脱离文档流 */
}

方案3:自定义过渡钩子(保留move过渡)

如果需要保留列表项的位置移动动画,可以通过过渡钩子手动控制初始状态,绕过Vue的自动位置计算:

<transition-group
  name="grow-x"
  tag="ul"
  class="container"
  appear
  @before-enter="(el) => {
    el.style.transform = 'scaleX(0)'
    el.style.transformOrigin = 'left center'
  }"
  @enter="(el, done) => {
    el.style.transform = ''
    el.offsetWidth // 触发重绘,确保动画生效
    done()
  }"
>
  <li v-for="(name, index) in names" :key="index">
    {{name}}
    <button @click="names.splice(index, 1);">-</button>
  </li>
</transition-group>

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 23:34:58