浅析 Vue 动画

197 阅读3分钟

过渡类名

进入类名 含义 离开类名 含义
v-enter 进入动画开始时的第一帧 v-leave 离开动画开始时的第一帧
v-enter-active 进入动画中 v-leave-active 离开动画中
v-enter-to 进入动画的最后一帧 v-leave-to 离开动画的最后一帧

其顺序可以理解为

  1. v-enter + v-enter-active
  2. v-enter-active + v-enter-to
  3. 元素的默认状态

使用transition创建动画

<template>
  <section>
    <button @click="changeVisible"> show</button>
    <hr>
    <transition name="fade">
      <p v-if="visible">hello world</p>
    </transition>
  </section>
</template>


<script>
  export default {
    data() {
      return {visible: true}
    },
    methods: {
      changeVisible() {
        this.visible = !this.visible
      }
    }
  }
</script>

<style>
  .fade-enter {
    opacity: 0;
    transform: translateX(-100px);
  }

  .fade-enter-active,
  .fade-leave-active {
    transition: all 1s ease;
  }

  .fade-leave-to {
    opacity: 0;
    transform: translateX(100px);
  }
</style>

使用animation创建动画

<template>
  <section>
    <button @click="changeVisible"> show</button>
    <hr>
    <transition name="fade">
      <p v-if="visible">hello world</p>
    </transition>
  </section>
</template>


<script>
  export default {
    data() {
      return {visible: true}
    },
    methods: {
      changeVisible() {
        this.visible = !this.visible
      }
    }
  }
</script>

<style>
  .fade-enter-active {
    animation: fadeAnimation 1s ease;
  }

  .fade-leave-active {
    animation: fadeAnimation 1s ease reverse;
  }

  @keyframes fadeAnimation {
    0% {
      opacity: 0;
      transform: translateX(-100px);
    }
    100% {
      opacity: 1;
      transform: translateX(0);
    }
  }
</style>

Vue 动画钩子

进入钩子 含义 离开钩子 含义
v-on:before-enter 进入前(后面的函数接收一个参数,el) v-on:before-leave 离开前(后面的函数接收一个参数,el)
v-on:enter 进入中(后面的函数接收两个参数,el,done) v-on:leave 离开中(后面的函数接收两个参数,el,done)
v-on:after-enter 进入后(后面的函数接收一个参数,el) v-on:after-leave 离开后(后面的函数接收一个参数,el)
v-on:enter-cancelled 进入被取消(后面的函数接收一个参数,el) v-on:leave-cancelled 离开被取消(后面的函数接收一个参数,el)

多个元素动画

<template>
  <section>
    <transition name="fade" mode="out-in">
      <button @click="tobtnb" v-if="btn === 'a' " key="a">A</button>
      <button @click="tobtna" v-if="btn === 'b' " key="b">B</button>
    </transition>
  </section>
</template>


<script>
  export default {
    data() {
      return {btn: 'a'}
    },
    methods: {
      tobtna() {
        this.btn = 'a'
      },
      tobtnb() {
        this.btn = 'b'
      }
    }
  }
</script>

<style>
  @keyframes fade {
    0% {
      opacity: 0;
      transform: translateX(-100px);
    }
    100% {
      opacity: 1;
      transform: translateX(0);
    }
  }

  .fade-enter-active {
    animation: fade 1s ease;
  }

  .fade-leave-active {
    animation: fade 1s ease reverse;
  }
</style>

注意点:

  1. 不同的元素需要加key
  2. 可以通过mode控制 先进后出 / 先出后进

多组件动画

<template>
  <section>
    <button @click="changeComponentToA">A</button>
    <button @click="changeComponentToB">B</button>
    <hr/>
    <transition name="fade" mode="out-in">
      <component :is="whichComponent"></component>
    </transition>
  </section>
</template>


<script>
  import A from './components/v-a.vue'
  import B from './components/v-b.vue'

  export default {
    data() {
      return {whichComponent: A}
    },
    methods: {
      changeComponentToA() {
        this.whichComponent = A
      },
      changeComponentToB() {
        this.whichComponent = B
      }
    }
  }
</script>

<style>
  @keyframes fade-in {
    0% {
      opacity: 0;
      transform: translateX(-100px);
    }
    100% {
      opacity: 1;
      transform: translateX(0);
    }
  }

  .fade-enter-active {
    animation: fade-in 1s ease;
  }

  .fade-leave-active {
    animation: fade-in 1s ease reverse;
  }
</style>

要实现组件切换动画只需要使用 component标签 + :is属性 即可


列表过度

<template>
  <section>
    <button @click="push">push</button>
    <button @click="remove">remove</button>
    <hr/>
    <transition-group name="fade" class="list" tag="ul">
      <li v-for="(item,keyIndex) in list" :key="keyIndex">{{item}}</li>
    </transition-group>
  </section>
</template>


<script>
  export default {
    data() {
      return {list: [1, 1, 1, 1]}
    },
    methods: {
      push() {
        this.list.push(1)
      },
      remove() {
        this.list.pop()
      }
    }
  }
</script>

<style>
  @keyframes fade {
    0% {
      opacity: 0;
      transform: translateX(-100px);
    }
    100% {
      opacity: 1;
      transform: translateX(0);
    }
  }

  .fade-enter-active {
    animation: fade 1s ease;
  }

  .fade-leave-active {
    animation: fade 1s ease reverse;
  }
</style>

要使用列表过度则需要使用 transition-group 标签即可,他拥有两个属性 name tag(包裹列表的标签,默认为span)