2、CSS过渡实战:v-enter/v-leave类名详解、transition属性配置、opacity与transform组合动画、过渡模式(in-out/out-in)

好,咱们直接进入正题。CSS过渡是Vue动画系统里最基础、也是最常用的一环。说白了,它就是利用CSS的transition属性,让元素在插入或移除时「动起来」。很多新手觉得这玩意儿简单,但实际项目中踩坑的不少。我今天就把这些细节掰开揉碎了讲清楚。

2.1 v-enter/v-leave 类名详解

Vue为过渡提供了6个类名钩子。但最核心的,就是v-enterv-leave这两组。我习惯把它们分成「入场三兄弟」和「离场三兄弟」来记。

类名 时机 作用
v-enter 元素插入前 定义起始状态(透明度0、位移等)
v-enter-active 整个入场阶段 定义过渡属性、时长、缓动函数
v-enter-to 元素插入后下一帧 定义结束状态(透明度1、位移归零)
v-leave 离开触发瞬间 定义离开起始状态
v-leave-active 整个离场阶段 定义过渡属性、时长、缓动函数
v-leave-to 离开触发后下一帧 定义离开结束状态

关键点:v-enter 和 v-leave-to 是「一对」——它们描述的都是元素的最终消失状态。v-enter-to 和 v-leave 是另一对——描述的都是元素的可见状态。

我在项目中遇到过一个问题:有同事把v-enterv-enter-to写反了,结果动画一开始元素就闪一下,然后才慢慢消失。嗯,这就是没搞懂「起始」和「结束」的关系。

2.2 transition 属性配置

光有类名还不够,你得告诉浏览器「怎么动」。这就是transition属性的活儿。我一般这么写:

.fade-enter-active,
.fade-leave-active {
  transition: opacity 0.3s ease;
}

.fade-enter,
.fade-leave-to {
  opacity: 0;
}

这里有个小细节——transition属性写在*-active类里。为什么?因为*-active在整个过渡期间都存在,而*-enter*-enter-to只存在一瞬间。

个人习惯:我会把 transition 属性统一写在 active 类里,这样维护起来清晰。别把 transition 分散到多个类里,后期改起来想骂人。

transition 的完整语法是:

transition: property duration timing-function delay;

举个例子:

.slide-enter-active {
  transition: all 0.4s cubic-bezier(0.25, 0.8, 0.25, 1);
}

你想想看,all虽然方便,但性能上不如指定具体属性。我建议能用opacitytransform就别用all,尤其是列表动画里。

2.3 opacity 与 transform 组合动画

这是实战中最常用的组合。为什么?因为这两个属性不会触发重排(reflow),性能好。说白了,GPU帮你干活,CPU不累。

来看一个经典的淡入+上滑效果:

.fade-slide-enter-active {
  transition: all 0.4s ease-out;
}

.fade-slide-leave-active {
  transition: all 0.3s ease-in;
}

.fade-slide-enter {
  opacity: 0;
  transform: translateY(20px);
}

.fade-slide-leave-to {
  opacity: 0;
  transform: translateY(-20px);
}

这里我用了不同的缓动函数——入场用ease-out,离场用ease-in。为什么?入场时我们希望元素「优雅地出现」,离场时我们希望它「干脆地消失」。这是我个人的小偏好,你可以试试看效果。

曾经踩过的坑:transform 的初始值和结束值一定要对应好。比如入场时 translateY(20px) 到 translateY(0),离场时 translateY(0) 到 translateY(-20px)。别搞反了,否则元素会「跳一下」。

再进阶一点,可以加上缩放:

.scale-fade-enter {
  opacity: 0;
  transform: scale(0.8) translateY(10px);
}

.scale-fade-enter-to {
  opacity: 1;
  transform: scale(1) translateY(0);
}

这种组合动画在弹窗、提示框里特别常见。我做过一个后台管理系统,所有模态框都用这种效果,用户反馈「感觉很丝滑」。

2.4 过渡模式:in-out 与 out-in

好,接下来是很多人容易忽略的点——过渡模式。默认情况下,Vue的<Transition>是「同时进出」的。什么意思?就是新元素进来和旧元素离开同时发生。这会导致布局抖动,尤其当两个元素占据同一位置时。

看代码:

<Transition name="fade" mode="out-in">
  <component :is="currentView" :key="currentView" />
</Transition>

两种模式的区别:

模式 行为 适用场景
out-in 旧元素先离开,新元素再进入 页面切换、tab切换(推荐)
in-out 新元素先进入,旧元素再离开 某些特殊效果(如叠加提示)
默认 同时进出 简单场景,元素不重叠时

我个人习惯,99%的场景用out-in。为什么?因为用户体验最好——用户先看到旧内容消失,然后新内容出现,逻辑清晰。我曾经在一个电商项目里用了默认模式,结果两个商品卡片同时存在,重叠在一起,被测试提了bug。从那以后,我只要涉及视图切换,必加mode="out-in"

重要提醒:使用过渡模式时,一定要给动态组件加上:key属性。否则Vue无法区分新旧元素,过渡效果会失效。我见过有人在这上面卡了一下午。

再补充一个in-out的实用场景——消息提示。比如一个通知条出现时,旧的通知条应该先「让位」:

<TransitionGroup name="notification" mode="in-out">
  <div v-for="msg in messages" :key="msg.id">
    {{ msg.text }}
  </div>
</TransitionGroup>

这里用in-out,新消息先滑入,旧消息再滑出,视觉上很连贯。不过说实话,这种场景不多,你记住out-in是主力就行。

小结

这一章的内容其实就四个字——「起止过渡」。搞懂了v-enterv-leave-to的关系,配置好transition属性,再用opacitytransform组合出丝滑效果,最后用mode控制进出顺序。嗯,CSS过渡这块你就拿捏了。

下一章我们会聊JavaScript钩子动画,那个更灵活,但坑也更多。到时候见。

公众号:蓝海资料掘金营,微信deep3321