Vue 动画详解

377 阅读2分钟

CSS 过渡

  • 文档中的例子
<template>
  <div id="demo">
  <button v-on:click="show = !show">
    Toggle
  </button>
  <transition name="fade">
    <p v-if="show">hello</p>
  </transition>
</div>
</template>

<script>
export default {
  data() {
    return {
      show:true
    }
  }
}
</script>

<style scope>
.fade-enter-active, .fade-leave-active {
  transition: opacity .5s;
}
.fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */ {
  opacity: 0;
}
</style>
  • Vue 封装了用于 transition 动画的 标签: <transition></transition>
    • transition 标签用有个 name 属性,这个属性传入了什么字符串,那么控制动画的类的开头就是什么。
    • 比如: <transition name="x"></transition>
      • 控制动画的类名就是: .x-enter、.x-leave
  • 控制动画的类
    1. .fade-enter
      • 淡入动画开始的瞬间,可以理解为动画的第一帧。.fade-enter 类在第一帧结束后,就会消失,替换为 .fade-enter-to
    2. .fade-enter-to
      • 淡入的过程中,.fade-enter-to 会一直持续到淡入结束
    3. .fade-enter-active
      • 淡入过渡生效的状态。
    4. 同理,.fade-leave, .fade-leave-to, .fade-leave-active 为淡出瞬间、淡出过程中、淡出过渡生效状态 所以,文档中淡入淡出例子相当于以下写法:
<style scope>
    .fade-enter-active,
    .fade-leave-active {
        transition: all .5s;
    }
    .fade-enter {
        opacity: 0;
    }
    .fade-enter-to {
        opacity: 1;
    }
    .fade-leave {
        opacity: 1;
    }
    .fade-leave-to {
        opacity: 0;
    }
</style>
  • 文档中的另一个例子:淡出时做一个侧移
<style scope>
.fade-enter-active{
    transition: all .3s ease;
}
.fade-leave-active {
  transition: all .8s cubic-bezier(1.0, 0.5, 0.8, 1.0);
}
.fade-enter {
    opacity: 0;
    transform: translateX(20px);
}
 .fade-leave-to {
    opacity: 0;
    transform: translateX(20px);
}
</style>

animation 动画

  • 文档中的例子
<template>
    <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>
</template>

<script>
export default {
  data() {
    return {
      show:true
    }
  }
}
</script>

<style scope>
/* 只定义动画过程中的过渡状态 */
.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);
  }
}
</style>
  • 不使用 style 类来控制动画,而是使用 @keyframes 来自定义动画过程
    • 使用 animation 来定义动画更为简洁,只需要定义好淡入和淡出过程中的状态
    • animation 动画的最终状态一定要是正常状态

使用第三方库来制作动画

<link href="https://cdn.jsdelivr.net/npm/animate.css@3.5.1" rel="stylesheet" type="text/css">
<template>
    <div id="example-3">
      <button @click="show = !show">
        Toggle render
      </button>
      <transition
        enter-active-class="animated rollIn"
        leave-active-class="animated rollOut"
      >
            <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>
</template>

<script>
export default {
  data() {
    return {
      show:true
    }
  }
}
</script>
  • 示例中使用了 animate.css 定义好的 rollIn 和 rollOut 类。
  • 我们可以方便的通过 enter-active-classleave-active-class 更换类名来实现不同的 animate 提供的动画效果

多元素动画

  • 按钮切换
<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 scope>
.fade-enter-active,
.fade-leave-active {
  transition: all 1s;
}
.fade-enter {
  opacity: 0;
}
.fade-leave-to {
  opacity: 0;
}
</style>
  • 在一个按钮消失的同时,另一个按钮紧接着出现
    • Vue 提供了 mode 属性来控制这个过程

      • mode="in-out" 新元素先完成过渡,旧元素再消失
      • mode="out-in" 旧元素先完成过渡,新元素再出现
    • mode 属性作用于 transition 标签上

  • 模拟轮播效果
<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 scope>
.fade-enter-active,
.fade-leave-active {
  transition: all 1s;
}
.fade-enter {
  opacity: 0;
  transform: translateX(100px);
}
.fade-leave-to {
  opacity: 0;
  transform: translateX(-100px);
}
button {
  position: absolute;
}
</style>
  • 模拟 tab切换
<template>
  <div>
    <button @click="view = 'A'">A</button>
    <button @click="view = 'B'">B</button>
    <transition name="component-fade" mode="out-in">
      <component :is="view"></component>
    </transition>
  </div>
</template>

<script>
import A from "@/components/A.vue";
import B from "@/components/B.vue";
export default {
  data() {
    return {
      view: "A",
    };
  },
  components: { A, B },
};
</script>

<style scope>
.component-fade-enter-active,
.component-fade-leave-active {
  transition: opacity 0.3s ease;
}
.component-fade-enter,
.component-fade-leave-to {
  opacity: 0;
}
</style>

列表过渡

<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" v-bind:key="item" class="list-item">
        {{ item }}
      </span>
    </transition-group>
  </div>
</template>

<script>
export default {
  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>

<style scope>
.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>

  • <transition-group> 组件
    • 不同于 <transition>,它会以一个真实的元素呈现,默认为一个 <span>。我们可以通过 tag 属性将其更换为其他元素。
    • mode 特性不能应用于 <transition-group>
    • 必须绑定不重复的 key