相关文章推荐
深情的火车  ·  python list 保存为txt-掘金·  1 年前    · 
多情的钢笔  ·  搜索结果_excel里 ...·  1 年前    · 
Vue.js从零开始——过渡 / 动画效果(1)

Vue.js从零开始——过渡 / 动画效果(1)

2 年前 · 来自专栏 全栈开发从零开始

Vue 在插入、更新或者移除 DOM 时,提供多种不同方式的应用过渡 / 动画效果,包括:

  • CSS 过渡和动画中自动应用 class
  • 可以配合使用第三方 CSS 动画库,如 Animate.css
  • 在过渡钩子函数中使用 JavaScript 直接操作 DOM
  • 可以配合使用第三方 JavaScript 动画库,如 Velocity.js

在这一章节,我们先看看进入、离开和列表的过渡。


1 单元素 / 组件的过渡

Vue 提供了 transition 的封装组件,在下列情形中,可以给任何元素和组件添加进入/离开过渡

  • 条件渲染 (使用 v-if )
  • 条件展示 (使用 v-show )
  • 动态组件
  • 组件根节点

这里是一个典型的例子, HTML 代码如下:

<div id="app">
  <button @click="show = !show">
    Toggle
  </button>
  <transition name="fade">
    <p v-if="show">hello</p>
  </transition>
</div>

Vue

new Vue({
  el: '#app',
  data: {
    show: true

CSS

.fade-enter-active, .fade-leave-active {
  transition: opacity .5s;
.fade-enter, .fade-leave-to {
  opacity: 0;
}

当插入或删除包含在 transition 组件中的元素时, Vue 将会做以下处理:

  1. 自动嗅探目标元素是否应用了 CSS 过渡或动画,如果是,在恰当的时机添加/删除 CSS 类名
  2. 如果过渡组件提供了 JavaScript 钩子函数,这些钩子函数将在恰当的时机被调用
  3. 如果没有找到 JavaScript 钩子并且也没有检测到 CSS 过渡/动画, DOM 操作 (插入/删除) 在下一帧中立即执行(这里是指浏览器逐帧动画机制,和 Vue nextTick 概念不同)

1.1 过渡的类名

在进入/离开的过渡中,会有 6 个 class 切换:

  1. v-enter :定义进入过渡的开始状态,在元素被插入之前生效,在元素被插入之后的下一帧移除
  2. v-enter-active :定义进入过渡生效时的状态,在整个进入过渡的阶段中应用,在元素被插入之前生效,在过渡 / 动画完成之后移除;这个类可以被用来定义进入过渡的过程时间,延迟和曲线函数
  3. v-enter-to :2.1.8 版及以上定义进入过渡的结束状态,在元素被插入之后下一帧生效 (与此同时 v-enter 被移除),在过渡 / 动画完成之后移除
  4. v-leave :定义离开过渡的开始状态,在离开过渡被触发时立刻生效,下一帧被移除
  5. v-leave-active :定义离开过渡生效时的状态,在整个离开过渡的阶段中应用,在离开过渡被触发时立刻生效,在过渡 / 动画完成之后移除;这个类可以被用来定义离开过渡的过程时间,延迟和曲线函数
  6. v-leave-to :2.1.8 版及以上定义离开过渡的结束状态,在离开过渡被触发之后下一帧生效 (与此同时 v-leave 被删除),在过渡/动画完成之后移除

对于这些在过渡中切换的类名来说,如果使用一个没有名字的 <transition> ,则 v- 是这些类名的默认前缀;如果使用了 <transition name="my-transition"> ,那么 v-enter 会替换为 my-transition-enter

v-enter-active v-leave-active 可以控制进入/离开过渡的不同的缓和曲线,后文会有示例说明。

1.2 CSS过渡

常用的过渡都是使用 CSS 过渡,下面是个例子:

HTML

<div id="app">
  <button @click="show = !show">
    Toggle render
  </button>
  <transition name="slide-fade">
    <p v-if="show">hello</p>
  </transition>
</div>

Vue (这里并没有改动):

new Vue({
  el: '#app',
  data: {
    show: true

CSS

/* 可以设置不同的进入和离开动画 */
/* 设置持续时间和动画函数 */
.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;
}

1.3 CSS 动画

CSS 动画用法和 CSS 过渡类似,区别是在动画中 v-enter 类名在节点插入 DOM 后不会立即删除,而是在 animationend 事件触发时删除,下面是个例子:

HTML

<div id="app">
  <button @click="show = !show">Toggle show</button>
  <transition name="bounce">
    <p v-if="show">Lorem ipsum dolor sit amet, consectetur adipiscing elit. 
      Mauris facilisis enim libero, at lacinia diam fermentum id. 
      Pellentesque habitant morbi tristique senectus et netus.</p>
  </transition>
</div>

Vue (这里并没有改动):

new Vue({
  el: '#app',
  data: {
    show: true

CSS

.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);
// 考虑到演示效果限制p元素的宽度
  width: 500px;
}

1.4 自定义过渡的类名

我们可以通过以下属性来自定义过渡类名:

  • 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 (个人很喜欢这个 CSS 库,省去了很多自己定义动画的精力,而且效果都不错,除非有特殊的需求,不然直接用它就足够了)结合使用十分有用,下面是个例子:

HTML

<!-- 这里引用 animate.css -->
  rel="stylesheet"
  href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.0.0/animate.min.css"
<div id="app">
  <button @click="show = !show">
    Toggle render
  </button>
  <transition
    name="custom-classes-transition"
    enter-active-class="animate__animated animate__bounceInRight"
    leave-active-class="animate__animated animate__bounceOutUp"
    <p v-if="show">hello</p>
  </transition>
</div>

Vue (这里并没有改动):

new Vue({
  el: '#app',
  data: {
    show: true

这个样例因为使用了 CSS 库,所以并没有写入任何 CSS 样式。

1.5 同时使用过渡和动画

Vue 为了知道过渡的完成,必须设置相应的事件监听器,可以是 transitionend animationend ,这取决于给元素应用的 CSS 规则;如果我们使用其中任何一种, Vue 能自动识别类型并设置监听。

但是,在一些场景中,我们需要给同一个元素同时设置两种过渡动效,比如动画很快的被触发并完成了,而过渡效果还没结束:在这种情况中,就需要使用 type 属性并设置 animation transition 来明确声明我们需要 Vue 监听的类型。

2 定义过渡持续时间

在很多情况下, Vue 可以自动得出过渡效果的完成时机,默认情况下, Vue 会等待其在过渡效果的根元素的第一个 transitionend animationend 事件。

然而也可以不这样设定,比如,我们可以拥有一个精心编排的一系列过渡效果,其中一些嵌套的内部元素相比于过渡效果的根元素有延迟的或更长的过渡效果。

在这种情况下可以用 <transition> 组件上的 duration prop 显式的定义过渡持续时间 (以毫秒计):

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

也可以定义进入和移出的持续时间:

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

3 JavaScript钩子

可以在 HTML 属性中声明 JavaScript 钩子:

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

Vue:

// ...
methods: {
  // 进入中
  beforeEnter: function (el) {
    // ...
  // 当与 CSS 结合使用时,回调函数 done 是可选的
  enter: function (el, done) {
    // ...
    done();
  afterEnter: function (el) {
    // ...
  enterCancelled: function (el) {
    // ...
  // 离开时
  beforeLeave: function (el) {
    // ...
  // 当与 CSS 结合使用时,回调函数 done 是可选的
  leave: function (el, done) {
    // ...
    done();
  afterLeave: function (el) {
    // ...
  // leaveCancelled 只用于 v-show 中
  leaveCancelled: function (el) {
    // ...

这些钩子函数可以结合 CSS 过渡 / 动画使用,也可以单独使用。

当只用 JavaScript 过渡的时候,在 enter leave 中必须使用 done 进行回调,否则,它们将被同步调用,过渡会立即完成。
推荐对于仅使用 JavaScript 过渡的元素添加 :css="false" Vue 会跳过 CSS 的检测,这也可以避免过渡过程中 CSS 的影响。

下面是一个使用 Velocity.js 的简单例子:

HTML

<!--
Velocity 和 jQuery.animate 的工作方式类似,也是用来实现 JavaScript 动画的一个很棒的选择
<script src="//cdnjs.cloudflare.com/ajax/libs/velocity/2.0.6/velocity.min.js"></script>
<div id="app">
  <button @click="show = !show">
    Toggle
  </button>
  <transition
    @before-enter="beforeEnter"
    @enter="enter"
    @leave="leave"
    :css="false"
    <p v-if="show">
  </transition>
</div>

Vue

new Vue({
  el: '#app',
  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, { fontSize: '2em', opacity: 0.5 }, { duration: 600 });
      Velocity(el, {opacity: 0}, { complete: done });

4 初始渲染的过渡

可以通过 appear 属性设置节点在初始渲染的过渡:

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

这里默认和进入 / 离开过渡一样,同样也可以自定义 CSS 类名:

<transition
  appear
  appear-class="custom-appear-class"
  appear-to-class="custom-appear-to-class"
  appear-active-class="custom-appear-active-class"
  <!-- ... -->
</transition>

也可以自定义 JavaScript 钩子:

<transition
  appear
  @before-appear="customBeforeAppearHook"
  @appear="customAppearHook"
  @after-appear="customAfterAppearHook"
  @appear-cancelled="customAppearCancelledHook"
  <!-- ... -->
</transition>

在上面的例子中,无论是 appear 属性还是 v-on:appear 钩子都会生成初始渲染过渡。

5 多个元素的过渡

多个元素的过渡,对于原生标签可以使用 v-if / v-else ,最常见的多标签过渡是一个列表和描述这个列表为空消息的元素:

<transition>
  <table v-if="items.length > 0">
    <!-- ... -->
  </table>
  <p v-else>Sorry, no items found.</p>
</transition>

可以这样使用,但是有一点需要注意:

当有相同标签名的元素切换时,需要通过 key 属性设置唯一的值来标记以让 Vue 区分它们,否则 Vue 为了效率只会替换相同标签内部的内容;即使在技术上没有必要,给在 <transition> 组件中的多个元素设置 key 也是一个更好的实践。

示例:

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

在一些场景中,也可以通过给同一个元素的 key 属性设置不同的状态来代替 v-if v-else ,上面的例子可以重写为:

<transition>
  <button :key="isEditing">
    {{ isEditing ? 'Save' : 'Edit' }}
  </button>
</transition>

使用多个 v-if 的多个元素的过渡可以重写为绑定了动态属性的单个元素过渡,比如:

<transition>
  <button v-if="docState === 'saved'" key="saved">
  </button>
  <button v-if="docState === 'edited'" key="edited">
  </button>
  <button v-if="docState === 'editing'" key="editing">
    Cancel
  </button>
</transition>

可以重写为:

<transition>
  <button :key="docState">
    {{ buttonMessage }}
  </button>
</transition>

Vue

// ...
computed: {
  buttonMessage: function () {
    switch (this.docState) {
      case 'saved': return 'Edit'
      case 'edited': return 'Save'
      case 'editing': return 'Cancel'

5.1 过渡模式

这里还有一个问题,在默认情况下,我们使用如下代码,生成按钮并点击:

<!DOCTYPE html>
    <meta charset="utf-8">
    <title>hello</title>
    <script src="https://unpkg.com/vue/dist/vue.js"></script>
    <style>
      .demo-enter-active, .demo-leave-active {
        transition: opacity .5s
      .demo-enter, .demo-leave-active {
        opacity: 0
    </style>
  </head>
    <div id="app">
      <transition name="demo">
        <button v-if="on" key="on" @click="on = false">
        </button>
        <button v-else key="off" @click="on = true">
        </button>
      </transition>
    </div>
    <script>
      new Vue({
        el: '#app',
        data: {
          on: false
    </script>
  </body>
</html>

on off 按钮的过渡中,两个按钮都被重绘了,一个离开过渡的时候另一个开始进入过渡,这是 <transition> 的默认行为:进入和离开同时发生。

在元素绝对定位在彼此之上的时候运行正常:

HTML

<div id="app">
  <div class="demo-wrapper">
    <transition name="demo">
      <button v-if="on" key="on" @click="on = false">
      </button>
      <button v-else key="off" @click="on = true">
      </button>
    </transition>
  </div>
</div>

CSS

.demo-wrapper {
  position: relative;
  height: 18px;
.demo-wrapper button {
  position: absolute;
.demo-enter-active, .demo-leave-active {
  transition: opacity .5s;
.demo-enter, .demo-leave-active {
  opacity: 0;
}

然后,我们加上 translate 让它们运动像滑动过渡,只需要修改 CSS:

.demo-wrapper {
  position: relative;
  height: 18px;
.demo-wrapper button {
  position: absolute;
.demo-enter-active, .demo-leave-active {
  transition: all 1s;
.demo-enter, .demo-leave-active {
  opacity: 0;
.demo-enter {
  transform: translateX(31px);
.demo-leave-active {
  transform: translateX(-31px);
}

同时生效的进入和离开的过渡不能满足所有要求,所以 Vue 提供了过渡模式:

  • in-out :新元素先进行过渡,完成之后当前元素过渡离开
  • out-in :当前元素先进行过渡,完成之后新元素过渡进入

out-in 重写最开始的开关按钮过渡:

<!-- 增加一个mode属性即可 -->
<transition name="demo" mode="out-in">
  <!-- ... the buttons ... -->
</transition>

只用添加一个简单的 HTML 属性,就解决了之前的过渡问题而无需任何额外的代码。

in-out 模式不是经常用到,但对于一些稍微不同的过渡效果还是有用的,比如与之前(上上个例子)滑动淡出的例子结合:

这种方式做出来就把 Vue 的过渡模式和 CSS 过渡更有趣的整合在了一起。

5.2 多个组件的过渡

多个组件的过渡简单很多,我们不需要使用 key 属性,我们只需要使用动态组件即可:

HTML

<transition name="component-fade" mode="out-in">
  <component :is="view"></component>
</transition>

Vue

new Vue({
  el: '#app',
  data: {
    view: 'v-a'
  components: {
    'v-a': {
      template: '<div>Component A</div>'
    'v-b': {
      template: '<div>Component B</div>'

CSS

.component-fade-enter-active, .component-fade-leave-active {
  transition: opacity .3s ease;
.component-fade-enter, .component-fade-leave-to {
  opacity: 0;
}

6 列表过渡

上文中,有关过渡我们已经看到过:

  • 单个节点
  • 同一时间渲染多个节点中的一个

那么怎么同时渲染整个列表,比如使用 v-for ——在这种场景中,使用 <transition-group> 组件。

关于这个组件的几个特点:

  • 不同于 <transition> ,它会以一个真实元素呈现:默认为一个 <span>, 也可以通过 tag 属性更换为其他元素
  • 过渡模式不可用,因为我们不再相互切换特有的元素
  • 内部元素总是需要提供唯一的 key 属性值
  • CSS 过渡的类将会应用在内部的元素中,而不是这个组 / 容器本身

6.1 列表的进入 / 离开过渡

下面是个例子:

HTML

<div id="app">
  <button @click="add">Add</button>
  <button @click="remove">Remove</button>
  <transition-group name="list" tag="p">
    <span v-for="item in items" :key="item" class="list-item">
      {{ item }}
    </span>
  </transition-group>
</div>

Vue

new Vue({
  el: '#app',
  data: {
    items: [1,2,3,4,5,6,7,8,9],
    nextNum: 10
  methods: {
    randomIndex: function () {
      return Math.floor(Math.random() * this.items.length);
    add: function () {
      this.items.splice(this.randomIndex(), 0, this.nextNum++);
    remove: function () {
      this.items.splice(this.randomIndex(), 1);

CSS

.list-item {
  display: inline-block;
  margin-right: 10px;
.list-enter-active, .list-leave-active {
  transition: all 1s;
.list-enter, .list-leave-to {
  opacity: 0;
  transform: translateY(30px);
}

这个例子有个小问题,当添加和移除元素的时候,周围的元素会瞬间移动到他们的新布局的位置,而不是平滑的过渡,下面我们会尝试解决这个问题。

6.2 列表的排序过渡

<transition-group> 组件还有一个特殊之处:不仅可以进入和离开动画,还可以改变定位。

要使用这个新功能只需了解新增的 v-move 类,它会在元素的改变定位的过程中应用,像之前的类名一样,可以通过 name 属性来自定义前缀,也可以通过 move-class 属性手动设置。

v-move 对于设置过渡的切换时机和过渡曲线非常有用,比如下面的这个例子:

<!DOCTYPE html>
    <meta charset="utf-8">
    <title>hello</title>
    <script src="https://unpkg.com/vue/dist/vue.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.14.1/lodash.min.js"></script>
    <style>
      .flip-list-move {
        transition: transform 1s;
    </style>
  </head>
    <div id="app">
      <button @click="shuffle">Shuffle</button>
      <transition-group name="flip-list" tag="ul">
        <li v-for="item in items" :key="item">
          {{ item }}
      </transition-group>
    </div>
    <script>
      new Vue({
        el: '#app',
        data: {
          items: [1,2,3,4,5,6,7,8,9]
        methods: {
          shuffle: function () {
            this.items = _.shuffle(this.items)
    </script>
  </body>
</html>

这个有趣的动画是因为 Vue 使用了一个叫 FLIP 简单的动画队列,使用 transforms 将元素从之前的位置平滑过渡新的位置。

我们将之前实现的例子和这个技术结合,使我们列表的一切变动都会有动画过渡,完整代码如下:

<!DOCTYPE html>
    <meta charset="utf-8">
    <title>hello</title>
    <script src="https://unpkg.com/vue/dist/vue.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.14.1/lodash.min.js"></script>
    <style>
      .list-item {
        transition: all 1s;
        display: inline-block;
        margin-right: 10px;
      .list-enter, .list-leave-to {
        opacity: 0;
        transform: translateY(30px);
      .list-leave-active {
        position: absolute;
    </style>
  </head>
    <div id="app" class="demo">
      <button @click="shuffle">Shuffle</button>
      <button @click="add">Add</button>
      <button @click="remove">Remove</button>
      <transition-group name="list" tag="p">
          v-for="item in items"
          :key="item"
          class="list-item"
          {{ item }}
        </span>
      </transition-group>
    </div>
    <script>
      new Vue({
        el: '#app',
        data: {
          items: [1,2,3,4,5,6,7,8,9],
          nextNum: 10
        methods: {
          randomIndex: function () {
            return Math.floor(Math.random() * this.items.length)
          add: function () {
            this.items.splice(this.randomIndex(), 0, this.nextNum++)
          remove: function () {
            this.items.splice(this.randomIndex(), 1)
          shuffle: function () {
            this.items = _.shuffle(this.items)
    </script>
  </body>
</html>
需要注意的是使用 FLIP 过渡的元素不能设置为 display: inline ;作为替代方案,我们可以设置为 display: inline-block 或者放置于弹性盒子( flex )中

FLIP 动画不仅可以实现单列过渡,多维网格也可以做到该效果:

<!DOCTYPE html>
    <title>hello</title>
    <script src="https://unpkg.com/vue"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.14.1/lodash.min.js"></script>
    <style>
      .container {
        display: flex;
        flex-wrap: wrap;
        width: 238px;
        margin-top: 10px;
      .cell {
        display: flex;
        justify-content: space-around;
        align-items: center;
        width: 25px;
        height: 25px;
        border: 1px solid #aaa;
        margin-right: -1px;
        margin-bottom: -1px;
      .cell:nth-child(3n) {
        margin-right: 0;
      .cell:nth-child(27n) {
        margin-bottom: 0;
      .cell-move {
        transition: transform 1s;
    </style>
  </head>
    <div id="app">
      <h1>数独随机排列演示</h1>
      <p>点击随机按钮进行随机排列</p>
      <button @click="shuffle">
      </button>
      <transition-group name="cell" tag="div" class="container">
        <div v-for="cell in cells" :key="cell.id" class="cell">
          {{ cell.number }}
        </div>
      </transition-group>
    </div>
    <script>
      new Vue({
        el: "#app",
        data: {
          cells: Array.apply(null, { length: 81 }).map(function(_, index) {
            return {
              id: index,
              number: (index % 9) + 1
        methods: {
          shuffle: function() {
            this.cells = _.shuffle(this.cells);
    </script>
  </body>
</html>

6.3 列表的交错过渡

通过 data 属性与 JavaScript 通信,就可以实现列表的交错过渡:

<!DOCTYPE html>
    <title>hello</title>
    <script src="https://unpkg.com/vue"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/velocity/1.2.3/velocity.min.js"></script>
  </head>
    <div id="app">
      <input v-model="query">
      <transition-group
        name="staggered-fade"
        tag="ul"
        :css="false"
        @before-enter="beforeEnter"
        @enter="enter"
        @leave="leave"
          v-for="(item, index) in computedList"
          :key="item.msg"
          :data-index="index"
        >{{ item.msg }}</li>
      </transition-group>
    </div>
    <script>
      new Vue({
        el: '#app',
        data: {
          query: '',
          list: [
            { msg: 'Pentera' },
            { msg: 'Metallica' },
            { msg: 'Queen' },
            { msg: 'Guns n\' roses' },
            { msg: 'AC/DC' }
        computed: {
          computedList: function () {
            let vm = this;
            return this.list.filter(function (item) {
              return item.msg.toLowerCase().indexOf(vm.query.toLowerCase()) !== -1;
        methods: {
          beforeEnter: function (el) {
            el.style.opacity = 0;
            el.style.height = 0;
          enter: function (el, done) {
            let delay = el.dataset.index * 150;
            setTimeout(function () {
              Velocity(
                { opacity: 1, height: '1.6em' },
                { complete: done }
            }, delay);
          leave: function (el, done) {
            let delay = el.dataset.index * 150;
            setTimeout(function () {
              Velocity(
                { opacity: 0, height: 0 },
                { complete: done }
            }, delay);
    </script>
  </body>
</html>
加了点私货,都是我喜欢的乐队,哈哈

7 过渡的进阶

7.1 可复用的过渡组件

过渡可以通过 Vue 的组件系统实现复用。

要创建一个可复用过渡组件,我们只需要将 <transition> 或者 <transition-group> 作为根组件,然后将任何子组件放置在其中就可以了。

使用 template 的简单例子:

Vue.component('my-special-transition', {
  template: `
    <transition
      name="very-special-transition"
      mode="out-in"
      @before-enter="beforeEnter"
      @after-enter="afterEnter"
      <slot></slot>
    </transition>
  methods: {
    beforeEnter: function (el) {
      // ...
    afterEnter: function (el) {
      // ...

函数式组件 更适合完成这个任务:

Vue.component('my-special-transition', {
  functional: true,
  render: function (createElement, context) {
    let data = {
      props: {
        name: 'very-special-transition',
        mode: 'out-in'
      on: {
        beforeEnter: function (el) {
          // ...
        afterEnter: function (el) {
          // ...
    return createElement('transition', data, context.children);

7.2 动态过渡

动态过渡最基本的例子是通过 name 属性来绑定动态值:

<transition :name="transitionName">
  <!-- ... -->
</transition>

这在我们想用 Vue 的过渡系统,来定义的 CSS 过渡 / 动画在不同过渡间切换时,会非常有用。

所有过渡属性都可以动态绑定,但我们不仅仅只有 HTML 属性可以利用,还可以通过事件钩子获取上下文中的所有数据,因为事件钩子都是方法——这意味着,根据组件的状态不同, JavaScript 过渡会有不同的表现,下面是个例子:

<!DOCTYPE html>
    <title>hello</title>
    <script src="https://unpkg.com/vue"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/velocity/1.2.3/velocity.min.js"></script>
  </head>
    <div id="app">
      Fade In: <input type="range" v-model="fadeInDuration" min="0" :max="maxFadeDuration">
      Fade Out: <input type="range" v-model="fadeOutDuration" min="0" :max="maxFadeDuration">
      <transition
        :css="false"
        @before-enter="beforeEnter"
        @enter="enter"
        @leave="leave"
        <p v-if="show">hello</p>
      </transition>
      <button
        v-if="stop"
        @click="stop = false; show = false"
      >Start animating</button>
      <button
        v-else
        @click="stop = true"
      >Stop it!</button>
    </div>
    <script>
      new Vue({
        el: '#app',
        data: {
          show: true,
          fadeInDuration: 1000,
          fadeOutDuration: 1000,
          maxFadeDuration: 1500,
          stop: true
        mounted: function () {
          this.show = false;
        methods: {
          beforeEnter: function (el) {
            el.style.opacity = 0;
          enter: function (el, done) {
            let vm = this;
            Velocity(el,
              { opacity: 1 },
                duration: this.fadeInDuration,
                complete: function () {
                  done();
                  if (!vm.stop) vm.show = false;
          leave: function (el, done) {
            let vm = this;
            Velocity(el,
              { opacity: 0 },
                duration: this.fadeOutDuration,
                complete: function () {
                  done();
                  vm.show = true;