Vue 提供了一些内置组件和对应的 API 来完成动画,利用它们可以方便地实现动画效果。
<transition>
内置组件:
Vue 提供了 <transition>
内置组件,可以给任意元素或组件添加进入/离开时的动画效果。在条件渲染、动态组件、改变 key 属性时将会触发。
主要针对单个元素或组件。
transition 过渡动画:
<!-- 给 <div> 元素的显示与隐藏添加过渡动画 -->
<template>
<!-- 2. 使用 <transition> 包裹想要添加动画的元素,Vue 在恰当的时机将编写好的类自动添加、移除 -->
<transition>
<div v-if="isShow">Hello Vue</div>
</transition>
<button @click="handleChange">切换</button>
</template>
<script setup>
import {ref} from 'vue'
const isShow = ref(false)
const handleChange = () => {
isShow.value = !isShow.value
}
</script>
<style scoped>
/* 1. 编写想要的动画的类 */
/* 进入前的状态 */
.v-enter-from,
/* 离开后的状态 */
.v-leave-to {
opacity: 0;
}
/* 进入后的状态 */
.v-enter-to,
/* 离开前的状态 */
.v-leave-from {
opacity: 1;
}
/* 进入时的效果 */
.v-enter-active,
/* 离开时的效果 */
.v-leave-active {
transition: opacity 1s ease;
}
</style>
animation 序列帧动画:
<!-- 给 <div> 元素的显示与隐藏添加 animation 动画 -->
<template>
<!-- 2. 使用 <transition> 包裹想要添加动画的元素,Vue 在恰当的时机将编写好的类自动添加、移除 -->
<transition>
<div v-if="isShow">Hello Vue</div>
</transition>
<button @click="handleChange">切换</button>
</template>
<script setup>
import {ref} from 'vue'
const isShow = ref(false)
const handleChange = () => {
isShow.value = !isShow.value
}
</script>
<style scoped>
/* 1. 编写想要的动画的类 */
@keyframes enterAnimation {
0% {
transform: scale(0);
}
50% {
transform: scale(2);
}
100% {
transform: scale(1);
}
}
/* 进入时的效果 */
.v-enter-active {
animation: enterAnimation 1s ease;
}
@keyframes leaveAnimation {
0% {
transform: scale(1);
}
50% {
transform: scale(2);
}
100% {
transform: scale(0);
}
}
/* 离开时的效果 */
.v-leave-active {
animation: leaveAnimation 1s ease;
}
</style>
<transition>
组件的原理:
Vue 并没有编写好动画的类,只是会在恰当的时机将开发者编写好的类自动添加、移除。
当插入或者删除包含在 <transition>
组件中的元素时,Vue 会做以下处理:
- 自动嗅探目标元素是否应用了 CSS 动画,如果有的话,那么将会在恰当的时机自动添加/移除 CSS 类名。
- 如果
<transition>
组件提供了 JavaScript 钩子函数,那么这些钩子函数将会在恰当的时机被调用。 - 如果没有检测到 CSS 动画并且也没有找到 JavaScript 钩子函数,那么 DOM 的插入、删除操作将会立即执行。
<transition>
组件的属性:
- name:动画的 class 类名的前缀。
- duration:显式地设置动画的持续时间。
- appear:首次渲染时是否采用动画。默认首次渲染是没有动画的。
- type:当同时使用过渡动画和序列帧动画时,以谁的时间为动画的结束时刻。属性值可以为 transition 或者 animation。当不设置type时,默认会取 transitioned 和 animationed 两者更长的为结束时刻。
- mode:当一个动画在两个元素之间切换时,如果不希望同时执行进入动画和离开动画,俺么可以设置动画的过渡模式。属性值可以为
in-out
,新元素先过渡进入,完成之后当前元素再过渡离开;out-in
:当前元素先过渡离开,新元素再过渡进入。<template> <!-- 如果不设置 mode 过渡属性,那么一个元素显示、一个元素消失将会同时执行动画。设置 mode="out-in" 之后,Hello Vue 会先消失,然后你好,世界再显示 --> <transition mode="out-in"> <div v-if="isShow">Hello Vue</div> <div v-else> 你好,世界</div> </transition> <button @click="handleChange">切换</button> </template> <script setup> import {ref} from 'vue' const isShow = ref(true) const handleChange = () => { isShow.value = !isShow.value } </script> <style scoped> .v-enter-from, .v-leave-to { opacity: 0; } .v-enter-to, .v-leave-from { opacity: 1; } .v-enter-active, .v-leave-active { transition: opacity 1s ease; } </style>
动画的 class 类:
v-enter-from
:进入动画的起始状态。在元素被插入之前添加,在元素插入完成后的下一帧移除。v-enter-active
:进入动画的生效状态。应用于整个进入动画阶段,在元素被插入之前添加,在动画完成之后移除。v-enter-to
:进入动画的结束状态。在元素被插入完成后的下一帧添加,在动画完成之后移除。v-leave-from
:离开动画的起始状态。在离开动画被触发时立刻添加,下一帧被移除。v-leave-active
:离开动画的生效状态。应用于整个离开动画阶段,在离开动画被触发时立刻添加,在动画完成之后移除。v-leave-to
:离开动画的结束状态。在离开动画被触发后的下一帧添加,在动画完成之后移除。
动画的 class 类的命名规则:
- 如果使用的是一个没有 name 的
<transition>
,那么所有的 class 是以v-
作为默认前缀。 - 如果给
<transition>
添加了 name 属性,那么所有的 class 会以 name 属性值做前缀。例如<transition name=''fade>
,那么所有的 class 以fase-
为前缀。
<transition-group>
内置组件:
Vue 提供了 <transition-group>
内置组件,可以为列表添加、删除数据时添加动画效果。
主要针对列表元素或组件。
<!-- 列表添加、删除元素时,添加、删除的元素将会运用动态效果 -->
<template>
<!-- 默认情况下,<transition-group> 不会渲染成一个元素的包裹器,可以通过指定 tag 属性将其渲染为想要的元素 -->
<transition-group tag="div">
<!-- 内部元素总是需要提供唯一的 key 属性。CSS 动画的类将会应用在内部的元素上,而不是外面的包裹容器上 -->
<template v-for="item in nums" :key="item">
<div>{{ item }}</div>
</template>
</transition-group>
<button @click="handleAdd">插入</button>
<button @click="handlDelete">删除</button>
</template>
<script setup>
import {ref} from 'vue'
const nums = ref([0, 1, 2, 3, 4, 5])
const randomIndex = () => {
return Math.floor(Math.random() * nums.value.length)
}
const handleAdd = () => {
nums.value.splice(randomIndex(), 0, nums.value.length)
}
const handlDelete = () => {
nums.value.splice(randomIndex(), 1)
}
</script>
<style scoped>
.v-enter-from,
.v-leave-to {
opacity: 0;
transform: translateY(30px);
}
.v-enter-to,
.v-leave-from {
opacity: 1;
transform: translateY(0);
}
.v-enter-active,
.v-leave-active {
transition: opacity 1s ease;
}
</style>
此时,列表添加、删除的元素是有动画的,但是由于添加或者删除导致的其他需要移动的元素是没有动画的。可以使用一个新增的 v-move
的 class 来完成动画,它会在元素改变位置的过程中应用。
<transition-group>
组件的属性:
- 除了 mode 过渡模式不可用外,其他
<transition>
组件的属性都可用。 - tag:默认情况下,
<transition-group>
不会渲染成一个元素的包裹器,可以通过指定 tag 属性将其渲染为想要的元素。
动画的 class 类:
<transition>
中动画的 class 类都可用。v-move
:会在元素改变位置的过程中应用。
上面的示例中,列表添加、删除的元素是有动画的,但是由于添加或者删除而导致的其他需要移动的元素是没有动画的。可以使用 新增的<template> <transition-group tag="div"> <template v-for="item in nums" :key="item"> <div>{{ item }}</div> </template> </transition-group> <button @click="handleAdd">插入</button> <button @click="handlDelete">删除</button> </template> <script setup> import {ref} from 'vue' const nums = ref([0, 1, 2, 3, 4, 5]) const randomIndex = () => { return Math.floor(Math.random() * nums.value.length) } const handleAdd = () => { nums.value.splice(randomIndex(), 0, nums.value.length) } const handlDelete = () => { nums.value.splice(randomIndex(), 1) } </script> <style scoped> .v-enter-from, .v-leave-to { opacity: 0; transform: translateY(30px); } .v-enter-to, .v-leave-from { opacity: 1; transform: translateY(0); } .v-enter-active, .v-leave-active, /* 为其他移动的元素添加动画 */ .v-move { transition: all 3s ease; } /* 元素离开时设置绝对定位,否则的话,离开动画的过程中,被删除元素仍然占据位置,导致其他需要移动的元素无法运用动画 */ .v-leave-active { position: absolute; } </style>
v-move
的 class 来完成动画。