• 首页
  • css3教程
  • html5教程
  • vue手册
  • php手册
  • MySQL手册
  • redis手册
  • jQuery手册
  • Transition 过度

    Vue 提供了两个内置组件,可以帮助你制作基于状态变化的过渡和动画:

    • <Transition>会在一个元素或组件进入和离开 DOM 时应用动画。
    • <TransitionGroup>会在一个元素或组件被插入到v-for列表中,或是被移动或从其中移除时应用动画。

    除了这两个组件,我们也可以通过其他技术手段来应用动画,比如切换 CSS 类或用状态绑定样式来驱动动画。这些其他的方法会在动画技巧章节中展开介绍到。

    <Transition>组件

    <Transition>是一个内置组件,这意味着它在任意别的组件中都可以被使用,无需注册。它可以在一般元素以及通过默认插槽传递的组件上应用进入和离开动画。进入或离开可以由以下其中一种触发:

    • v-if所带来的条件渲染
    • v-show所带来的条件显示
    • 由特殊元素<component>切换的动态组件

    以下是最基本用法的示例:

    <button @click="show = !show">Toggle</button>
    <Transition>
      <p v-if="show">hello</p>
    </Transition>
    
    /* 下面我们会解释这些类是做什么的 */
    .v-enter-active,
    .v-leave-active {
      transition: opacity 0.5s ease;
    }
    
    .v-enter-from,
    .v-leave-to {
      opacity: 0;
    }
    
    <Transition>仅支持单个元素或组件作为其插槽内容。如果内容是一个组件,这个组件必须仅有一个根元素。

    当一个<Transition>组件中的元素被插入或移除时,会发生下面这些事情:

    • Vue 会自动检测目标元素是否应用了 CSS 过渡或动画。如果是,则一些 CSS 过渡类会在适当的时机被添加和移除。
    • 如果有作为监听器的 JavaScript 钩子,这些钩子函数会在适当时机被调用。
    • 如果没有探测到 CSS 过渡或动画、没有提供 JavaScript 钩子,那么 DOM 的插入、删除操作将在浏览器的下一个动画帧上执行。


    基于 CSS 的过渡

    过渡 CSS 类

    一共有 6 个应用于进入与离开过渡效果的 CSS 类。


    • v-enter-active:进入动画的生效状态,应用于整个进入动画阶段。这个 CSS 类,在元素被插入之前,添加,在过渡/动画完成之后移除。这个类可以用来定义,进入动画的持续时间、延迟与速度曲线类型。
    • v-enter-from:进入动画的起始状态。这个 CSS 类,在元素插入之前添加,在元素插入完成后的下一帧,移除。
    • v-enter-to:进入动画的结束状态。这个 CSS 类,在元素插入完成后的下一帧,被添加(也就是v-enter-from被移除的同时),在过渡/动画完成之后移除。
    • v-leave-active:离开动画的生效状态,应用于整个离开动画阶段。在离开过渡效果被触发时立即添加,在过渡/动画完成之后移除。这个类可以用来定义,离开动画的持续时间、延迟与速度曲线类型。
    • v-leave-from:离开动画的起始状态,在离开过渡效果被触发时,立即添加,在一帧后被移除。
    • v-leave-to:离开动画的结束状态。这个 CSS 类,在一个离开动画被触发后的下一帧,被添加(也就是v-leave-from被移除的同时),在过渡/动画完成之后移除。

    v-enter-activev-leave-active给了我们为进入和离开动画指定不同速度曲线的能力。


    为过渡命名

    可以通过一个name prop 来声明一种过渡:

    <Transition name="fade">
      ...
    </Transition>
    

    对于一个有名字的过渡,它的过渡相关 CSS 类会以其名字而不是 v 作为前缀。举个例子,上面被应用的 CSS 类将会是fade-enter-active而不是v-enter-active。这个fade过渡的 CSS 类将会是这样:

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


    CSS 的 transition

    <Transition>一般都会搭配原生 CSS 过渡一起使用,正如你在上面的例子中所看到的那样。这个transition CSS 属性是一个简写形式,使我们可以一次定义一个过渡的各个方面,包括需要执行动画的属性、持续时间和速度曲线。

    下面是一个更高级的例子,使用不同的持续时间和速度曲线来过渡多个属性:

    <Transition name="slide-fade">
      <p v-if="show">hello</p>
    </Transition>
    
    /* 进入和离开动画可以使用不同 持续时间和速度曲线。 */
    .slide-fade-enter-active {
      transition: all 0.3s ease-out;
    }
    .slide-fade-leave-active {
      transition: all 0.8s cubic-bezier(1, 0.5, 0.8, 1);
    }
    .slide-fade-enter-from,
    .slide-fade-leave-to {
      transform: translateX(20px);
      opacity: 0;
    }
    


    CSS 的 animation

    原生 CSS 动画和 CSS trasition 的应用方式基本上是相同的,只有一点不同,那就是*-enter-from不是在元素插入后立即移除,而是在一个animationend事件触发时被移除。

    对于大多数的 CSS 动画,我们可以简单地在*-enter-active*-leave-active类下面声明它们。下面是一个示例:

    <Transition name="bounce">
      <p v-if="show" style="text-align: center;">
        Hello here is some bouncy text!
      </p>
    </Transition>
    
    .bounce-enter-active {
      animation: bounce-in 0.5s;
    }
    
    .bounce-leave-active {
      animation: bounce-in 0.5s reverse;
    }
    
    @keyframes bounce-in {
      0% {
        transform: scale(0);
      }
      50% {
        transform: scale(1.25);
      }
      100% {
        transform: scale(1);
      }
    }
    


    自定义过渡类

    你也可以向<Transition>传递以下的props来指定自定义的过渡类:

    • enter-from-class
    • enter-active-class
    • enter-to-class
    • leave-from-class
    • leave-active-class
    • leave-to-class

    你传入的这些类会覆盖相应阶段的默认类名。这个功能在你想要在 Vue 的动画机制下集成其他的第三方 CSS 动画库时非常有用,比如Animate.css

    <!-- 假设你已经引入了 Animate.css -->
    <Transition
      name="custom-classes"
      enter-active-class="animate__animated animate__tada"
      leave-active-class="animate__animated animate__bounceOutRight"
    >
      <p v-if="show">hello</p>
    </Transition>
    


    同时使用 transition 和 animation

    Vue 需要附加事件侦听器,以便知道过渡何时结束。可以是transitionendanimationend,这取决于你所应用的 CSS 规则。如果你仅仅使用二者其中之一,Vue 可以自动探测到正确的类型。

    然而在某些场景中,你或许想要在同一个元素上同时使用它们两个,举个例子,触发了一个 CSS 动画的同时,由于副作用触发了另一个 CSS 过渡。此时你需要显式地传入type prop 来声明,告诉 Vue 需要关心哪种类型,传入的值是animationtransition

    <Transition type="animation">...</Transition>
    


    深层级过渡与显式过渡时间

    尽管过渡类仅能应用在<Transition>的直接子元素上,我们还是可以使用深层级的 CSS 选择器,使深层级的元素发生过渡。

    <Transition name="nested">
      <div v-if="show" class="outer">
        <div class="inner">
          Hello
        </div>
      </div>
    </Transition>
    
    /* 应用于深层级元素的规则 */
    .nested-enter-active .inner,
    .nested-leave-active .inner {
      transition: all 0.3s ease-in-out;
    }
    
    .nested-enter-from .inner,
    .nested-leave-to .inner {
      transform: translateX(30px);
      opacity: 0;
    }
    

    我们甚至可以在深层级的元素上添加一个过渡延迟,这会创建一个交错进入动画序列:

    /* 延迟进入深层级元素以获得交错效果 */
    .nested-enter-active .inner {
      transition-delay: 0.25s;
    }
    

    然而,这会带来一个小问题。默认情况下,<Transition>组件会通过监听过渡根元素上的第一个transitionend或者animationend事件来尝试自动判断过渡何时结束。而在深层级的过渡中,期望的行为应该是等待所有内部元素的过渡完成。

    在这种情况下,你可以通过向<Transition>组件传入duration prop 来显式指定的过渡持续时间(以毫秒为单位)。总持续时间应该匹配延迟加上内部元素的过渡持续时间:

    <Transition :duration="550">...</Transition>
    

    如果有必要的话,你也可以用对象的形式传入,分开指定进入和离开所需的时间:

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


    性能考量

    你可能注意到我们上面例子中展示的动画所用到的属性大多是transformopacity之类的。用这些属性制作动画非常高效,因为:

    • 他们在动画过程中不会影响到 DOM 结构,因此每一个动画帧都不会触发昂贵的 CSS 布局重新计算。
    • 大多数的现代浏览器都可以在执行transform动画时利用 GPU 进行硬件加速。

    相比之下,像height或者margin这样的属性会触发 CSS 布局变动,因此执行它们的动画效果更昂贵,需要谨慎使用。我们可以在CSS-Triggers这类的网站查询哪些属性会在执行动画时触发 CSS 布局变动。


    JavaScript 钩子

    你可以通过监听<Transition>组件事件的方式在过渡过程中挂上钩子函数:

    <Transition
      @before-enter="onBeforeEnter"
      @enter="onEnter"
      @after-enter="onAfterEnter"
      @enter-cancelled="onEnterCancelled"
      @before-leave="onBeforeLeave"
      @leave="onLeave"
      @after-leave="onAfterLeave"
      @leave-cancelled="onLeaveCancelled"
    >
      <!-- ... -->
    </Transition>
    
    // 在元素被插入到 DOM 之前被调用
    // 用这个来设置元素的 "enter-from" 状态
    function onBeforeEnter(el) {},
    
    // 在元素被插入到 DOM 之后的下一帧被调用
    // 用这个来开始进入动画
    function onEnter(el, done) {
      // 调用回调函数 done 表示过渡结束
      // 如果与 CSS 结合使用,则这个回调是可选参数
      done()
    }
    
    // 当进入过渡完成时调用。
    function onAfterEnter(el) {}
    function onEnterCancelled(el) {}
    
    // 在 leave 钩子之前调用
    // 大多数时候,你应该只会用到 leave 钩子
    function onBeforeLeave(el) {}
    
    // 在离开过渡开始时调用
    // 用这个来开始离开动画
    function onLeave(el, done) {
      // 调用回调函数 done 表示过渡结束
      // 如果与 CSS 结合使用,则这个回调是可选参数
      done()
    }
    
    // 在离开过渡完成、 且元素已从 DOM 中移除时调用
    function onAfterLeave(el) {}
    
    // 仅在 v-show 过渡中可用
    function leaveCancelled(el) {}
    

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

    在使用仅由 JavaScript 执行的动画时,最好是添加一个:css="false" prop。这显式地向 Vue 表明跳过 CSS 过渡的自动探测。除了性能稍好一些之外,还可以防止 CSS 规则意外地干扰过渡。

    <Transition
      ...
      :css="false"
    >
      ...
    </Transition>
    

    在有了:css="false"后,我们就全权自己负责控制什么时候过渡结束了。这种情况下对于@enter@leave钩子来说,回调函数done()就是必须的。否则,钩子将被同步调用,过渡将立即完成


    可重用过渡

    得益于 Vue 的组件系统,过渡是可以被重用的。要创建一个可被重用的过渡,我们需要为<Transition>组件创建一个包装组件,并向内传入插槽内容:

    <!-- MyTransition.vue -->
    <script>
    // JavaScript 钩子逻辑...
    </script>
    
    <template>
      <!-- 包装内置的 Transition 组件 -->
      <Transition
        name="my-transition"
        @enter="onEnter"
        @leave="onLeave"
      >
        <slot></slot> <!-- 向内传递插槽内容 -->
      </Transition>
    </template>
    
    <style>
    /*
      必要的 CSS...
      注意:避免在这里使用 <style scoped>
      因为那不会应用到插槽内容上
    */
    </style>
    

    现在 MyTransition 可以在导入后像内置组件那样使用了:

    <MyTransition>
      <div v-if="show">Hello</div>
    </MyTransition>
    


    出现时过渡

    如果你想在某个节点初次渲染时应用一个过渡效果,你可以添加appear attribute:

    <Transition appear>
      ...
    </Transition>
    


    元素间过渡

    除了通过v-if/v-show切换一个元素,我们也可以通过v-if/v-else/v-else-if在几个组件间进行切换过:

    <Transition>
      <button v-if="docState === 'saved'">Edit</button>
      <button v-else-if="docState === 'edited'">Save</button>
      <button v-else-if="docState === 'editing'">Cancel</button>
    </Transition>
    


    过渡模式

    在之前的例子中,进入和离开的元素都是在同时开始动画的,并且我们必须将它们设为position: absolute以避免二者同时存在时出现的布局问题。

    然而,在某些场景中这可能不是个好的方案,或者并不能符合行为预期。我们可能想要先执行离开动画,然后在其完成之后再执行元素的进入动画。手动编排这样的动画是非常复杂的,好在我们可以通过向<Transition>传入一个mode prop 来实现这个行为:

    <Transition mode="out-in">
      ...
    </Transition>
    

    <Transition>也支持mode="in-out",虽然这并不常用。


    组件间过渡

    <Transition>也可以用在动态组件之间:

    <Transition name="fade" mode="out-in">
      <component :is="activeComponent"></component>
    </Transition>
    


    动态过渡

    <Transition>props(比如name)也可以是动态的!这让我们可以根据状态变化动态地应用不同类型的过渡:

    <Transition :name="transitionName">
      <!-- ... -->
    </Transition>Fallthrough
    

    当你使用 Vue 的过渡类约定规则定义了 CSS 过渡/动画,并想在它们之间切换时,这可能很有用。

    你也可以根据你的组件的当前状态在 JavaScript 过渡钩子中应用不同的行为。在此篇的最后,我们可以得出结论,创建动态过渡的终极方式是创建可重用的过渡组件,这些组件接受prop来改变过渡的性质。现在在编写动画时,就真的只有你想不到,没有做不到的了。