Vue 动画:CSS 动画原理 & animate.css 库的使用 & 同时使用过渡和动画 & Js 动画与 Velocity.js 的结合 & 多个元素或组件的过渡 & 动画封装

Vue 中 CSS 动画原理

<div id="app">
  <div v-if="show">hello world</div>
  <button @click="handleClick">切换</button>
</div>
<script>
  var vm = new Vue({
    el: '#app',
    data: {
      show: true
    },
    methods: {
      handleClick() {
        this.show = !this.show;
      }
    }
  })
</script>

现在希望 hello world 在隐藏和显示的时候加上渐隐渐现的动画效果。

在想要添加动画的标签外部添加 <transiton></transiton> 标签,Vue 会自动帮助我们构建一个动画的流程。

<transiton></transiton> 包裹的内容有一个过渡的动画效果,可以通过 name 属性给该标签起一个名字。

  • 从隐藏状态变成显示状态:
  • 从显示状态变成隐藏状态:

fade-enter 中的前缀 fade 是 transition 中 name 定义的名字。如果 transition 没有定义 name 属性,前缀默认为 v。即:v-enter,v-enter-active,……

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

<body>
  <div id="app">
    <transition name="fade">
      <div v-if="show">hello world</div>
    </transition>
    <button @click="handleClick">切换</button>
  </div>
  <script>
    var vm = new Vue({
      el: '#app',
      data: {
        show: true
      },
      methods: {
        handleClick() {
          this.show = !this.show;
        }
      }
    })
  </script>
</body>

通过在某一时刻自动向 div 元素上增加一些 class 的底层原理,Vue 帮助我们实现了 CSS 过渡动画效果。

在 Vue 中使用 animate.css 库

使用 @keyframes 实现动画效果

<head>
  <style>
    @keyframes bounce-in {
      0% {
        transform: scale(0)
      }
      50% {
        transform: scale(1.5)
      }
      100% {
        transform: scale(1)
      }
    }

    .fade-enter-active {
      transform-origin: left center;
      animation: bounce-in 1s;
    }
    .fade-leave-active {
      transform-origin: left center;
      animation: bounce-in 1s reverse;
    }
  </style>
</head>

<body>
  <div id="app">
    <transition name="fade">
      <div v-if="show">hello world</div>
    </transition>
    <button @click="handleClick">切换</button>
  </div>
  <script>
    var vm = new Vue({
      el: '#app',
      data: {
        show: true
      },
      methods: {
        handleClick() {
          this.show = !this.show;
        }
      }
    })
  </script>
</body>

不使用 transition 标签默认提供的类名

当使用 transition 标签的时候,设置了 name=“fade”,它就会自动添加 fade-enter-active、fade-leave-active 这样子的 class 的名字。

如果我想自定义 class,不用默认提供的命名规范可以吗?

可以,只需要在 transition 外面自定义 class 名字:

<transition name="fade" enter-active-class="active" leave-active-class="leave">
  <div v-if="show">hello world</div>
</transition>
.active {
  transform-origin: left center;
  animation: bounce-in 1s;
}
.leave {
  transform-origin: left center;
  animation: bounce-in 1s reverse;
}

使用 animate.css 库

<head>
  <!-- …… -->
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.0.0/animate.min.css">
</head>

<body>
  <div id="app">
    <transition name="fade" enter-active-class="animate__animated animate__swing"
      leave-active-class="animate__animated animate__shakeY">
      <div v-if="show">hello world</div>
    </transition>
    <button @click="handleClick">切换</button>
  </div>
  <script>
    var vm = new Vue({
      el: '#app',
      data: {
        show: true
      },
      methods: {
        handleClick() {
          this.show = !this.show;
        }
      }
    })
  </script>
</body>
  • 引入 animate.css 动画库(教程:https://animate.style/#usage

    复杂的动画不再需要自己去写,直接用库里提供的动画效果即可。

  • 必须使用自定义 class 名字的形式来使用 animate.css

  • class 类里面必须包含 animate__animated 这样一个具体的类,然后根据需求不同,把动画效果的名字写在第二个类名的位置。( enter-active-class="animate__animated animate__swing")

    动画效果的名字可以在官网进行复制(点击下图中的复制符号即可)在这里插入图片描述

在 Vue 中同时使用过渡和动画

如何在第一次加载显示的时候也具备动画的效果?

上面的例子中,只是在点击按钮的时候触发动画效果。如果想要在第一次加载显示的时候也具备动画的效果,该如何去做?

增加一个自定义的 class 名字 appear-active-class (也要同时增加 appear 属性):

<transition 
	name="fade" 
	appear 
	enter-active-class="animate__animated animate__swing"
    leave-active-class="animate__animated animate__shakeY" 
    appear-active-class="animate__animated animate__swing"
>
  <div v-if="show">hello world</div>
</transition>

同时使用过渡和动画

当 @keyframe 形式的动画 (animate__swing) 执行的是 1s,transition 动画执行的时间是 3s 时,整个动画效果会按哪个时间显示呢?

可以手动设置:(type="transition"

<head>
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.0.0/animate.min.css">
  <style>
    .fade-enter,
    .fade-leave-to {
      opacity: 0;
    }
    .fade-enter-active,
    .fade-leave-active {
      transition: opacity 3s
    }
  </style>
</head>

<body>
  <div id="app">
    <transition type="transition" name="fade" appear
      enter-active-class="animate__animated animate__swing fade-enter-active"
      leave-active-class="animate__animated animate__shakeY fade-leave-active"
      appear-active-class="animate__animated animate__swing">
      <div v-if="show">hello world</div>
    </transition>
    <button @click="handleClick">切换</button>
  </div>
  <script>
    var vm = new Vue({
      el: '#app',
      data: {
        show: true
      },
      methods: {
        handleClick() {
          this.show = !this.show;
        }
      }
    })
  </script>
</body>

上面是按照 transition 过渡动画的时长作为总时长,能否自定义动画的时长呢?

自定义动画的时长

<transition :duration="5000" name="fade" appear
  enter-active-class="animate__animated animate__swing fade-enter-active"
  leave-active-class="animate__animated animate__shakeY fade-leave-active"
  appear-active-class="animate__animated animate__swing">
  <div v-if="show">hello world</div>
</transition>

:duration="5000":5000 指的是毫秒。
点击按钮, 1s 动画就执行完了,样式依然存在,5s 后样式才会被清除。
:duration="{enter: 5000, leave: 10000}":出场动画 10s 后被移除,入场动画 5s 后 class 被清除。


Vue 中的 Js 动画与 Velocity.js 的结合

上面的例子都是通过 CSS 实现的动画效果,那有没有办法通过 JS 实现动画效果呢?

有的,Vue 提供了一些 JS 动画的钩子。

JS 动画

  • 入场动画
    • @before-enter 当元素即将显示的一瞬间,Vue 会自动触发其绑定的事件。
    • @enter 当 @before-enter 被触发结束之后,就要真正运行动画。当真正开始运行动画效果时,其所有的动画会写在 @enter 钩子对应的回调函数里面。
    • @after-enter 当 done 被调用后,Vue 会触发 @after-enter 事件。
  • 出场动画
    • @before-leave
    • @leave
    • @after-leave
<div id="app">
  <transition name="fade" @before-enter="handleBeforeEnter" @enter="handleEnter" @after-enter="handleAfterEnter">
    <div v-show="show">hello world</div>
  </transition>
  <button @click="handleClick">切换</button>
</div>
<script>
  var vm = new Vue({
    el: '#app',
    data: {
      show: true
    },
    methods: {
      handleClick() {
        this.show = !this.show;
      },
      handleBeforeEnter(el) { // el 指的是动画包裹的div标签  (当元素即将显示的一瞬间,Vue会自动触发handleBeforeEnter事件)
        el.style.color = "red";
      },
      handleEnter(el, done) {
        // 接收两个参数:第一个参数 el,div的元素;第二个参数 done,回调函数  (当@before-enter被触发结束之后,就要真正运行动画,当真正开始运行动画效果时,其所有的动画会写在@enter钩子对应的回调函数handleEnter里面)
        setTimeout(() => {
          el.style.color = "green";
          // done(); 为了使效果明显一点,写在下面:4s时让动画结束
        }, 2000)
        setTimeout(() => {
          done(); // 当动画结束时,要手动调用 done 这个回调函数,相当于告诉 Vue 动画已经执行完了
        }, 4000)
      },
      handleAfterEnter(el) { //当 done 被调用后,Vue 会触发 @after-enter 事件
        el.style.color = "black";
      }
    }
  })
</script>

JS 常用动画库:Velocity.js

下载 velocity.js 后用 <script></script> 标签引入。

var vm = new Vue({
  el: '#app',
  data: {
    show: true
  },
  methods: {
    handleClick() {
      this.show = !this.show;
    },
    handleBeforeEnter(el) {
      el.style.opacity = 0;
    },
    handleEnter(el, done) {
      Velocity(el, {opacity: 1}, {duration: 1000, complete: done})
    },
    handleAfterEnter(el) {
      console.log("动画结束")
    }
  }
})

在这里插入图片描述

Vue 中多个元素或组件的过渡动画

多个元素的过渡动画

.v-enter, .v-leave-to {
  opacity: 0;
}

.v-enter-active, .v-leave-active {
  transition: opacity 1s;
}
<div id="app">
 <transition>
    <div v-if="show" key="hello">hello world</div>
    <div v-else key="bye">bye world</div>
  </transition>
  <button @click="handleClick">切换</button>
</div>
<script>
  var vm = new Vue({
    el: '#app',
    data: {
      show: true
    },
    methods: {
      handleClick() {
        this.show = !this.show;
      }
    }
  })
</script>

transition 提供了 mode 属性:

<transition mode="in-out">    // 先显示再隐藏

多个组件的过渡动画

<div id="app">
  <transition mode="in-out">
    <child v-if="show"></child>
    <child-one v-else></child-one>
  </transition>
  <button @click="handleClick">切换</button>
</div>
<script>
  Vue.component('child', {
    template: '<div>child</div>'
  })
  Vue.component('child-one', {
    template: '<div>child-one</div>'
  })
  var vm = new Vue({
    el: '#app',
    data: {
      show: true
    },
    methods: {
      handleClick() {
        this.show = !this.show;
      }
    }
  })
</script>
  • 也可以通过动态组件来实现:
<div id="app">
  <transition mode="in-out">
    <component :is="type"></component>
  </transition>
  <button @click="handleClick">切换</button>
</div>
<script>
  Vue.component('child', {
    template: '<div>child</div>'
  })
  Vue.component('child-one', {
    template: '<div>child-one</div>'
  })
  var vm = new Vue({
    el: '#app',
    data: {
      type: 'child'
    },
    methods: {
      handleClick() {
        this.type = this.type === 'child' ? 'child-one' : 'child';
      }
    }
  })
</script>

Vue 中的列表过渡

<div id="app">
  <div v-for="item of list" :key="item.id">
    {{item.title}}
  </div>
  <button @click="handleBtnClick">Add</button>
</div>
<script>
  var count = 0;
  var vm = new Vue({
    el: '#app',
    data: {
      list: []
    },
    methods: {
      handleBtnClick() {
        this.list.push({
          id: count++,
          title: 'hello world'
        })
      }
    }
  })
</script>

👆每点击一个 Add 就会增加一条数据。
现在我们想往列表中增加内容时实现列表过渡的效果:

  • <transition-group></transition-group>:相当于在每一个 div 外层都加了一个 <transition></transition>
    <transition-group>
      <div v-for="item of list" :key="item.id">
        {{item.title}}
      </div>
    </transition-group>
    
  • .v-enter, .v-leave-to {
      opacity: 0;
    }
    
    .v-enter-active, .v-leave-active {
      transition: opacity 1s
    }
    

如果能不用 index 作为 key 值,就不要用!!!


Vue 中的动画封装

如果我们觉得某一个动画效果用的比较多,可以对这个动画效果进行封装。

.v-enter, .v-leave-to {
  opacity: 0;
}

.v-enter-active, .v-leave-active {
  transition: opacity 1s
}
<div id="app">
  <fade :show="show">
    <div>hello world</div>
  </fade>
  <button @click="handleBtnClick">切换</button>
</div>
<script>
  Vue.component('fade', {
    props: ['show'],
    template: `
      <transition>
        <slot v-if="show"></slot>
      </transition>
    `
  })
  var vm = new Vue({
    el: '#app',
    data: {
      show: false
    },
    methods: {
      handleBtnClick() {
        this.show = !this.show;
      }
    }
  })
</script>

👆 已经把动画效果封装到了 <fade></fade> 组件中,如果想再用这个动画效果,可以直接复制 fade 组件的内容,传不同的 dom 元素进去:

<fade :show="show">
  <h1>hello world</h1>
</fade>

样式也可一起封装到动画组件中。
不用 css 动画,转而使用 js 动画:

Vue.component('fade', {
  props: ['show'],
  template: `
    <transition @before-enter="handleBeforeEnter" @enter="handleEnter">
      <slot v-if="show"></slot>
    </transition>
  `,
  methods: {
    handleBeforeEnter(el) {
      el.style.color = "red";
    },
    handleEnter(el, done) {
      setTimeout(() => {
        el.style.color = "green";
        done();
      }, 2000)
    }
  }
})

可以完整的把所有的动画的实现封装在一个组件里面。


更多 more:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章