Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

关于Vue2.0的过渡效果与过渡状态 #2

Open
bluezhan opened this issue Nov 10, 2016 · 4 comments
Open

关于Vue2.0的过渡效果与过渡状态 #2

bluezhan opened this issue Nov 10, 2016 · 4 comments

Comments

@bluezhan
Copy link
Owner

bluezhan commented Nov 10, 2016

前言

因为是MVVM模式,加上又是单页面应用,会考虑到页面的切换过渡效果和各种组件、控件、插件的界面动态切换效果。固然就会在这一块加入概念和处理方式。

大概的处理方式就是:

  • CSS3.0的动画
  • JavaScript的处理

看一下官方过渡效果

通过 Vue 2.0 的过渡系统, 在插入、更新或者移除 DOM 时,提供多种不同方式的应用过渡效果。
包括以下工具:

  • CSS 过渡和动画中自动应用 class
  • 可以配合使用第三方 CSS 动画库,如 Animate.css
  • 在过渡钩子函数中使用 JavaScript 直接操作 DOM
  • 可以配合使用第三方 JavaScript 动画库,如 Velocity.js
  • 在这里,我们只会讲到进入、离开和列表的过渡, 你也可以看下一节的 管理过渡状态.

单元素/组件的过渡

贴个例子:

<div id="demo">
  <button v-on:click="show = !show">
    点击我切换(Toggle)
  </button>
  <transition name="fade">
    <p v-if="show">我就是个动画</p>
  </transition>
</div>
.fade-enter-active, .fade-leave-active {
  transition: opacity .5s
}
.fade-enter, .fade-leave-active {
  opacity: 0
}
new Vue({
  el: '#demo',
  data: {
    show: true
  }
})

在上面的案例,会发现:有个 transition 的封装元素,而且还会发生CSS的类的命名格式。

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

  • 条件渲染 (使用 v-if
  • 条件展示 (使用 v-show
  • 循环语句 (使用 v-for;只在插入和删除时触发,使用 vue-animated-list 插件)
  • 动态组件 (数据驱动组件过渡)
  • 组件根节点 (组件的根节点上,并且被 Vue2.0 实例 DOM 方法(如 vm.$appendTo(el))触发。)

在 transition 的封装元素里有个name属性并赋了值 fade ;上面说得很明白了,可以给任何元素和组件添加 entering/leaving 过渡。这个时候去瞄一下CSS的类命名就完全明白了。

本来这里作者是想封装个基本动画的;最后是没必要的,所以需要自己添加,也可以引入动画库和插件。

这里应该还涉及到 transition 的封装组件的状态和处理机制,这才是好玩的东西,也是个重点。下面讲到,点击我吧。

我们先来看一段Vue2.0源码:

// 这是切换CSS的类命名
var autoCssTransition = cached(function (name) {
  return {
    enterClass: (name + "-enter"),
    leaveClass: (name + "-leave"),
    appearClass: (name + "-enter"),
    enterActiveClass: (name + "-enter-active"),
    leaveActiveClass: (name + "-leave-active"),
    appearActiveClass: (name + "-enter-active")
  }
});
  // 开始 enter 的 transition 
  beforeEnterHook && beforeEnterHook(el);
  if (expectsCSS) {
    addTransitionClass(el, startClass);
    addTransitionClass(el, activeClass);
    nextFrame(function () {
      removeTransitionClass(el, startClass);
      if (!cb.cancelled && !userWantsControl) {
        whenTransitionEnds(el, type, cb);
      }
    });
  }
 // 触发 leave 的 transition 
  if (expectsCSS) {
      addTransitionClass(el, leaveClass);
      addTransitionClass(el, leaveActiveClass);
      nextFrame(function () {
        removeTransitionClass(el, leaveClass);
        if (!cb.cancelled && !userWantsControl) {
          whenTransitionEnds(el, type, cb);
        }
      });
    }

从上面看出来添加了startClass接着就是删除了它,初始化元素的状态并集合 transition 的封装组件的状态和处理机制。

看官方解析:

元素封装成过渡组件之后,在遇到插入或删除时,Vue 2.0 将

  • 自动嗅探目标元素是否有 CSS 过渡或动画,并在合适时添加/删除 CSS 类名。
  • 如果过渡组件设置了过渡的 JavaScript 钩子函数,会在相应的阶段调用钩子函数。
  • 如果没有找到 JavaScript 钩子并且也没有检测到 CSS 过渡/动画,DOM 操作(插入/删除)在下一帧中立即执行。(注意:此指浏览器逐帧动画机制,与 Vue 2.0,和 Vue 2.0 的 nextTick 概念不同)

过渡的-CSS-类名

会有 4 个(CSS)类名在 enter/leave 的过渡中切换

  • v-enter: 定义进入过渡的开始状态。在元素被插入时生效,在下一个帧移除。
  • v-enter-active: 定义进入过渡的结束状态。在元素被插入时生效,在 transition/animation 完成之后移除。
  • v-leave: 定义离开过渡的开始状态。在离开过渡被触发时生效,在下一个帧移除。
  • v-leave-active: 定义离开过渡的结束状态。在离开过渡被触发时生效,在 transition/animation完成之后移除。

transition

对于这些在 enter/leave 过渡中切换的类名,v- 是这些类名的前缀。使用 <name="my-transition>" 可以重置前缀,比如 v-enter 替换为 my-transition-enter

v-enter-activev-leave-active 可以控制 进入/离开 过渡的不同阶段,在下面章节会有个示例说明。

不过看起来是很复杂的,我就简单说上几句:

  • v-enter: 就是元素插入时的初始化状态,自己爱咋样定义就咋样定义,不过在下一个帧移除。
  • v-enter-active: 就是和v-enter在下一行代码插入的,不过它是在 transition/animation 完成之后移除的。
  • v-leave: 被触发离开时生效,在下一个帧移除。基本这个鬼是用不到的。
  • v-leave-active: 就是和v-leave在下一行代码插入的,在 transition/animation完成之后移除。

其实如果感觉到很困惑的话,一定是对DOM插入状态和CSS3.0动画的理解不彻底。

有时候去看看源码就很明白很清楚了

@bluezhan
Copy link
Owner Author

bluezhan commented Nov 10, 2016

添加CSS 3.0 动画

在上面的例子里什么都没变就是修改的样式的调用,改用了CSS3.0的 animation 动画。

.bounce-enter-active {  animation: bounce-in .5s; }
.bounce-leave-active {  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); }
}

自定义过渡类名

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

  • enter-class
  • enter-active-class
  • leave-class
  • leave-active-class
    他们的优先级高于普通的类名,这对于 Vue 的过渡系统和其他第三方 CSS 动画库,如 Animate.css 结合使用十分有用。
 <transition
    name="custom-classes-transition"
    enter-active-class="animated tada"
    leave-active-class="animated bounceOutRight"
  >
    <p v-if="show">hello</p>
  </transition>

同时使用 Transitions 和 Animations

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

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

官方没有给出例子,而且不一目了然。

给同一个元素同时设置两种过渡动效animationtransition ;它们的是同时执行的。

    .fade-enter-active ,
    .fade-leave-active {
        animation: bounce-in 1.5s;
        transition: opacity .5s;
    }
   <transition name="fade" type="animation">
         <p v-if="show">我就是个动画</p>
   </transition>

看到没,就是transition的属性type处理了。

@bluezhan
Copy link
Owner Author

JavaScript 钩子

所谓JavaScript 钩子,就是开放过渡效果接口;可以在属性中声明 JavaScript 钩子。

<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>
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) {/*...*/}
}

当只用 JavaScript 过渡的时候, 在 enter 和 leave 中,回调函数 done 是必须的 。 否则,它们会被同步调用,过渡会立即完成。

推荐对于仅使用 JavaScript 过渡的元素添加 v-bind:css="false",Vue 会跳过 CSS 的检测。这也可以避免过渡过程中 CSS 的影响。

这里引进了 Velocity.js
What is Velocity.js?

Velocity专为动画而设计 ,简单易用,功能强大,广泛地被一些主流公司所使用(包括Tumblr, Microsoft and WhatsApp)。并且Velocity 是基于MIT许可协议的开源库。

Velocity模仿了jQuery的语法,可以完美地同jQuery协作,当然也能独立地使用,所以对你来说应该很容易学。由于jQuery使用普遍,我也将向你展示Velocity如何同它协作。

Note: 为了提高你所有动画的性能,只需要简单地将目前jQuery的animate()函数调用换成velocity(),因为Velocity将原来jQueryanimate()函数的语法和功能在velocity()函数内部都做了解释映射,所以能兼容以前animate()的使用方式(velocity()还扩展了animate()的功能)。但即使是这样的一个小改变,也能使你的网站有一个显著的性能提升。

<!--
Velocity works very much like jQuery.animate and is
a great option for JavaScript animations
-->
<script src="https://cdnjs.cloudflare.com/ajax/libs/velocity/1.2.3/velocity.min.js"></script>
<div id="example-4">
  <button @click="show = !show">
    Toggle
  </button>
  <transition
    v-on:before-enter="beforeEnter"
    v-on:enter="enter"
    v-on:leave="leave"
    v-bind:css="false"
  >
    <p v-if="show">
      Demo
    </p>
  </transition>
</div>
new Vue({
  el: '#example-4',
  data: {
    show: false
  },
  methods: {
    beforeEnter: function (el) {
      el.style.opacity = 0
    },
    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 })
    }
  }
})

@bluezhan
Copy link
Owner Author

初始渲染的过渡 - appear 特性

可以通过 appear 特性设置节点的在初始渲染的过渡。
这里默认和进入和离开过渡一样,同样也可以自定义 CSS 类名。

<transition
  appear
  appear-class="custom-appear-class"
  appear-active-class="custom-appear-active-class"
>
  <!-- ... -->
</transition>
<transition
  appear
  v-on:before-appear="customBeforeAppearHook"
  v-on:appear="customAppearHook"
  v-on:after-appear="customAfterAppearHook"
>
  <!-- ... -->
</transition>

多个元素的过渡 - 元素切换 key 特性

 <!-- v-if / v-else -->
<transition>
  <table v-if="items.length > 0">
    <!-- ... -->
  </table>
  <p v-else>Sorry, no items found.</p>
</transition>
<!-- key -->
<transition>
  <button v-bind:key="docState">
    {{ buttonMessage }}
  </button>
</transition>
// ... key 的处理
computed: {
  buttonMessage: function () {
    switch (docState) {
      case 'saved': return 'Edit'
      case 'edited': return 'Save'
      case 'editing': return 'Cancel'
    }
  }
}

过渡模式 - in-out和out-in

<transition name="fade" mode="out-in">
     <button v-if="flag" key="save" v-on:click="flag = !flag">save</button>
   <button v-else key="edit" v-on:click="flag = !flag">edit</button>
 </transition>
  • in-out: 新元素先进行过渡,完成之后当前元素过渡离开。
  • out-in: 当前元素先进行过渡,完成之后新元素过渡进入。

多个组件的过渡

多个组件的过渡很简单很多 - 我们不需要使用 key 特性。相反,我们只需要使用动态组件:

<transition name="component-fade" mode="out-in">
  <component v-bind:is="view"></component>
</transition>
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>'
    }
  }
})

@bluezhan
Copy link
Owner Author

过渡状态

Vue 的过渡系统提供了非常多简单的方法设置进入、离开和列表的动效。那么对于数据元素本身的动效呢,比如:

  • 数字和运算
  • 颜色的显示
  • SVG 节点的位置
  • 元素的大小和其他的属性

所有的原始数字都被事先存储起来,可以直接转换到数字。做到这一步,我们就可以结合 Vue 的响应式和组件系统,使用第三方库来实现切换元素的过渡状态。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant