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

Vue Transition Group互斥元素列表动画异常问题求助

解决Vue Transition-Group切换互斥分组时动画失效的问题

我太懂这个头疼的点了——用<transition-group>给筛选列表加动画,结果切换互斥分组(比如从p1切到p2)时,新旧元素的动画挤在一起,效果特别生硬,偏偏Vue文档还明确说<transition-group>不支持过渡模式(mode),谁看了不挠头😅。不过咱们可以通过控制筛选结果的更新时机,模拟出“先离开再进入”的流畅过渡效果,下面是具体方案:

核心思路

当检测到用户从一个非“全部”分组切换到另一个非“全部”分组时,先保留当前列表让它完整走完离开动画,等所有旧元素都消失后,再渲染新的分组列表,彻底避免动画重叠的问题。

修改后的完整代码

HTML部分

<script src="https://unpkg.com/vue/dist/vue.js"></script>
<div id="app">
  <select v-model="filter">
    <option v-for="(o, i) in filterOptions" :key="`o-${i}`" :value="o">
      {{ o }}
    </option>
  </select>
  <table>
    <transition-group 
      name="fade" 
      tag="ul"
      @leave="handleLeave"
    >
      <li v-for="t in filteredRows" :key="t.label">
        {{ t.label }} - {{ t.prop }}
      </li>
    </transition-group>
  </table>
</div>

JavaScript部分

new Vue({
  el: '#app',
  data: {
    filter: 'all',
    filterOptions: ['all', 'p1', 'p2'],
    rows: [
      { label: 'Row 1', prop: 'p1' },
      { label: 'Row 2', prop: 'p1' },
      { label: 'Row 3', prop: 'p2' },
      { label: 'Row 4', prop: 'p2' },
    ],
    tempFilter: 'all', // 控制实际渲染的筛选值,避免直接修改filter触发冲突
    leavingElementCount: 0 // 跟踪正在执行离开动画的元素数量
  },
  watch: {
    filter(newFilter, oldFilter) {
      // 判断是否是互斥分组间的切换(非all→非all)
      if (oldFilter !== 'all' && newFilter !== 'all' && oldFilter !== newFilter) {
        // 先保留旧筛选值,触发旧列表的离开动画
        this.tempFilter = oldFilter;
        this.leavingElementCount = this.filteredRows.length;
      } else {
        // 其他场景(all→分组、分组→all、all→all)直接更新
        this.tempFilter = newFilter;
      }
    }
  },
  computed: {
    filteredRows: function() {
      // 基于tempFilter而非原始filter渲染列表
      return this.rows.filter(o => o.prop === this.tempFilter || this.tempFilter === 'all');
    }
  },
  methods: {
    handleLeave(el, done) {
      // 每完成一个元素的离开动画,计数减一
      this.leavingElementCount--;
      done();
      // 所有旧元素都离开后,更新tempFilter渲染新列表
      if (this.leavingElementCount === 0) {
        this.tempFilter = this.filter;
      }
    }
  }
})

CSS部分

.fade-enter-active, .fade-leave-active {
  transition: all .5s ease;
}
.fade-enter, .fade-leave-to {
  opacity: 0;
  transform: translateX(20px); /* 加个位移让动画更自然,避免只有透明度变化的生硬感 */
}
li {
  margin: 4px 0;
  padding: 8px;
  background: #f5f5f5;
  border-radius: 4px;
}

关键细节解释

  1. 状态变量拆分:用tempFilter控制实际渲染的列表,避免直接修改filter导致新旧列表同时触发动画;leavingElementCount精准跟踪离开动画的完成进度。
  2. 筛选切换判断:通过watch监听filter变化,只在互斥分组切换时触发延迟逻辑,其他场景保持正常的即时更新。
  3. leave事件监听:绑定<transition-group>的@leave事件,确保所有旧元素的离开动画完成后,才渲染新的分组列表。
  4. 动画优化:给进入/离开动画添加transform位移效果,比单纯的透明度变化更有层次感。

这样修改后,切换p1和p2时,旧列表会先平滑退场,新列表再优雅入场,完全解决了动画重叠生硬的问题~

内容的提问来源于stack exchange,提问作者João Menighin

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.12 04:52:52