当前位置: 首页>移动开发>正文

浅谈 Vue 的动画原理

1. CSS transition

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

  • 条件渲染 (使用 v-if)
  • 条件展示 (使用 v-show)
  • 动态组件
  • 组件根节点
    举个例子,点击toggle,p 标签显示的 hello 就会淡化消失,再点击就会缓慢显现。
<!-- html -->
<div id="demo">
  <button v-on:click="show = !show">
    Toggle
  </button>
  <transition name="fade">
    <p v-if="show">hello</p>
  </transition>
</div>
// JS
new Vue({
  el: '#demo',
  data: {
    show: true
  }
})
/* CSS */
.fade-enter-active, .fade-leave-active {
  transition: opacity .5s;
}
.fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */ {
  opacity: 0;
}

过渡的类名
进入/离开 & 列表过渡 — Vue.js 中文文档 (bootcss.com)
在进入/离开的过渡中,会有6个class切换:
v-enter-activev-leave-active定义进入、离开过渡(或动画)生效时的状态。,一般会合并写这2个类,因为动的都是一样的。
v-enterv-leave-to表示进入和退出状态,这2个类也会合写。剩下2个一般不用。
p标签在进入动画的过程中经历了什么?

  1. 最开始 p标签 是隐藏起来的,并没有在DOM中出现。
  2. 当点击toggle按钮让它出现时,v-enter上的样式出现在动画的第一帧,并且附加上v-enter-active
  3. 之后便进入了动画/过渡的状态,v-enter被改为v-enter-to
  4. 动画/过渡状态结束之后,v-enter-activev-enter-to被一并删除。
  5. 同理,离出动画也是一样。
    CSS过渡
    CSS-过渡
    常用的过渡都是使用 CSS 过渡。使用方法同上。

2. CSS animation

CSS 动画用法同 CSS 过渡,区别是在动画中 v-enter 类名在节点插入 DOM 后不会立即删除,而是在 animationend 事件触发时删除。

<!-- html -->
<div id="example-2">
  <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>
// JS
new Vue({
  el: '#example-2',
  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);
  }
}

自定义过渡的类名
我们可以通过以下 attribute 来自定义过渡类名:

  • 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 结合使用十分有用。
比如在以下示例中修改enter-active-classleave-active-class的属性可以直接实现想要的动画效果。

<link href="https://cdn.jsdelivr.net/npm/animate.css@3.5.1" rel="stylesheet" type="text/css">

<div id="example-3">
  <button @click="show = !show">
    Toggle render
  </button>
  <transition
    name="custom-classes-transition"
    enter-active-class="animated tada"
    leave-active-class="animated bounceOutRight"
  >
    <p v-if="show">hello</p>
  </transition>
</div>

3. JS 操作动画

可以在 attribute 中声明 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>
// JS
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 transitions/animations 使用,也可以单独使用。
当只用 JavaScript 过渡的时候,在 enterleave 中必须使用 done 进行回调,告诉他们这个阶段有事情要做。否则,它们将被同步调用,过渡会立即完成。
文档之中也有使用 Velocity.js 这个第三方js动画库的例子,这里不再赘述。

4. 多元素动画

对于原生标签可以使用 v-if/v-else。当有相同标签名的元素切换时,需要通过 key attribute 设置唯一的值来标记以让 Vue 区分它们,否则 Vue 为了效率只会替换相同标签内部的内容。为了能够实现动画效果,给在 <transition> 组件中的多个元素设置 key 是一个更好的实践。

<template>
  <div id="example-4">
    <transition name="fade">
      <button key="on" v-if="status === 'off'" @click="status = 'on'">
        ON
      </button>
      <button key="off" v-else @click="status = 'off'">OFF</button>
    </transition>
  </div>
</template>

<script>
export default {
  data() {
    return {
      status: "on",
    };
  },
};
</script>

<style scoped>
.fade-enter-active,
.fade-leave-active {
  transition: all 1s;
}
.fade-enter,
.fade-leave-to {
  opacity: 0;
}
</style>

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

  • in-out:新元素先进行过渡,完成之后当前元素过渡离开。
  • out-in:当前元素先进行过渡,完成之后新元素过渡进入。
    用 out-in 重写之前的开关按钮过渡:
<transition name="fade" mode="out-in">

再应用

// 先进再出
<transition name="fade" mode="in-out">
<style>
.fade-enter-active,.fade-leave-active {
  transition: all 1s;
}
.fade-enter {
  opacity: 0;
  transform:translateX(100px)
}
.fade-leave-to {
  opacity: 0;
  transform:translateX(-100px)
}
#example-4{
  position:relative;
}
button{
 position:absolute;
}
</style>

// 实现轮播效果
<transition name="fade">
// css同上

多个组件的过渡

<transition name="component-fade" mode="out-in">
  <button @click="view='v-a'">A</button>
  <button @click="view='v-b'">B</button>
  <component :is="view"></component>
</transition>
<script>
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>'
    }
  }
})
</script>
<style>
.component-fade-enter-active, .component-fade-leave-active {
  transition: opacity .3s ease;
}
.component-fade-enter, .component-fade-leave-to
/* .component-fade-leave-active for below version 2.1.8 */ {
  opacity: 0;
}
</style>

组件的过渡使用 <component :is="view"></component> ,要更加简单一些。

5. 列表动画

那么要同时渲染整个列表,就要使用 <transition-group> 组件,它的几个特点如下:

  • 不同于 <transition>,它会以一个真实元素呈现:默认为一个 <span>。可以通过 tag attribute 更换为其他元素。
  • 过渡模式不可用,因为我们不再相互切换特有的元素。mode="out-in"不能用。
  • 内部元素总是需要提供唯一的 key attribute 值。
  • CSS 过渡的类将会应用在内部的元素中,而不是这个组/容器本身。
    注意:不能加其它的标签,for循环模版外只能是<transition-group>,需要改变标签的话,可以使用tag
<template>
  <div id="list-demo" class="demo">
    <button v-on:click="add">Add</button>
    <button v-on: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>
</template>

<script>
export default {
  name: "list-demo",
  data() {
    return {
      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);
    },
  },
};
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.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);
}
</style>

https://www.xamrdz.com/mobile/4b91995176.html

相关文章: