Vue3的内置组件 -实现过渡动画 TransitionGroup
是一个内置组件,用于对 v-for 列表中的元素或组件的插入、移除和顺序改变添加动画效果
支持和 基本相同的 props、CSS 过渡 class 和 JavaScript 钩子监听器,但有以下几点区别:
-
默认情况下,它不会渲染一个容器元素。但你可以通过传入 tag prop 来指定一个元素作为容器元素来渲染。
-
过渡模式在这里不可用,因为我们不再是在互斥的元素之间进行切换。
-
列表中的每个元素都必须有一个独一无二的 key attribute。
-
CSS 过渡 class 会被应用在列表内的元素上,而不是容器元素上
基本用法
<template>
<button @click="addItem">增加item</button>
<button @click="removeItem">移除item</button>
<TransitionGroup name="list" tag="div">
<div v-for="(item, index) in items" :key="item" :data-index="index">
{{ item }}
</div>
</TransitionGroup>
</template>
<script setup lang="ts">
const items = ref(['Item 1', 'Item 2', 'Item 3']);
const nextId = ref(4);
const addItem = () => {
items.value.push(`Item ${nextId.value++}`);
};
const removeItem = () => {
items.value.pop();
};
</script>
<style scoped>
.list-enter-active,
.list-leave-active {
transition: all 0.5s;
}
.list-enter-from,
.list-leave-to {
opacity: 0;
transform: translateX(30px);
}
</style>
移动动画
上面的示例有一些明显的缺陷:当某一项被插入或移除时,它周围的元素会立即发生"跳跃"而不是平稳地移动。我们可以通过添加一些额外的 CSS 规则来解决这个问题
<template>
<button @click="addItem">在任意位置添加一项</button>
<button @click="removeItem">移除任意位置上的一项</button>
<button @click="shuffleItems">排序位置</button>
<TransitionGroup name="list" tag="div">
<div v-for="(item, index) in items" :key="item" :data-index="index">
{{ item }}
</div>
</TransitionGroup>
</template>
<script setup lang="ts">
const items = ref(['Item 1', 'Item 2', 'Item 3']);
const nextId = ref(4);
const addItem = () => {
items.value.push(`Item ${nextId.value++}`);
};
const removeItem = () => {
items.value.pop();
};
const shuffleItems = () => {
items.value = items.value.sort(() => Math.random() - 0.5);
};
</script>
<style scoped>
.list-group {
display: flex;
flex-direction: column;
gap: 10px;
}
.list-item {
padding: 10px;
background-color: #f0f0f0;
border-radius: 4px;
}
.list-enter-active,
.list-leave-active,
.list-move { /* 对移动中的元素应用的过渡 */
transition: all 0.5s;
}
.list-enter-from,
.list-leave-to {
opacity: 0;
transform: translateX(30px);
}
/* 确保将离开的元素从布局流中删除
以便能够正确地计算移动的动画。 */
.list-leave-active {
position: absolute;
}
</style>
自定义过渡组 class
你还可以通过向 传递 moveClass prop 为移动元素指定自定义过渡 class,类似于自定义过渡 class。
在 中,你可以通过以下属性来自定义过渡类:
-
enter-active-class:定义进入时的活动类。
-
leave-active-class:定义离开时的活动类。
-
move-class:定义移动时的类(用于列表项的重新排序动画)。
<template>
这些类允许你自定义进入、离开和移动时的动画效果。<button @click="addItem">Add Item</button> <button @click="removeItem">Remove Item</button> <TransitionGroup enter-active-class="custom-enter-active" leave-active-class="custom-leave-active" move-class="custom-move" tag="div" class="list-group" ></template> <script> import { ref } from 'vue';{{ item }}</TransitionGroup>export default {
setup() {
const items = ref(['Item 1', 'Item 2', 'Item 3']);
const nextId = ref(4);const addItem = () => { items.value.push(`Item ${nextId.value++}`); }; const removeItem = () => { items.value.pop(); }; return { items, addItem, removeItem }; },
};
<style> .list-group { display: flex; flex-direction: column; gap: 10px; }
</script>.list-item {
padding: 10px;
background-color: #f0f0f0;
border-radius: 4px;
}/* 自定义进入活动类 */
.custom-enter-active {
animation: fadeIn 0.5s ease-out;
}/* 自定义离开活动类 */
.custom-leave-active {
animation: fadeOut 0.5s ease-out;
}/* 自定义移动类 */
.custom-move {
transition: transform 0.5s ease;
}/* 自定义动画 */
@keyframes fadeIn {
from {
opacity: 0;
transform: translateX(-20px);
}
to {
opacity: 1;
transform: translateX(0);
}
}@keyframes fadeOut {
from {
opacity: 1;
transform: translateX(0);
}
to {
opacity: 0;
transform: translateX(20px);
}
}
</style>
渐进延迟列表动画
-
安装gsap库:
npm install gsap --save
-
在组件中中引入gsap库:
import gsap from 'gsap';
-
在组件中使用gsap库:
<script setup> <template> <TransitionGroup tag="ul" :css="false" @before-enter="onBeforeEnter" @enter="onEnter" @leave="onLeave" > - {{ item.msg }} </TransitionGroup> </template> <script setup lang="ts"> import { ref, computed ,unref} from 'vue' import gsap from 'gsap'; const list = ref([ { msg: 'Hello' }, { msg: 'World' }, { msg: 'Foo' }, { msg: 'Bar' }, { msg: 'Baz' }, ]) const query = ref('') const computedList = computed(() => { return list.value.filter((item) => item.msg.toLowerCase().includes(unref(query))) }) const onBeforeEnter = (el: any) => { el.style.opacity = 0 el.style.height = 0 } const onEnter=(el, done) => { gsap.to(el, { opacity: 1, height: '1.6em', delay: el.dataset.index * 0.15, onComplete: done }) } const onLeave =(el, done) =>{ gsap.to(el, { opacity: 0, height: 0, delay: el.dataset.index * 0.15, onComplete: done }) } </script>