# 1.install & import

vue
/* 需要使用到的库 */
npm install animate.css 
npm install gsap
npm install lodash
main.js
import "animate.css"  // 引入到 main.js

# 2.animate 基本使用

vue
// App.vue
<template>
  <div class="app">
    <div><button @click="isShow = !isShow">显示/隐藏</button></div>
    <transition name="neko">
      <h2 class="title" v-if="isShow">Hello World</h2>
    </transition>
  </div>
</template>
<script>
export default {
  data() {
    return {
      isShow: true,
    };
  },
};
</script>
<style scoped>
.title {
  display: inline-block;
  background-color: pink;
}
.animate__flipInY {
  animation-direction: reverse;
}
.neko-enter-active {
  animation: bounceInUp 1s ease-in;
}
.neko-leave-active {
  animation: bounceInUp 1s ease-in reverse;
}
</style>

参考结果:

animate_animation

# 3.animate attribute

动画名参考:https://animate.style

vue
<template>
  <div class="app">
    <div><button @click="isShow = !isShow">显示/隐藏</button></div>
    <!-- 后面的 animate__fadeInDown 动画名基础上必须加上 animate__animated -->
    <!-- 动画名参考网址: -->
    <transition
      enter-active-class="animate__animated animate__fadeInDown"
      leave-active-class="animate__animated animate__flipInY"
    >
      <h2 class="title" v-if="isShow">Hello World</h2>
    </transition>
  </div>
</template>
<script>
export default {
  data() {
    return {
      isShow: true,
    };
  },
};
</script>
<style scoped>
.title {
  display: inline-block;
  background-color: pink;
}
.animate__flipInY {
  animation-direction: reverse;
}
</style>

参考结果:

animate_animation_attribute

  • attribute:自定义优先级更高
    1. enter-from-class
    2. enter-active-class
    3. enter-to-class
    4. leave-from-class
    5. leave-active-class
    6. leave-to-class

# 4.gsap 生命周期钩子

vue
<template>
  <div class="app">
    <div><button @click="isShow = !isShow">显示/隐藏</button></div>
    <transition
      @before-enter="beforeEnter"
      @enter="enter"
      @after-enter="afterEnter"
      @before-leave="beforeLeave"
      @leave="leave"
      @afterLeave="afterLeave"
    >
      <h2 class="title" v-if="isShow">你好呀,李银河</h2>
    </transition>
  </div>
</template>
<script>
export default {
  data() {
    return {
      isShow: true,
    };
  },
  methods: {
    beforeEnter() {
      console.log("beforeEnter");
    },
    enter() {
      console.log("enter");
    },
    afterEnter() {
      console.log("afterEnter");
    },
    beforeLeave() {
      console.log("beforeLeave");
    },
    leave() {
      console.log("leave");
    },
    afterLeave() {
      console.log("afterLeave");
    },
  },
};
</script>
<style scoped>
.title {
  display: inline-block;
}
</style>

参考结果:

animate_gsap_attribute

  • @before-enter:进入动画之前
  • @enter:进入动画
  • @after-enter:进入动画结束
  • @enter-cancelled:进入取消
  • @before-leave:在离开之前
  • @leave:离开动画
  • @after-leave:离开之后
  • @leave-cancelled:离开取消
  • :css:让 Vue 跳过 CSS 的检测,性能略高一些,避免过渡过程中 CSS 规则的影响
  • v-move:对于哪些其他需要移动的节点是没有动画的,就使用新增的 v-move 的 class 来完成动画

# 5.gsap 基本使用

vue
<template>
  <div class="app">
    <div><button @click="isShow = !isShow">显示/隐藏</button></div>
    <transition @enter="enter" @leave="leave" :css="false">
      <h2 class="title" v-if="isShow">Hello World</h2>
    </transition>
  </div>
</template>
<script>
import gsap from "gsap";
export default {
  data() {
    return {
      isShow: true,
    };
  },
  methods: {
    enter(el, done) {
      console.log("enter");
      gsap.from(el, {
        scale: 0,
        color: "red",
        x: 100,
        y: -200,
        onComplete: done, // 需要进行 done 回调
      });
    },
    leave(el, done) {
      console.log("leave");
      gsap.to(el, {
        scale: 0,
        x: 100,
        y: -100,
        onComplete: done,
      });
    },
  },
};
</script>
<style scoped>
.title {
  display: inline-block;
}
</style>

​ 参考结果:

animate_gsap

# 6.gsap 实现数字递增效果

vue
<template>
  <div class="app">
    <input type="number" step="100" v-model="counter" />
    <h2>当前计数: {{ showNumber.toFixed(0) }}</h2>
  </div>
</template>
<script>
import gsap from "gsap";
export default {
  data() {
    return {
      counter: 0,
      showNumber: 0,
    };
  },
  watch: {
    counter(newValue) {
      // 在 duration 的时间段一点点变化
      gsap.to(this, { duration: 1, showNumber: newValue });
    },
  },
};
</script>
<style scoped></style>

参考结果:

![animate_gsap_progressive_increase](animate_gsap_progressive_increase .gif)

# 7.gsap 列表交替

vue
// App
<template>
  <div>
    <button @click="addNum">添加数字</button>
    <button @click="removeNum">删除数字</button>
    <button @click="shuffleNum">数字洗牌</button>
    <transition-group tag="p" name="digitalList">
      <span v-for="item in numbers" :key="item" class="item">
        {{ item }}
      </span>
    </transition-group>
  </div>
</template>
<script>
import _ from "lodash";
export default {
  data() {
    return {
      numbers: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
      numCounter: 10,
    };
  },
  methods: {
    addNum() {
      // 随机插入数字
      this.numbers.splice(this.randomIndex(), 0, this.numCounter++);
    },
    // 随机删除数字
    removeNum() {
      this.numbers.splice(this.randomIndex(), 1);
    },
    shuffleNum() {
      console.log(_); // 打印一下
      this.numbers = _.shuffle(this.numbers);
    },
    randomIndex() {
      // 定义随机插入索引
      return Math.floor(Math.random() * this.numbers.length);
    },
  },
};
</script>
<style scoped>
.item {
  margin-right: 10px;
  display: inline-block;
}
.digitalList-enter-from,
.digitalList-leave-to {
  opacity: 0;
  /* 从下方往上显示 */
  transform: translateY(30px);
}
.digitalList-enter-active,
.digitalList-leave-active {
  transition: all 1s ease;
}
/* 移除是依然占据位置,所以在移除时设置绝对定位 脱离标准流 */
.digitalList-leave-active {
  position: absolute;
}
/* v-move 给需要移动的节点设置动画 */
.digitalList-move {
  /* transform 不写值 交给 Vue 处理就行了 */
  transition: transform 1s ease;
}
</style>

参考结果:

transition-group_gsap_lodash

# 8. 列表的交错过渡案例

vue
// App
<template>
  <div>
    <input v-model="keyword" />
    <transition-group
      tag="ul"
      name="why"
      :css="false"
      @before-enter="beforeEnter"
      @enter="enter"
      @leave="leave"
    >
      <!-- 给 li 绑定:data-index 属性 -->
      <li v-for="(item, index) in showNames" :key="item" :data-index="index">
        {{ item }}
      </li>
    </transition-group>
  </div>
</template>
<script>
import gsap from "gsap";
export default {
  data() {
    return {
      names: [
        "椎名真白",
        "楪祈",
        "樱岛麻衣",
        "牧濑红莉栖",
        "奈亚子",
        "薇尔莉特",
        "土间埋",
        "立华奏",
        "阎魔爱",
        "小鸟游六花",
        "和泉纱雾",
        "香风智乃",
        "伊莉雅",
        "祢豆子",
        "爱蜜莉雅",
        "雷姆",
        "saber",
        "远坂凛",
        "夜刀神十香",
        "四糸乃",
        "时崎狂三",
        "五河琴里",
        "鸢一折纸",
        "亚丝娜",
        "茵蒂克丝",
        "铃音",
        "萌王",
        "入间",
        "梅普鲁",
      ],
      keyword: "",
    };
  },
  computed: {
    showNames() {
      // 筛选 names 数组里是否有从 input value 里面获取的字符串  再判断是否存在 再显示
      return this.names.filter((item) => item.indexOf(this.keyword) !== -1);
    },
  },
  methods: {
    // 进入之前回调
    beforeEnter(el) {
      el.style.opacity = 0;
      el.style.height = 0;
    },
    enter(el, done) {
      gsap.to(el, {
        opacity: 1,
        height: "1.5em",
        delay: el.dataset.index * 0.05, // 根据索引值 * 0.5 的时间依次变化
        onComplete: done,
      });
    },
    leave(el, done) {
      gsap.to(el, {
        opacity: 0,
        height: 0,
        delay: el.dataset.index * 0.05,
        onComplete: done,
      });
    },
  },
};
</script>
<style scoped></style>

参考结果:

transition-group_gsap_list