基礎:過渡動畫
Vue 在插入、更新或者移除 DOM 時,提供多種不同方式的應用過渡效果。包括以下工具:
- 在 CSS 過渡和動畫中自動應用 class
- 可以配合使用第三方 CSS 動畫庫,如 Animate.css
- 在過渡鉤子函數中使用 JavaScript 直接操作 DOM
- 可以配合使用第三方 JavaScript 動畫庫,如 Velocity.js
11.1 元素·組件過渡
Vue 提供了 transition
的封裝組件,可以給任何元素和組件添加進入/離開過渡。
- 條件渲染 (使用
v-if
) - 條件展示 (使用
v-show
) - 動態組件
- 組件根節點
我們再使用過渡動畫編碼時主要有如下幾步:
- 在需要添加過渡動畫的目標元素外部定義過渡動畫(
transition
) - 定義目標元素過渡動畫相關的樣式
- 過渡樣式
- 隱藏樣式
當我們在HTML中插入或刪除包含在 transition
組件中的元素時,Vue 將會做以下處理:
- 自動嗅探目標元素是否應用了 CSS 過渡或動畫,如果是,在恰當的時機添加/刪除 CSS 類名。
- 如果過渡組件提供了JavaScript 鉤子函數,這些鉤子函數將在恰當的時機被調用。
- 如果沒有找到 JavaScript 鉤子並且也沒有檢測到 CSS 過渡/動畫,DOM 操作 (插入/刪除) 在下一幀中立即執行。(注意:此指瀏覽器逐幀動畫機制,和 Vue 的
nextTick
概念不同)
我們看一個完整示例:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Vue之過渡動畫</title>
<style>
.fade-enter-active, .fade-leave-active {
transition: opacity 2s;
}
.fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */ {
opacity: 0;
}
</style>
</head>
<body>
<div id="app">
<button v-on:click="isShow = !isShow">切換</button>
<transition name="fade">
<p v-if="isShow">活着就是勝利,掙錢只是遊戲,健康纔是目的,快樂纔是真諦。</p>
</transition>
</div>
</body>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script type="text/javascript">
const vm = new Vue({
el : "#app",
data: {
isShow: true
}
})
</script>
</html>
11.2 過渡類名
在進入/離開的過渡中,會有 6 個class
切換。
v-enter
:定義進入過渡的開始狀態。在元素被插入之前生效,在元素被插入之後的下一幀移除。v-enter-active
:定義進入過渡生效時的狀態。在整個進入過渡的階段中應用,在元素被插入之前生效,在過渡/動畫完成之後移除。這個類可以被用來定義進入過渡的過程時間,延遲和曲線函數。v-enter-to
:在元素被插入之後下一幀生效 (與此同時v-enter
被移除),在過渡/動畫完成之後移除。v-leave
:定義離開過渡的開始狀態。在離開過渡被觸發時立刻生效,下一幀被移除。v-leave-active
:定義離開過渡生效時的狀態。在整個離開過渡的階段中應用,在離開過渡被觸發時立刻生效,在過渡/動畫完成之後移除。這個類可以被用來定義離開過渡的過程時間,延遲和曲線函數。v-leave-to
:離開過渡的結束狀態。在離開過渡被觸發之後下一幀生效 (與此同時v-leave
被刪除),在過渡/動畫完成之後移除。
對於這些在過渡中切換的類名來說,如果你使用一個沒有名字的 ,則 `v-` 是這些類名的默認前綴。如果你使用了
,那麼 v-enter
會替換爲 my-transition-enter
。
v-enter-active
和 v-leave-active
可以控制進入/離開過渡的不同的緩和曲線。
CSS動畫用法同CSS 過渡,區別是在動畫中 v-enter
類名在節點插入 DOM 後不會立即刪除,而是在animationend
事件觸發時刪除。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Vue之過渡動畫</title>
<style>
.fade-enter-active, .fade-leave-active {
transition: opacity 2s;
}
.fade-enter, .fade-leave-to {
opacity: 0;
}
/* 設置不同的進入和離開動畫, 側面橫向切換效果 */
/* 設置持續時間和動畫函數 */
.slide-fade-enter-active {
transition: all .3s ease;
}
.slide-fade-leave-active {
transition: all .8s cubic-bezier(1.0, 0.5, 0.8, 1.0);
}
.slide-fade-enter, .slide-fade-leave-to {
transform: translateX(10px);
opacity: 0;
}
/* 設置彈跳動畫切換 */
.bounce-enter-active {
animation: bounce-in .5s;
}
.bounce-leave-active {
animation: bounce-in .5s reverse;
}
@keyframes bounce-in {
0% {
transform: scale(0);
}
50% {
transform: scale(1.5);
}
100% {
transform: scale(1);
}
}
</style>
</head>
<body>
<div id="app1">
<button v-on:click="isShow = !isShow">切換</button>
<transition name="fade">
<p v-if="isShow">直接切換:活着就是勝利,掙錢只是遊戲。</p>
</transition>
</div>
<div id="app2">
<button v-on:click="isShow = !isShow">切換</button>
<transition name="slide-fade">
<p v-if="isShow">橫向切換:健康纔是目的,快樂纔是真諦。</p>
</transition>
</div>
<div id="app3">
<button v-on:click="isShow = !isShow">切換</button>
<transition name="bounce">
<p v-if="isShow">動畫切換:寥落古行宮,宮花寂寞紅。白頭宮女在,閒坐說玄宗。</p>
</transition>
</div>
</body>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script type="text/javascript">
const vm1 = new Vue({
el : "#app1",
data: {
isShow: true
}
})
const vm2 = new Vue({
el : "#app2",
data: {
isShow: true
}
})
const vm3 = new Vue({
el : "#app3",
data: {
isShow: true
}
})
</script>
</html>
※自定義過渡類名:我們可以通過以下attribute
來自定義過渡類名:
enter-class
enter-active-class
enter-to-class
(2.1.8+)leave-class
leave-active-class
leave-to-class
(2.1.8+)
他們的優先級高於普通的類名,這對於 Vue
的過渡系統和其他第三方 CSS 動畫庫(Animate.css
)結合使用十分有用。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Vue之過渡動畫</title>
<link href="https://cdn.jsdelivr.net/npm/[email protected]" rel="stylesheet" type="text/css">
</head>
<body>
<div id="app4">
<button v-on:click="isShow = !isShow">自定義切換</button>
<transition name="custom" enter-active-class="animated tada" leave-active-class="animated bounceOutRight">
<p v-if="isShow">君自故鄉來,應知故鄉事。來日綺窗前,寒梅著花未?</p>
</transition>
</div>
</body>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script type="text/javascript">
const vm4 = new Vue({
el : "#app4",
data: {
isShow: true
}
})
</script>
</html>
-
同時使用過渡和動畫
- Vue 爲了知道過渡的完成,必須設置相應的事件監聽器。它可以是
transitionend
或animationend
,這取決於給元素應用的 CSS 規則。如果你使用其中任何一種,Vue 能自動識別類型並設置監聽。如果需要給同一個元素同時設置兩種過渡動效,比如animation
很快的被觸發並完成了,而transition
效果還沒結束。在這種情況中,你就需要使用type
attribute 並設置animation
或transition
來明確聲明你需要 Vue 監聽的類型。
- Vue 爲了知道過渡的完成,必須設置相應的事件監聽器。它可以是
-
顯性過渡持續時間
-
Vue可以自動得出過渡效果的完成時機。默認情況下,Vue 會等待其在過渡效果的根元素的第一個
transitionend
或animationend
事件。但是我們可以擁有一個屬於自己業務定製的一系列過渡效果,其中一些嵌套的內部元素相比於過渡效果的根元素有延遲的或更長的過渡效果。此時可以用組件上的duration
屬性定製一個顯性的過渡持續時間 (以毫秒計),也可以定製進入和移出的持續時間。<transition :duration="1000">...</transition> <transition :duration="{ enter: 500, leave: 800 }">...</transition>
-
11.3 過渡擴展
11.3.1 初始渲染的過渡
可以通過 appear
屬性設置節點在初始渲染的過渡,在下面的例子中,無論是 appear
屬性還是 v-on:appear
鉤子都會生成初始渲染過渡。
這裏默認和進入/離開過渡一樣,同樣也可以自定義 CSS 類名。
<transition
appear
appear-class="custom-appear-class"
appear-to-class="custom-appear-to-class" (2.1.8+)
appear-active-class="custom-appear-active-class"
>
<!-- ... -->
</transition>
自定義 JavaScript 鉤子:
<transition
appear
v-on:before-appear="customBeforeAppearHook"
v-on:appear="customAppearHook"
v-on:after-appear="customAfterAppearHook"
v-on:appear-cancelled="customAppearCancelledHook"
>
<!-- ... -->
</transition>
11.3.2 多個元素過渡
對於原生標籤可以使用 v-if
/v-else
。最常見的多標籤過渡是一個列表和描述這個列表爲空消息的元素。
<transition>
<table v-if="items.length > 0">
<!-- ... -->
</table>
<p v-else>Sorry, no items found.</p>
</transition>
可以這樣使用,但是有一點需要注意:
當有相同標籤名的元素切換時,需要通過 key
屬性設置唯一的值來標記以讓 Vue
區分它們,否則 Vue
爲了效率只會替換相同標籤內部的內容。即使在技術上沒有必要,給在組件中的多個元素設置 key 是一個更好的實踐。
示例:
<transition>
<button v-if="isEditing" key="save">
Save
</button>
<button v-else key="edit">
Edit
</button>
</transition>
在一些場景中,也可以通過給同一個元素的 key
設置不同的狀態來代替 v-if
和 v-else
,上面的例子可以重寫爲:
<transition>
<button v-bind:key="isEditing">
{{ isEditing ? 'Save' : 'Edit' }}
</button>
</transition>
使用多個 v-if
的多個元素的過渡可以重寫爲綁定了動態屬性的單個元素過渡。例如:
<transition>
<button v-if="docState === 'saved'" key="saved">
Edit
</button>
<button v-if="docState === 'edited'" key="edited">
Save
</button>
<button v-if="docState === 'editing'" key="editing">
Cancel
</button>
</transition>
可以重寫爲:
<transition>
<button v-bind:key="docState">
{{ buttonMessage }}
</button>
</transition>
// ...
computed: {
buttonMessage: function () {
switch (this.docState) {
case 'saved': return 'Edit'
case 'edited': return 'Save'
case 'editing': return 'Cancel'
}
}
}
11.3.3 過渡模式
同時生效的進入和離開的過渡不能滿足業務上的所有要求,所以 Vue 提供了過渡模式:
in-out
:新元素先進行過渡,完成之後當前元素過渡離開。out-in
:當前元素先進行過渡,完成之後新元素過渡進入。
用 out-in
重寫之前的開關按鈕過渡:
<transition name="fade" mode="out-in">
<!-- ... the buttons ... -->
</transition>
只用添加一個簡單的 attribute,就解決了之前的過渡問題而無需任何額外的代碼。in-out
模式不是經常用到,但對於一些稍微不同的過渡效果還是有用的。
11.3.4 多個組件的過渡
我們直接使用動態組件來實現多個組件的過渡。
<transition name="component-fade" mode="out-in">
<component v-bind:is="view"></component>
</transition>
<script type="text/javascript">
new Vue({
el: '#transition-components-demo',
data: {
view: 'v-a'
},
components: {
'v-a': {
template: '<div>Component A</div>'
},
'v-b': {
template: '<div>Component B</div>'
}
}
})
</script>
<style>
.component-fade-enter-active, .component-fade-leave-active {
transition: opacity .3s ease;
}
.component-fade-enter, .component-fade-leave-to
/* .component-fade-leave-active for below version 2.1.8 */ {
opacity: 0;
}
</style>