鍍金池/ 教程/ HTML/ 過(guò)渡
自定義過(guò)濾器
自定義指令
概述
安裝
起步
深入響應(yīng)式原理
Class 與 Style 綁定
混合
列表渲染
方法與事件處理器
表單控件綁定
對(duì)比其它框架
組件
計(jì)算屬性
插件
數(shù)據(jù)綁定語(yǔ)法
構(gòu)建大型應(yīng)用
條件渲染
Vue 實(shí)例
過(guò)渡

過(guò)渡

通過(guò) Vue.js 的過(guò)渡系統(tǒng),可以在元素從 DOM 中插入或移除時(shí)自動(dòng)應(yīng)用過(guò)渡效果。Vue.js 會(huì)在適當(dāng)?shù)臅r(shí)機(jī)為你觸發(fā) CSS 過(guò)渡或動(dòng)畫(huà),你也可以提供相應(yīng)的 JavaScript 鉤子函數(shù)在過(guò)渡過(guò)程中執(zhí)行自定義的 DOM 操作。

為了應(yīng)用過(guò)渡效果,需要在目標(biāo)元素上使用 transition 特性:

<div v-if="show" transition="my-transition"></div>

transition 特性可以與下面資源一起用:

  • v-if
  • v-show
  • v-for (只為插入和刪除觸發(fā))
  • 動(dòng)態(tài)組件 (介紹見(jiàn)組件)
  • 在組件的根節(jié)點(diǎn)上,并且被 Vue 實(shí)例 DOM 方法(如 vm.$appendTo(el))觸發(fā)。

當(dāng)插入或刪除帶有過(guò)渡的元素時(shí),Vue 將:

  1. 嘗試以 ID "my-transition" 查找 JavaScript 過(guò)渡鉤子對(duì)象——通過(guò) Vue.transition(id, hooks)transitions 選項(xiàng)注冊(cè)。如果找到了,將在過(guò)渡的不同階段調(diào)用相應(yīng)的鉤子。

  2. 自動(dòng)嗅探目標(biāo)元素是否有 CSS 過(guò)渡或動(dòng)畫(huà),并在合適時(shí)添加/刪除 CSS 類(lèi)名。

  3. 如果沒(méi)有找到 JavaScript 鉤子并且也沒(méi)有檢測(cè)到 CSS 過(guò)渡/動(dòng)畫(huà),DOM 操作(插入/刪除)在下一幀中立即執(zhí)行。

CSS 過(guò)渡

示例

典型的 CSS 過(guò)渡像這樣:

<div v-if="show" transition="expand">hello</div>

然后為 .expand-transition, .expand-enter.expand-leave 添加 CSS 規(guī)則:

/* 必需 */
.expand-transition {
  transition: all .3s ease;
  height: 30px;
  padding: 10px;
  background-color: #eee;
  overflow: hidden;
}

/* .expand-enter 定義進(jìn)入的開(kāi)始狀態(tài) */
/* .expand-leave 定義離開(kāi)的結(jié)束狀態(tài) */
.expand-enter, .expand-leave {
  height: 0;
  padding: 0 10px;
  opacity: 0;
}

另外,可以提供 JavaScript 鉤子:

Vue.transition('expand', {

  beforeEnter: function (el) {
    el.textContent = 'beforeEnter'
  },
  enter: function (el) {
    el.textContent = 'enter'
  },
  afterEnter: function (el) {
    el.textContent = 'afterEnter'
  },
  enterCancelled: function (el) {
    // handle cancellation
  },

  beforeLeave: function (el) {
    el.textContent = 'beforeLeave'
  },
  leave: function (el) {
    el.textContent = 'leave'
  },
  afterLeave: function (el) {
    el.textContent = 'afterLeave'
  },
  leaveCancelled: function (el) {
    // handle cancellation
  }
})
{% raw %}
<div id="demo">
  <div v-if="show" transition="expand">hello</div>
  <button @click="show = !show">Toggle</button>
</div>

<style>
.expand-transition {
  transition: all .3s ease;
  height: 30px;
  padding: 10px;
  background-color: #eee;
  overflow: hidden;
}
.expand-enter, .expand-leave {
  height: 0;
  padding: 0 10px;
  opacity: 0;
}
</style>

<script>
new Vue({
  el: '#demo',
  data: {
    show: true,
    transitionState: 'Idle'
  },
  transitions: {
    expand: {
      beforeEnter: function (el) {
        el.textContent = 'beforeEnter'
      },
      enter: function (el) {
        el.textContent = 'enter'
      },
      afterEnter: function (el) {
        el.textContent = 'afterEnter'
      },
      beforeLeave: function (el) {
        el.textContent = 'beforeLeave'
      },
      leave: function (el) {
        el.textContent = 'leave'
      },
      afterLeave: function (el) {
        el.textContent = 'afterLeave'
      }
    }
  }
})
</script>
{% endraw %}

過(guò)渡的 CSS 類(lèi)名

類(lèi)名的添加和切換取決于 transition 特性的值。比如 transition="fade",會(huì)有三個(gè) CSS 類(lèi)名:

  1. .fade-transition 始終保留在元素上。

  2. .fade-enter 定義進(jìn)入過(guò)渡的開(kāi)始狀態(tài)。只應(yīng)用一幀然后立即刪除。

  3. .fade-leave 定義離開(kāi)過(guò)渡的結(jié)束狀態(tài)。在離開(kāi)過(guò)渡開(kāi)始時(shí)生效,在它結(jié)束后刪除。

如果 transition 特性沒(méi)有值,類(lèi)名默認(rèn)是 .v-transition, .v-enter.v-leave

自定義過(guò)渡類(lèi)名

1.0.14 新增

我們可以在過(guò)渡的 JavaScript 定義中聲明自定義的 CSS 過(guò)渡類(lèi)名。這些自定義類(lèi)名會(huì)覆蓋默認(rèn)的類(lèi)名。當(dāng)需要和第三方的 CSS 動(dòng)畫(huà)庫(kù),比如 Animate.css 配合時(shí)會(huì)非常有用:

<div v-show="ok" class="animated" transition="bounce">Watch me bounce</div>
Vue.transition('bounce', {
  enterClass: 'bounceInLeft',
  leaveClass: 'bounceOutRight'
})

顯式聲明 CSS 過(guò)渡類(lèi)型

1.0.14 新增

Vue.js 需要給過(guò)渡元素添加事件偵聽(tīng)器來(lái)偵聽(tīng)過(guò)渡何時(shí)結(jié)束?;谒褂玫?CSS,該事件要么是 transitionend,要么是 animationend。如果你只使用了兩者中的一種,那么 Vue.js 將能夠根據(jù)生效的 CSS 規(guī)則自動(dòng)推測(cè)出對(duì)應(yīng)的事件類(lèi)型。但是,有些情況下一個(gè)元素可能需要同時(shí)帶有兩種類(lèi)型的動(dòng)畫(huà)。比如你可能希望讓 Vue 來(lái)觸發(fā)一個(gè) CSS animation,同時(shí)該元素在鼠標(biāo)懸浮時(shí)又有 CSS transition 效果。這樣的情況下,你需要顯式地聲明你希望 Vue 處理的動(dòng)畫(huà)類(lèi)型 (animation 或是 transition):

Vue.transition('bounce', {
  // 該過(guò)渡效果將只偵聽(tīng) `animationend` 事件
  type: 'animation'
})

過(guò)渡流程詳解

當(dāng) show 屬性改變時(shí),Vue.js 將相應(yīng)地插入或刪除 <div> 元素,按照如下規(guī)則改變過(guò)渡的 CSS 類(lèi)名:

  • 如果 show 變?yōu)?false,Vue.js 將:

    1. 調(diào)用 beforeLeave 鉤子;
    2. 添加 v-leave 類(lèi)名到元素上以觸發(fā)過(guò)渡;
    3. 調(diào)用 leave 鉤子;
    4. 等待過(guò)渡結(jié)束(監(jiān)聽(tīng) transitionend 事件);
    5. 從 DOM 中刪除元素并刪除 v-leave 類(lèi)名;
    6. 調(diào)用 afterLeave 鉤子。
  • 如果 show 變?yōu)?true,Vue.js 將:
    1. 調(diào)用 beforeEnter 鉤子;
    2. 添加 v-enter 類(lèi)名到元素上;
    3. 把它插入 DOM;
    4. 調(diào)用 enter 鉤子;
    5. 強(qiáng)制一次 CSS 布局,讓 v-enter 確實(shí)生效。然后刪除 v-enter 類(lèi)名,以觸發(fā)過(guò)渡,回到元素的原始狀態(tài);
    6. 等待過(guò)渡結(jié)束;
    7. 調(diào)用 afterEnter 鉤子。

另外,如果在它的進(jìn)入過(guò)渡還在進(jìn)行中時(shí)刪除元素,將調(diào)用 enterCancelled 鉤子,以清理變動(dòng)或 enter 創(chuàng)建的計(jì)時(shí)器。反過(guò)來(lái)對(duì)于離開(kāi)過(guò)渡亦如是。

上面所有的鉤子函數(shù)在調(diào)用時(shí),它們的 this 均指向所屬的 Vue 實(shí)例。如果元素是 Vue 實(shí)例的根節(jié)點(diǎn),則這個(gè)實(shí)例是上下文。否則,上下文是過(guò)渡指令所屬的實(shí)例。

最后,enterleave 可以有第二個(gè)可選的回調(diào)參數(shù),用于顯式控制過(guò)渡如何結(jié)束。因此不必等待 CSS transitionend 事件, Vue.js 將等待你手工調(diào)用這個(gè)回調(diào),以結(jié)束過(guò)渡。例如:

enter: function (el) {
  // 沒(méi)有第二個(gè)參數(shù)
  // 由 CSS transitionend 事件決定過(guò)渡何時(shí)結(jié)束
}

vs.

enter: function (el, done) {
  // 有第二個(gè)參數(shù)
  // 過(guò)渡只有在調(diào)用 `done` 時(shí)結(jié)束
}

當(dāng)多個(gè)元素一起過(guò)渡時(shí),Vue.js 會(huì)批量處理,只強(qiáng)制一次布局。

CSS 動(dòng)畫(huà)

CSS 動(dòng)畫(huà)用法同 CSS 過(guò)渡,區(qū)別是在動(dòng)畫(huà)中 v-enter 類(lèi)名在節(jié)點(diǎn)插入 DOM 后不會(huì)立即刪除,而是在 animationend 事件觸發(fā)時(shí)刪除。

示例: (省略了兼容性前綴)

<span v-show="show" transition="bounce">Look at me!</span>
.bounce-transition {
  display: inline-block; /* 否則 scale 動(dòng)畫(huà)不起作用 */
}
.bounce-enter {
  animation: bounce-in .5s;
}
.bounce-leave {
  animation: bounce-out .5s;
}
@keyframes bounce-in {
  0% {
    transform: scale(0);
  }
  50% {
    transform: scale(1.5);
  }
  100% {
    transform: scale(1);
  }
}
@keyframes bounce-out {
  0% {
    transform: scale(1);
  }
  50% {
    transform: scale(1.5);
  }
  100% {
    transform: scale(0);
  }
}
{% raw %}
<div id="anim" class="demo">
  <span v-show="show" transition="bounce">Look at me!</span>
  <br>
  <button @click="show = !show">Toggle</button>
</div>

<style>
  .bounce-transition {
    display: inline-block;
  }
  .bounce-enter {
    -webkit-animation: bounce-in .5s;
    animation: bounce-in .5s;
  }
  .bounce-leave {
    -webkit-animation: bounce-out .5s;
    animation: bounce-out .5s;
  }
  @keyframes bounce-in {
    0% {
      -webkit-transform: scale(0);
      transform: scale(0);
    }
    50% {
      -webkit-transform: scale(1.5);
      transform: scale(1.5);
    }
    100% {
      -webkit-transform: scale(1);
      transform: scale(1);
    }
  }
  @keyframes bounce-out {
    0% {
      -webkit-transform: scale(1);
      transform: scale(1);
    }
    50% {
      -webkit-transform: scale(1.5);
      transform: scale(1.5);
    }
    100% {
      -webkit-transform: scale(0);
      transform: scale(0);
    }
  }
  @-webkit-keyframes bounce-in {
    0% {
      -webkit-transform: scale(0);
      transform: scale(0);
    }
    50% {
      -webkit-transform: scale(1.5);
      transform: scale(1.5);
    }
    100% {
      -webkit-transform: scale(1);
      transform: scale(1);
    }
  }
  @-webkit-keyframes bounce-out {
    0% {
      -webkit-transform: scale(1);
      transform: scale(1);
    }
    50% {
      -webkit-transform: scale(1.5);
      transform: scale(1.5);
    }
    100% {
      -webkit-transform: scale(0);
      transform: scale(0);
    }
  }
</style>

<script>
new Vue({
  el: '#anim',
  data: { show: true }
})
</script>
{% endraw %}

JavaScript 過(guò)渡

也可以只使用 JavaScript 鉤子,不用定義任何 CSS 規(guī)則。當(dāng)只使用 JavaScript 過(guò)渡時(shí),enterleave 鉤子需要調(diào)用 done 回調(diào),否則它們將被同步調(diào)用,過(guò)渡將立即結(jié)束。

為 JavaScript 過(guò)渡顯式聲明 css: false 是個(gè)好主意,Vue.js 將跳過(guò) CSS 檢測(cè)。這樣也會(huì)阻止無(wú)意間讓 CSS 規(guī)則干擾過(guò)渡。

在下例中我們使用 jQuery 注冊(cè)一個(gè)自定義的 JavaScript 過(guò)渡:

Vue.transition('fade', {
  css: false,
  enter: function (el, done) {
    // 元素已被插入 DOM
    // 在動(dòng)畫(huà)結(jié)束后調(diào)用 done
    $(el)
      .css('opacity', 0)
      .animate({ opacity: 1 }, 1000, done)
  },
  enterCancelled: function (el) {
    $(el).stop()
  },
  leave: function (el, done) {
    // 與 enter 相同
    $(el).animate({ opacity: 0 }, 1000, done)
  },
  leaveCancelled: function (el) {
    $(el).stop()
  }
})

然后用 transition 特性中:

<p transition="fade"></p>

漸近過(guò)渡

transitionv-for 一起用時(shí)可以創(chuàng)建漸近過(guò)渡。給過(guò)渡元素添加一個(gè)特性 stagger, enter-staggerleave-stagger

<div v-for="item in list" transition="stagger" stagger="100"></div>

或者,提供一個(gè)鉤子 stagger, enter-staggerleave-stagger,以更好的控制:

Vue.transition('stagger', {
  stagger: function (index) {
    // 每個(gè)過(guò)渡項(xiàng)目增加 50ms 延時(shí)
    // 但是最大延時(shí)限制為 300ms
    return Math.min(300, index * 50)
  }
})

示例:

<iframe width="100%" height="200" style="margin-left:10px" src="http://jsfiddle.net/yyx990803/mvo99bse/embedded/result,html,js,css" allowfullscreen="allowfullscreen" frameborder="0"></iframe>
上一篇:自定義指令下一篇:列表渲染