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><div><button @click="addItem">Add Item</button><button @click="removeItem">Remove Item</button><TransitionGroupenter-active-class="custom-enter-active"leave-active-class="custom-leave-active"move-class="custom-move"tag="div"class="list-group"><divv-for="(item, index) in items":key="item":data-index="index"class="list-item">{{ item }}</div></TransitionGroup></div>
</template><script>
import { ref } from 'vue';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 };},
};
</script><style>
.list-group {display: flex;flex-direction: column;gap: 10px;
}.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><input v-model="query" /><TransitionGrouptag="ul":css="false"@before-enter="onBeforeEnter"@enter="onEnter"@leave="onLeave"><liv-for="(item, index) in computedList":key="item.msg":data-index="index">{{ item.msg }}</li></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 = 0el.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>