Vue3 - Transition
tags: Vue
category: Front-End
description: Vue3 - Transition
created_at: 2022/07/17 15:00:00
回到 手把手開始寫 Vue3
前言
Vue
內建有提供兩個可以幫忙處理過渡(Transition
),又或者是說漸變的組件。
分別是
Transition
TransitionGroup
這個東西在 React
沒有內建,所以會需要額外安裝套件來實作,例如 headlessui
官方基本範例
<script setup>
import { ref } from "vue";
const show = ref(true);
</script>
<template>
<button @click="show = !show">Toggle</button>
<Transition>
<p v-if="show">hello</p>
</Transition>
</template>
<style scoped>
.v-enter-active,
.v-leave-active {
transition: opacity 0.5s ease;
}
.v-enter-from,
.v-leave-to {
opacity: 0;
}
</style>
可以看到當你點下 Toggle
這個按鈕,就會淡入淡出那個 p
標籤。
再來就是說明一下他是怎麼運作的,一樣上官方的圖
有以下這些 class
,稍微簡單描述: (我是覺得也可以想像成類似生命週期的概念XD)
v-enter-from
: 元素進來的初始狀態。v-enter-active
: 在進入的過程中。v-enter-to
: 元素進入的結束狀態。v-leave-from
: 元素離開的初始狀態。v-leave-active
: 在離開的過程中。v-leave-to
: 元素離開的最後狀態。
上範例:
<script setup>
import { ref } from "vue";
const show = ref(true);
</script>
<template>
<button @click="show = !show">Toggle</button>
<Transition>
<p v-if="show">hello</p>
</Transition>
</template>
<style scoped>
.v-enter-from {
color: red;
}
.v-enter-active {
transition: 3s;
}
.v-enter-to {
color: blue;
}
.v-leave-from {
color: green;
}
.v-leave-active {
transition: 5s;
}
.v-leave-to {
color: yellow;
}
</style>
這樣設定的話,那個 p
標籤會有這樣的行為:
- 出現會以3秒的時間從紅色變為藍色
- 消失會用5秒鐘,從綠色變回黃色
- 當沒有要漸變時,就會是原來的黑色
也可以改 class
前綴,只要為 transition
加上 name
<script setup>
import { ref } from "vue";
const show = ref(true);
</script>
<template>
<button @click="show = !show">Toggle</button>
<Transition name="hi">
<p v-if="show">hello</p>
</Transition>
</template>
<style scoped>
.hi-enter-from {
color: red;
}
.hi-enter-active {
transition: 3s;
}
.hi-enter-to {
color: blue;
}
.hi-leave-from {
color: green;
}
.hi-leave-active {
transition: 5s;
}
.hi-leave-to {
color: yellow;
}
</style>
加上自訂動畫
<script setup>
import { ref } from "vue";
const show = ref(true);
</script>
<template>
<button @click="show = !show">Toggle</button>
<Transition>
<p v-if="show" class="text-center">hello</p>
</Transition>
</template>
<style scope>
.v-enter-active {
animation: bounce-in 0.5s;
}
.v-leave-active {
animation: bounce-in 0.5s reverse;
}
@keyframes bounce-in {
0% {
transform: scale(0);
}
50% {
transform: scale(1.25);
}
100% {
transform: scale(1);
}
}
</style>
這樣子他在出現就會從 scale(0)
開始,而消失則會從 scale(1)
開始。
不過上面這一堆都是還要自己去額外寫對應 class
做操作,既然今天套上了 tailwindcss
應該會盡量少寫額外的 class
,除非你有需要。
所以他其實還可以用別的方式操作,直接讓他在對應的時期幫你套上你要的 class
<script setup>
import { ref } from "vue";
const show = ref(true);
</script>
<template>
<button @click="show = !show">Toggle</button>
<Transition
enter-from-class="text-red-500"
enter-active-class="duration-[3s]"
enter-to-class="text-blue-500"
leave-from-class="text-green-500"
leave-active-class="duration-[5s]"
leave-to-class="text-yellow-500"
>
<p v-if="show">hello</p>
</Transition>
</template>
最後他還有提供 hook
可以用,一樣貼官方範例來說明
<script setup>
import { ref } from "vue";
const show = ref(true);
// 進入初始狀態
const onBeforeEnter = (el) => {
console.log("onBeforeEnter", el);
};
// 進入過程
const onEnter = (el, done) => {
console.log("onEnter", (el, done));
// 結束之後呼叫 done
done();
};
// 在進入結束之後
const onAfterEnter = (el) => {
console.log("onAfterEnter", el);
};
// 過程被打斷,只適用 v-show
const onEnterCancelled = (el) => {
console.log("onEnterCancelled", el);
};
// 離開之前,也就是初始狀態
const onBeforeLeave = (el) => {
console.log("onBeforeLeave", el);
};
// 離開過程中
const onLeave = (el, done) => {
console.log("onLeave", (el, done));
// 離開OK一樣記得呼叫 done
done();
};
// 離開之後
const onAfterLeave = (el) => {
console.log("onAfterLeave", el);
};
// 一樣也只適用於 v-show
const onLeaveCancelled = (el) => {
console.log("onLeaveCancelled", el);
};
</script>
<template>
<button @click="show = !show">Toggle</button>
<Transition
@before-enter="onBeforeEnter"
@enter="onEnter"
@after-enter="onAfterEnter"
@enter-cancelled="onEnterCancelled"
@before-leave="onBeforeLeave"
@leave="onLeave"
@after-leave="onAfterLeave"
@leave-cancelled="onLeaveCancelled"
>
<p v-if="show">hello</p>
</Transition>
</template>
上面這段可以稍微戳一戳玩一玩,然後有 hook
也還是可以跟原來的方法混用,像是下面這樣
<Transition
@before-enter="onBeforeEnter"
@enter="onEnter"
@after-enter="onAfterEnter"
@enter-cancelled="onEnterCancelled"
@before-leave="onBeforeLeave"
@leave="onLeave"
@after-leave="onAfterLeave"
@leave-cancelled="onLeaveCancelled"
enter-from-class="text-red-500"
enter-active-class="duration-[3s]"
enter-to-class="text-blue-500"
leave-from-class="text-green-500"
leave-active-class="duration-[5s]"
leave-to-class="text-yellow-500"
>
但是要注意的是,當 @enter
或是 @leave
呼叫 done()
之後就結束了,反之不呼叫就不會結束,這邊可以自己玩看看。
另外有時候你可能只想要 hook
不想要讓他幫你帶 class
避免其他錯誤,可以把它關掉
<Transition
...
:css="false"
>
...
</Transition>
還有初始的進入狀態,可以加上 appear
<template>
<button @click="show = !show">Toggle</button>
<Transition
appear
enter-from-class="text-red-500"
enter-active-class="duration-[3s]"
enter-to-class="text-blue-500"
leave-from-class="text-green-500"
leave-active-class="duration-[5s]"
leave-to-class="text-yellow-500"
>
<p v-if="show">hello</p>
</Transition>
</template>
這樣的話畫面剛進去就會做 enter
相關的漸變。
最後再提一個,動畫的順序,在一般情況下是同時做
例如隱藏舊的並加入新的,一般情況是同時做,所以舊的會跑 leave
,而新的會跑 enter
,例如下面這段
<script setup>
import { ref } from "vue";
const count = ref(0);
</script>
<template>
<button @click="count++">Add</button>
<Transition
enter-from-class="text-red-500"
enter-active-class="duration-[3s]"
enter-to-class="text-blue-500"
leave-from-class="text-green-500"
leave-active-class="duration-[5s]"
leave-to-class="text-yellow-500"
>
<p v-if="count % 2 === 0">Count is even.</p>
<p v-else>Count is odd.</p>
</Transition>
</template>
如果要做到讓其中一邊先做,就要設定 mode
<Transition
mode="out-in"
...
>
...
</Transition>
有兩個參數可以丟:
out-in
: 先做leave
,後做enter
。in-out
: 先做enter
,後做leave
。
上面感覺交代得有點混,就放一個範例當作結尾吧,假設要做這樣的動畫
寫起來大概會長得像這樣子
<script setup>
import { ref } from "vue";
const count = ref(0);
</script>
<template>
<button @click="count++">Add</button>
<Transition
mode="out-in"
enter-from-class="translate-y-8 opacity-0"
enter-active-class="duration-200"
enter-to-class="translate-y-0 opacity-100"
leave-from-class="translate-y-0 opacity-100"
leave-active-class="duration-200"
leave-to-class="-translate-y-8 opacity-0"
>
<p v-if="count % 2 === 0">Count is even.</p>
<p v-else>Count is odd.</p>
</Transition>
</template>
寫起來的意思大概是,進入時從下往上,離開時從原位往更上面。透明度也跟著變動。
如果是有多個元素要做過渡,例如使用 v-for
產生的列表,需要使用 TransitionGroup,因為基本上跟 Transition
沒有差多少,篇幅也有點多了,就不特別寫了。