_
2023/12/14 09:00:53660

<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>

动画和过渡执行的每一个时刻都会有一个构造函数会被调用,我们来看一下这些钩子函数都会在什么时候被执行。

before-enter 在元素被插入到 DOM 之前被调用。

enter 在元素被插入到 DOM 之后的下一帧被调用。

after-enter 当进入过渡完成时调用。

enter-cancelled 当进入过渡在完成之前被取消时调用。

before-leave 在 leave 钩子之前调用。

leave 在离开过渡开始时调用。

after-leave 在离开过渡完成、且元素已从 DOM 中移除时调用。

leave-cancelled 仅在 v-show 过渡中可用。

先准备一下基础代码

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>document</title>
</head>
<script src="https://unpkg.com/vue@next"></script>

<body>
  <div id="app"></div>
</body>

<script>
  const app = Vue.createApp({
    data() {
      return {
        message: "hello world!",
        isShow:false
      }
    },
    methods: {
      handelClick(){
        this.isShow=!this.isShow
      }
    },
    template: `
      <Transition>
        <div v-if="isShow" class="div">{{message}}</div>
      </Transition>
      <button @click="handelClick">启动/关闭</button>
    `
  })
  const vm = app.mount("#app")
</script>

<style>
  .div{
    position: absolute;
    left: 0;
    top: 100px;
  }
</style>

</html>

JS实现动画效果

利用这几个钩子函数我们可以自己通过js来执行动画,但是在我们采用js来驱动动画时,我们需要通过设置<Transition>组件属性:css="false",让它禁止采用css动画。

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

在使用js执行动画时我们也可以采用第三方动画库来执行,比如GreenSock Anime.js Motion One等等。

那么,我们这里只是给大家做个简单的演示,我们就简单用js在实现一个移动动画。

我们先给来hello world div元素设置一个position定位,方便我们后面操控left和top坐标。

<div v-if="isShow" class="div">{{message}}</div>
<style>
  .div{
    position: absolute;
    left: 0;
    top: 100px;
  }
</style>

好了,然后我们使用before-enter钩子函数在元素被插入到 DOM 之前设置一个动画开始前的默认值,给元素left默认设置成0。

onBeforeEnter(el){ //他接收一个dom元素
  el.style.left="0"
},

然后在enter钩子函数里来编写js动画。在这个方法里面我们来使用setInterval每20毫秒让元素向左边移动1px,直到移动到100px时停止动画,enter这个方法它接受两个参数dom元素 和 done 回调方法。

在需要停止动画的时候,我们清除一下setInterval 然后还需要调用一下 done方法以此来告诉VUE我们的动画运行结束了

onEnter(el, done){
        let moveTimer = setInterval(() => {
          let offsetX = parseInt(el.offsetLeft)
          offsetX++
          el.style.left=offsetX+'px'
          if(offsetX>=100){
            //动画只想结束了
            done()
            clearInterval(moveTimer)
          }
        },20);
      },

最后在onAfterEnter钩子函数里面我们可以监听到动画执行结束,我们可以在这里面处理一下动画结束后的业务逻辑,这里需要注意的是,enter方法里面动画执行完成之后必须调用done方法告诉vue动画执行完成了onAfterEnter函数才会被调用。

onAfterEnter(el){
  console.log("入场动画结束")
},

Transition组件现在的样子。

template: `
      <Transition
        @before-enter="onBeforeEnter"
        @enter="onEnter"
        @after-enter="onAfterEnter"
      >
        <div v-if="isShow" class="div">{{message}}</div>
      </Transition>
      <button @click="handelClick">启动/关闭</button>
    `

好搞完了这些,我们就可以来运行看一下效果了。

离场动画的编写

我们写好了入场动画,我们可以按照相同方式来写离场动画。主要用到before-leave leave after-leave这三个钩子函数。运行方式以及接收的参数和入场动画是一致的

编写的所有代码:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>document</title>
</head>
<script src="https://unpkg.com/vue@next"></script>

<body>
  <div id="app"></div>
</body>

<script>
  const app = Vue.createApp({
    data() {
      return {
        message: "hello world!",
        isShow:false
      }
    },
    methods: {
      handelClick(){
        this.isShow=!this.isShow
      },
      onBeforeEnter(el){
        el.style.left="0"
      },
      onEnter(el, done){
        let moveTimer = setInterval(() => {
          let offsetX = parseInt(el.offsetLeft)
          offsetX++
          el.style.left=offsetX+'px'
          if(offsetX>=100){
            //动画只想结束了
            done()
            clearInterval(moveTimer)
          }
        },20);
      },
      onAfterEnter(el){
        console.log("入场动画结束")
      },
      onBeforeLeave(el){
        el.style.left = "100px"
      },
      onLeave(el,done){
        let moveTimer = setInterval(() => {
          let offsetX = parseInt(el.offsetLeft)
          offsetX--
          el.style.left = offsetX + 'px'
          if (offsetX <=0) {
            //动画只想结束了
            done()
            clearInterval(moveTimer)
          }
        }, 20);
      },
      onAfterLeave(el){
        console.log("离场动画结束")
      }
    },
    template: `
      <Transition
        @before-enter="onBeforeEnter"
        @enter="onEnter"
        @after-enter="onAfterEnter"
        @before-leave="onBeforeLeave"
        @leave="onLeave"
        @after-leave="onAfterLeave"
      >
        <div v-if="isShow" class="div">{{message}}</div>
      </Transition>
      <button @click="handelClick">启动/关闭</button>
    `
  })
  const vm = app.mount("#app")
</script>

<style>
  .div{
    position: absolute;
    left: 0;
    top: 100px;
  }
</style>

</html>

好了, 那么这节课我们就到这里结束了。

视频讲解