Vue.js 過渡 & 動畫

本章節我們主要討論 Vue.js 的過渡效果與動畫效果。


過渡

Vue 在插入、更新或者移除 DOM 時,提供多種不同方式的應用過渡效果。

Vue 提供了內建的過渡封裝元件,該元件用於包裹要實現過渡效果的元件。

語法格式

<transition name = "nameoftransition">
   <div></div>
</transition>

我們可以通過以下例項來理解 Vue 的過渡是如何實現的:

例項

<div id = "databinding"> <button v-on:click = "show = !show">點我</button> <transition name = "fade"> <p v-show = "show" v-bind:style = "styleobj">動畫例項</p> </transition> </div> <script type = "text/javascript"> var vm = new Vue({ el: '#databinding', data: { show:true, styleobj :{ fontSize:'30px', color:'red' } }, methods : { } }); </script>

例項中通過點選 "點我" 按鈕將變數 show 的值從 true 變為 false。如果為 true 顯示子元素 p 標籤的內容。

下面這段程式碼展示了 transition 標籤包裹了 p 標籤:

<transition name = "fade">
    <p v-show = "show" v-bind:style = "styleobj">動畫例項</p>
</transition>

過渡其實就是一個淡入淡出的效果。Vue在元素顯示與隱藏的過渡中,提供了 6 個 class 來切換:

  • v-enter:定義進入過渡的開始狀態。在元素被插入之前生效,在元素被插入之後的下一幀移除。

  • v-enter-active:定義進入過渡生效時的狀態。在整個進入過渡的階段中應用,在元素被插入之前生效,在過渡/動畫完成之後移除。這個類可以被用來定義進入過渡的過程時間,延遲和曲線函式。

  • v-enter-to: 2.1.8版及以上 定義進入過渡的結束狀態。在元素被插入之後下一幀生效 (與此同時 v-enter 被移除),在過渡/動畫完成之後移除。

  • v-leave: 定義離開過渡的開始狀態。在離開過渡被觸發時立刻生效,下一幀被移除。

  • v-leave-active:定義離開過渡生效時的狀態。在整個離開過渡的階段中應用,在離開過渡被觸發時立刻生效,在過渡/動畫完成之後移除。這個類可以被用來定義離開過渡的過程時間,延遲和曲線函式。

  • v-leave-to: 2.1.8版及以上 定義離開過渡的結束狀態。在離開過渡被觸發之後下一幀生效 (與此同時 v-leave 被刪除),在過渡/動畫完成之後移除。

對於這些在過渡中切換的類名來說,如果你使用一個沒有名字的 <transition>,則 v- 是這些類名的預設字首。如果你使用了 <transition name="my-transition">,那麼 v-enter 會替換為 my-transition-enter

v-enter-activev-leave-active 可以控制進入/離開過渡的不同的緩和曲線,在下面章節會有個示例說明。

CSS 過渡

通常我們都使用 CSS 過渡來實現效果。

如下例項:

例項

<div id = "databinding"> <button v-on:click = "show = !show">點我</button> <transition name="slide-fade"> <p v-if="show">hello</p> </transition> </div> <script type = "text/javascript"> new Vue({ el: '#databinding', data: { show: true } }) </script>

CSS 動畫

CSS 動畫用法類似 CSS 過渡,但是在動畫中 v-enter 類名在節點插入 DOM 後不會立即刪除,而是在 animationend 事件觸發時刪除。

例項

<div id = "databinding"> <button v-on:click = "show = !show">點我</button> <transition name="bounce"> <p v-if="show">入門教學 -- 學的不僅是技術,更是夢想!!!</p> </transition> </div> <script type = "text/javascript"> new Vue({ el: '#databinding', data: { show: true } }) </script>

自定義過渡的類名

我們可以通過以下特性來自定義過渡類名:

  • enter-class
  • enter-active-class
  • enter-to-class (2.1.8+)
  • leave-class
  • leave-active-class
  • leave-to-class (2.1.8+)

自定義過渡的類名優先順序高於普通的類名,這樣就能很好的與第三方(如:animate.css)的動畫庫結合使用。

例項

<div id = "databinding"> <button v-on:click = "show = !show">點我</button> <transition name="custom-classes-transition" enter-active-class="animated tada" leave-active-class="animated bounceOutRight" > <p v-if="show">入門教學 -- 學的不僅是技術,更是夢想!!!</p> </transition> </div> <script type = "text/javascript"> new Vue({ el: '#databinding', data: { show: true } }) </script>

同時使用過渡和動畫

Vue 為了知道過渡的完成,必須設定相應的事件監聽器。它可以是 transitionendanimationend ,這取決於給元素應用的 CSS 規則。如果你使用其中任何一種,Vue 能自動識別型別並設定監聽。

但是,在一些場景中,你需要給同一個元素同時設定兩種過渡動效,比如 animation 很快的被觸發並完成了,而 transition 效果還沒結束。在這種情況中,你就需要使用 type 特性並設定 animationtransition 來明確宣告你需要 Vue 監聽的型別。

顯性的過渡持續時間

在很多情況下,Vue 可以自動得出過渡效果的完成時機。預設情況下,Vue 會等待其在過渡效果的根元素的第一個 transitionendanimationend 事件。然而也可以不這樣設定——比如,我們可以擁有一個精心編排的一系列過渡效果,其中一些巢狀的內部元素相比於過渡效果的根元素有延遲的或更長的過渡效果。

在這種情況下你可以用 <transition> 元件上的 duration 屬性定製一個顯性的過渡持續時間 (以毫秒計):

<transition :duration="1000">...</transition>

你也可以定製進入和移出的持續時間:

<transition :duration="{ enter: 500, leave: 800 }">...</transition>

JavaScript 鉤子

可以在屬性中宣告 JavaScript 鉤子:

HTML 程式碼:

<transition v-on:before-enter="beforeEnter" v-on:enter="enter" v-on:after-enter="afterEnter" v-on:enter-cancelled="enterCancelled" v-on:before-leave="beforeLeave" v-on:leave="leave" v-on:after-leave="afterLeave" v-on:leave-cancelled="leaveCancelled" > <!-- ... --> </transition>

JavaScript 程式碼:

// ... methods: { // -------- // 進入中 // -------- beforeEnter: function (el) { // ... }, // 此回撥函式是可選項的設定 // 與 CSS 結合時使用 enter: function (el, done) { // ... done() }, afterEnter: function (el) { // ... }, enterCancelled: function (el) { // ... }, // -------- // 離開時 // -------- beforeLeave: function (el) { // ... }, // 此回撥函式是可選項的設定 // 與 CSS 結合時使用 leave: function (el, done) { // ... done() }, afterLeave: function (el) { // ... }, // leaveCancelled 只用於 v-show 中 leaveCancelled: function (el) { // ... } }

這些鉤子函式可以結合 CSS transitions/animations 使用,也可以單獨使用。

當只用 JavaScript 過渡的時候,enterleave 中必須使用 done 進行回撥。否則,它們將被同步呼叫,過渡會立即完成。

推薦對於僅使用 JavaScript 過渡的元素新增 v-bind:css="false",Vue 會跳過 CSS 的檢測。這也可以避免過渡過程中 CSS 的影響。

一個使用 Velocity.js 的簡單例子:

例項

<div id = "databinding"> <button v-on:click = "show = !show">點我</button> <transition v-on:before-enter="beforeEnter" v-on:enter="enter" v-on:leave="leave" v-bind:css="false" > <p v-if="show">入門教學 -- 學的不僅是技術,更是夢想!!!</p> </transition> </div> <script type = "text/javascript"> new Vue({ el: '#databinding', data: { show: false }, methods: { beforeEnter: function (el) { el.style.opacity = 0 el.style.transformOrigin = 'left' }, enter: function (el, done) { Velocity(el, { opacity: 1, fontSize: '1.4em' }, { duration: 300 }) Velocity(el, { fontSize: '1em' }, { complete: done }) }, leave: function (el, done) { Velocity(el, { translateX: '15px', rotateZ: '50deg' }, { duration: 600 }) Velocity(el, { rotateZ: '100deg' }, { loop: 2 }) Velocity(el, { rotateZ: '45deg', translateY: '30px', translateX: '30px', opacity: 0 }, { complete: done }) } } }) </script>


初始渲染的過渡

可以通過 appear 特性設定節點在初始渲染的過渡

<transition appear>
  <!-- ... -->
</transition>

這裡預設和進入/離開過渡一樣,同樣也可以自定義 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>

多個元素的過渡

我們可以設定多個元素的過渡,一般列表與描述:

需要注意的是當有相同標籤名的元素切換時,需要通過 key 特性設定唯一的值來標記以讓 Vue 區分它們,否則 Vue 為了效率只會替換相同標籤內部的內容。

<transition>
  <table v-if="items.length > 0">
    <!-- ... -->
  </table>
  <p v-else>抱歉,沒有找到您查詢的內容。</p>
</transition>

如下例項:

<transition>
  <button v-if="isEditing" key="save">
    Save
  </button>
  <button v-else key="edit">
    Edit
  </button>
</transition>

在一些場景中,也可以通過給同一個元素的 key 特性設定不同的狀態來代替 v-ifv-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'
    }
  }
}