一、Transition 组件基础

1. 核心作用

Vue3 的 <Transition> 组件用于给元素/组件添加进入/离开的过渡动画效果,支持四种触发场景:

  • 条件渲染(v-if)
  • 条件展示(v-show)
  • 动态组件切换
  • 组件根节点变化

2. 基础实现

<template>
  <button @click="show = !show">切换</button>
  <Transition name="fade">
    <div v-if="show" class="box">过渡内容</div>
  </Transition>
</template>

<script setup>
import { ref } from 'vue'
const show = ref(false)
</script>

<style>
/* 进入/离开过程的激活状态 */
.fade-enter-active,
.fade-leave-active {
  transition: opacity 0.5s ease;
}

/* 进入前/离开后的状态 */
.fade-enter-from,
.fade-leave-to {
  opacity: 0;
}
</style>

通过 name 属性定义 CSS 类名前缀,Vue 会自动在适当时机添加/移除以下 6 个类:

  • v-enter-from → 进入开始状态
  • v-enter-active → 进入过渡过程
  • v-enter-to → 进入结束状态
  • v-leave-from → 离开开始状态
  • v-leave-active → 离开过渡过程
  • v-leave-to → 离开结束状态

二、进阶动画实现

1. 自定义类名(兼容动画库)

<Transition 
  enter-active-class="animate__animated animate__bounceIn"
  leave-active-class="animate__animated animate__bounceOut"
>
  <div v-if="show">Animate.css 动画</div>
</Transition>

通过指定 enter-active-class 等属性,可与第三方动画库(如 Animate.css)无缝集成

2. 关键帧动画

@keyframes slide {
  from { transform: translateX(-100%); }
  to { transform: translateX(0); }
}

.slide-enter-active {
  animation: slide 0.5s;
}
.slide-leave-active {
  animation: slide 0.5s reverse;
}

与 CSS Transition 不同,关键帧动画只需定义 *-active 类即可

3. 混合动画处理

当元素同时存在过渡和动画时,需指定 type 属性:

<Transition type="animation">...</Transition>

这会强制 Vue 优先监听 animationend 事件

三、JavaScript 钩子

通过事件监听实现复杂动画逻辑:

<Transition
  @before-enter="onBeforeEnter"
  @enter="onEnter"
  @after-enter="onAfterEnter"
  @enter-cancelled="onEnterCancel"
  @before-leave="onBeforeLeave"
  @leave="onLeave"
  @after-leave="onAfterLeave"
  @leave-cancelled="onLeaveCancel"
>
  <!-- 内容 -->
</Transition>

示例 GSAP 动画:

function onEnter(el, done) {
  gsap.from(el, {
    duration: 0.8,
    opacity: 0,
    y: 100,
    onComplete: done
  })
}

每个钩子接收元素参数,enter/leave 钩子额外接收 done 回调

四、TransitionGroup 组件

用于列表元素动画,新增特性:

  • 必须设置 tag 属性定义包裹元素
  • 每个元素需要唯一 key
  • 支持 move-class 处理元素位置变化
<TransitionGroup name="list" tag="ul">
  <li v-for="item in items" :key="item.id">
    {{ item.text }}
  </li>
</TransitionGroup>

<style>
.list-move {
  transition: transform 0.8s ease;
}
.list-enter-from {
  opacity: 0;
  transform: translateY(30px);
}
</style>

通过绝对定位优化离开动画:

.list-leave-active {
  position: absolute;
}

实现元素平滑重排

五、性能优化建议

  1. 优先使用 transform/opacity
    避免触发重排的属性(如 width/height),使用 GPU 加速属性

  2. 合理设置 will-change

    .v-enter-active {
      will-change: transform, opacity;
    }
    
  3. 延迟动画策略
    通过 data-index 实现交错动画:

    function onEnter(el, done) {
      gsap.to(el, {
        delay: el.dataset.index * 0.1,
        /*...*/
      })
    }
    
  4. 强制硬件加速

    .optimized {
      transform: translateZ(0);
    }
    

六、常见问题解决

  1. 初始渲染动画
    添加 appear 属性:

    <Transition appear>...</Transition>
    
  2. 过渡模式设置

    <Transition mode="out-in">...</Transition>
    
  3. 滚动条抖动
    使用 overflow: hidden 临时隐藏滚动条:

    .v-enter-active {
      overflow: hidden;
    }