Vue3 提供了 TransitionTransitionGroup 两个过渡动画组件,其中 Transition 主要用于元素或组件使用 v-ifv-show 和路由切换时显示过渡动画,TransitionGroup 用于 v-for 渲染的列表项添加或移除时显示过渡动画。

Transition

Transition 可以实现在组件或元素插入到页面和从页面移除时显示过渡动画,这个组件在 Vue2 也出现过。

通过 v-ifv-show 、组件路由跳转都可以使用 Transition 的过渡动画。

组件使用

下面是一个简单的 Transition

<template>
  <div>
    <button type="button" @click="show = !show">开关</button>
    <Transition>
      <div id="box" v-show="show"></div>
    </Transition>
  </div>
</template>

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

<style>
/*Transition 的过渡时间和动画*/
.v-enter-active,.v-leave-active {
  transition: opacity 0.5s ease;
}
.v-enter-from,.v-leave-to {
  opacity: 0;
}
</style>

上面的 HTML 代码中包含一个 div 和一个 button ,点击按钮 div 就可以实现淡入淡出的显示和隐藏,过渡动画的时间是 0.5秒。

Transition 的动画和时间都需要通过 CSS 来定义,如果没有 CSS 的话,是不会有过渡动画效果的。

过渡动画的时间和动画也需要放到指定的 class ,下面是 class 说明:

  1. .v-enter-from : 进入动画的起始状态。在元素插入之前添加,在元素插入完成后的下一帧移除。
  2. .v-enter-active : 进入动画的生效状态。应用于整个进入动画阶段。在元素被插入之前添加,在过渡或动画完成之后移除。这个 class 可以被用来定义进入动画的持续时间、延迟与速度曲线类型。
  3. .v-enter-to : 进入动画的结束状态。在元素插入完成后的下一帧被添加 (也就是 .v-enter-from 被移除的同时),在过渡或动画完成之后移除。
  4. .v-leave-from : 离开动画的起始状态。在离开过渡效果被触发时立即添加,在一帧后被移除。
  5. .v-leave-active : 离开动画的生效状态。应用于整个离开动画阶段。在离开过渡效果被触发时立即添加,在过渡或动画完成之后移除。这个 class 可以被用来定义离开动画的持续时间、延迟与速度曲线类型。
  6. .v-leave-to : 离开动画的结束状态。在一个离开动画被触发后的下一帧被添加 (也就是 .v-leave-from 被移除的同时),在过渡或动画完成之后移除。

上面实现的淡入淡出主要就是更改透明度,在 .v-enter-from 元素插入之前把透明度设置为 0,在 .v-enter-active 元素插入之后恢复默认的透明度,同时设置过渡时间为 0.5秒,隐藏的时候也是一样的。

给过渡效果命名

有时候可能会遇到一个组件中,不同的元素需要使用不同的过渡效果,如果还按照上面的方法直接给元素套 Transitionclass 的话,可能会导致所有元素使用的都是相同的过渡效果。

给过渡效果命名可以在 Transition 组件上传入一个 name prop ,如下:

<Transition name="box">
  <div id="box" v-show="show"></div>
</Transition>

Transition 加入过渡名称后 class 的前缀也不能再使用 v- 开头,需要使用 名称- 作为前缀。

上面给 Transition 传入了一个 box 作为过渡名称,它的 class 就是:

.box-enter-active,.box-leave-active {
  transition: opacity 0.5s ease;
}
.box-enter-from,.box-leave-to {
  opacity: 0;
}

使用 CSS 的 animation 动画

Transition 组件也可以使用 CSS 的 animation 动画。在 CSS animation 动画中,一般只需要在 .v-enter-active.v-leave-active 两个 class 下声明。

下面是一个简单的 Transition CSS animation 动画:

<Transition>
  <div id="box" v-show="show"></div>
</Transition>
/*进入的动画*/
.v-enter-active {
  animation: box-enter 0.5s cubic-bezier(0.0, 0.0, 1.0, 1.0);
}
@keyframes box-enter {
  0% {
    width: 0;
    height: 0;
  }
  100% {
    width: 150px;
    height: 150px;
  }
}
/*离开的动画*/
.v-leave-active {
  animation: box-leave 0.5s cubic-bezier(0.0, 0.0, 1.0, 1.0);
}
@keyframes box-leave {
  0% {
    width: 150px;
    height: 150px;
  }
  100% {
    width: 0;
    height: 0;
  }
}

上面实现的是一个简单的缩放动画,显示的时候慢慢放大,隐藏的时候慢慢缩小。

animation 后面的 box-enterbox-leave 是定义动画名称,主要用于 @keyframes 设置动画。0.5s 是动画时间。cubic-bezier(0.0, 0.0, 1.0, 1.0) 是一个控制动画匀速运动的函数。下面的 @keyframes 就是设置动画在不同阶段的状态,我上面只是设置了 0%100% 的状态。

动画效果如下

Vue3Transition组件的animation动画

自定义过渡 class

除了使用官方定义的 class 外,你也可以自定义过渡的 class,自定义 class 可以通过给 Transition 组件传入指定的 prop 来定义。

下面是可以传入的 prop:

  • enter-from-class
  • enter-active-class
  • enter-to-class
  • leave-from-class
  • leave-active-class
  • leave-to-class

这些 prop 名称的作用可以参考上面的组件使用的 class 说明。

还是上面的 CSS animation 动画,自定义 class 如下:

<Transition enter-active-class="show" leave-active-class="hide">
  <div id="box" v-show="show"></div>
</Transition>
/*进入的动画*/
.show {
  animation: box-enter 0.5s cubic-bezier(0.0, 0.0, 1.0, 1.0);
}
/*离开的动画*/
.hide {
  animation: box-leave 0.5s cubic-bezier(0.0, 0.0, 1.0, 1.0);
}

Transition 的 JavaScript 钩子

Transition 提供了一些钩子事件,通过监听钩子事件,你可以使用 JavaScript 来实现过渡动画,也可以根据过渡阶段来加载数据。

下面是事件说明:

  • before-enter : 在元素被插入到页面前调用,相当于 enter-from
  • enter : 在元素插入到页面的下一帧调用,可以用来设置动画
  • after-enter : 过渡完成时调用
  • enter-cancelled : 也是在过渡完成后调用,在 after-enter 之后
  • before-leave : 在离开过渡开始前调用
  • leave : 在离开过渡开始时调用,可以用来设置过渡动画
  • after-leave : 在离开过渡完成且元素被移除时调用
  • leave-cancelled : 在过渡完成后调用,只能在 v-show 使用

下面实现元素淡入后显示文字,元素淡出前移除文字:

<Transition @after-enter="onAfterEnter" @before-leave="onBeforeLeave">
  <div id="box" v-show="show">{{ text }}</div>
</Transition>

JavaScript setup:

import { ref } from 'vue';
const text = ref('');

// 进入过渡完成时调用
function onAfterEnter() {
  text.value = 'Hello';
}
// 离开过渡开始前调用
function onBeforeLeave() {
  text.value = '';
}

这里的过渡动画使用的还是 CSS 动画,下面是实现效果:

Vue3Transition实现淡入后显示文字,淡出前移除文字

如果你的动画是通过 JS 实现,不需要使用 CSS 的话,可以给 Transition 组件加一个 :css="false" 属性来跳过 CSS 过渡检测,可以提升性能和防止 CSS 干扰过渡效果。

封装过渡效果

如果你需要在多个组件中使用同一个过渡效果的话,也可以使用插槽 slot 来封装过渡效果。

下面还是封装一个实现淡入淡出的插槽组件,组件命名为 FadeInAndFadeOut.vue

<template>
  <Transition name="fade">
    <slot></slot>
  </Transition>
</template>

<style>
.fade-enter-active,.fade-leave-active {
  transition: opacity 0.5s ease;
}
.fade-enter-from,.fade-leave-to {
  opacity: 0;
}
</style>

在其他组件中调用上面封装的淡入淡出过渡:

<template>
  <div>
    <FadeInAndFadeOut>
      <div id="box" v-show="show"></div>
    </FadeInAndFadeOut>
  </div>
</template>

<script setup>
import FadeInAndFadeOut from './FadeInAndFadeOut.vue';
</script>

初次渲染时使用过渡效果

Transition 在打开页面第一次渲染的时候是不会有过渡效果的,如果需要在第一次渲染的时候就显示过渡效果可以给 Transition 组件加一个 appear 属性,如下:

<Transition appear></Transition>

TransitionGroup

TransitionGroup 主要用于 v-for 渲染的列表项元素添加或移除时显示过渡动画。

TransitionGroup 也可以使用和 Transition 一样的 CSS class,也可以使用 name prop 更改 CSS class 前缀,也可以使用和 Transition 一样的事件钩子。

进入和离开动画

下面是一个简单的 TransitionGroup 列表过渡:

<TransitionGroup name="list" tag="ul">
  <li v-for="item in list" :key="item">{{ item }}</li>
</TransitionGroup>
.list-enter-active,.list-leave-active {
  transition: all 0.5s ease;
}
.list-enter-from,.list-leave-to {
  opacity: 0;
  transform: translateX(30px);
}

列表过渡效果如下:

Vue3TransitionGroup列表过渡

我上面的 TransitionGroup 组件内直接就写 li 元素了,没有写列表的外层 ul ,我给 TransitionGroup 组件加了一个 tag=“ul” ,通过 tag 属性也能在 li 的外层渲染一个 ul

TransitionGroup 内使用 v-for 渲染的列表每个列表项都需要有一个单独的 :key 值,:key 值不能出现重复,也不能使用 v-for="(item, index) of list" 这种自动生成的索引值。

平滑移动列表项

上面的过渡动画在列表中间插入或移除元素时,周围的列表项会立即移动,没有过渡动画。

下面修改一下 CSS,当列表发生变化时,让周围的列表项也能平滑的移动:

.list-enter-active,.list-leave-active,.list-move {
  transition: all 0.5s ease;
}
.list-enter-from,.list-leave-to {
  opacity: 0;
  transform: translateX(30px);
}
.list-leave-active {
  position: absolute;
}

效果如下:

Vue3TransitionGroup列表平滑移动