官网:https://greensock.com/

背景

一般常用还有Animate.css,但是那个不够灵活,js动画更加灵活,可以动态给组件添加动画,以及动画设置成动态的属性,比如右移100px,这个100px不是写死的而是动态的。

image.png

=====================

安装

  1. npm install gsap

或者-S,一样的

引入

在需要使用的页面中引入
image.png

使用

一般会在Vue 的内置的 transition 组件,的动画生命周期函数里面使用,如下例子

transition 组件 和 动画生命周期可以查看:https://www.yuque.com/yejielin/mypn47/qyoykq#ZBnFH

使用gsap对象:gsap.方法( 目标组件或元素 , { 选项 })

  1. <template>
  2. <div class="app">
  3. <div><button @click="isShow = !isShow">显示/隐藏</button></div>
  4. <!-- 这里是Vue内置组件transition -->
  5. <transition @enter="enter"
  6. @leave="leave"
  7. :css="false">
  8. <h2 class="title" v-if="isShow">Hello World</h2>
  9. </transition>
  10. </div>
  11. </template>
  12. <script>
  13. // 引入
  14. import gsap from 'gsap';
  15. export default {
  16. data() {
  17. return {
  18. isShow: true,
  19. }
  20. },
  21. methods: {
  22. // Vue内置组件transition的动画生命周期,这里是组件出现时执行函数
  23. enter(el, done) { // 这里的参数el就是h2元素
  24. console.log("enter");
  25. // 使用gsap,from是定义开始时的状态
  26. gsap.from(el, { // el正好可以在这里使用
  27. scale: 0,
  28. x: 200,
  29. onComplete: done // 完成时,调用done方法
  30. })
  31. },
  32. // Vue内置组件transition的动画生命周期,这里是组件消失时执行函数
  33. leave(el, done) {
  34. console.log("leave");
  35. // 使用gsap,to是定义结束时的状态
  36. gsap.to(el, {
  37. scale: 0,
  38. x: 200,
  39. onComplete: done
  40. })
  41. }
  42. }
  43. }
  44. </script>
  45. <style scoped>
  46. .title {
  47. display: inline-block;
  48. }
  49. </style>

选项对象

image.png
详细的可以查看 https://greensock.com/docs/v3/GSAP/gsap.from()),里面的Special Properties

====================

效果案例

数字变化

在一些项目中,我们会见到数字快速变化的动画效果,这个动画可以很容易通过gsap来实现:

image.png image.png

  1. <template>
  2. <div class="app">
  3. <input type="number" step="100" v-model="counter">
  4. <h2>当前计数: {{showNumber.toFixed(0)}}</h2>
  5. </div>
  6. </template>
  7. <script>
  8. import gsap from 'gsap';
  9. export default {
  10. data() {
  11. return {
  12. counter: 0,
  13. showNumber: 0
  14. }
  15. },
  16. watch: {
  17. counter(newValue) {
  18. gsap.to(this, {duration: 1, showNumber: newValue})
  19. }
  20. }
  21. }
  22. </script>
  23. <style scoped>
  24. </style>

列表过渡的移动动画

image.png

  1. <template>
  2. <div>
  3. <button @click="addNum">添加数字</button>
  4. <button @click="removeNum">删除数字</button>
  5. <button @click="shuffleNum">数字洗牌</button>
  6. <transition-group tag="p" name="why">
  7. <span v-for="item in numbers" :key="item" class="item">
  8. {{item}}
  9. </span>
  10. </transition-group>
  11. </div>
  12. </template>
  13. <script>
  14. import _ from 'lodash'; // lodash库,增加了很多函数、对象、数组的使用方法
  15. export default {
  16. data() {
  17. return {
  18. numbers: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
  19. numCounter: 10
  20. }
  21. },
  22. methods: {
  23. addNum() {
  24. // this.numbers.push(this.numCounter++) // 这样是添加到最后
  25. this.numbers.splice(this.randomIndex(), 0, this.numCounter++) // 这里是随机插入元素
  26. },
  27. removeNum() {
  28. this.numbers.splice(this.randomIndex(), 1) // 随机删除元素
  29. },
  30. shuffleNum() {
  31. this.numbers = _.shuffle(this.numbers); // .shuffle 创建一个被打乱值的集合
  32. },
  33. randomIndex() {
  34. return Math.floor(Math.random() * this.numbers.length)
  35. }
  36. },
  37. }
  38. </script>
  39. <style scoped>
  40. .item {
  41. margin-right: 10px;
  42. display: inline-block; /* inline一般会有很多限制,建议使用inline-block */
  43. }
  44. /* 这样会插入、删除时,会有往上插入和往下消失的效果 */
  45. .why-enter-from,
  46. .why-leave-to {
  47. opacity: 0;
  48. transform: translateY(30px);
  49. }
  50. .why-enter-active,
  51. .why-leave-active {
  52. transition: all 1s ease;/* ease 是运动曲线,是匀速运动*/
  53. }
  54. /* 名字-move 的class,是vue定义的位移的动画,方便我们操作,不管怎么位移都可以套用这个动画效果*/
  55. .why-move {
  56. transition: transform 1s ease; /* 这里是位移时间1秒,而不是瞬间跳过去完成 */
  57. }
  58. /* 原本移除的时候会有问题,移除动画结束前,元素都会占据DOM空间,因此播放完动画,其他元素才会过来 */
  59. /* 移除的动画时,设置成绝对定位,脱离标准流,就没有这个问题 */
  60. .why-leave-active {
  61. position: absolute;
  62. }
  63. </style>

列表交替案例

image.png

  1. <template>
  2. <div>
  3. <input v-model="keyword">
  4. <transition-group tag="ul" name="why" :css="false"
  5. @before-enter="beforeEnter"
  6. @enter="enter"
  7. @leave="leave">
  8. <li v-for="(item, index) in showNames" :key="item" :data-index="index">
  9. {{item}}
  10. </li>
  11. </transition-group>
  12. </div>
  13. </template>
  14. <script>
  15. import gsap from 'gsap';
  16. export default {
  17. data() {
  18. return {
  19. names: ["abc", "cba", "nba", "why", "lilei", "hmm", "kobe", "james"],
  20. keyword: ""
  21. }
  22. },
  23. computed: {
  24. showNames() {
  25. // 过滤后的显示
  26. return this.names.filter(item => item.indexOf(this.keyword) !== -1)
  27. }
  28. },
  29. methods: {
  30. // 设置初始状态
  31. beforeEnter(el) {
  32. el.style.opacity = 0;
  33. el.style.height = 0;
  34. },
  35. enter(el, done) {
  36. gsap.to(el, {
  37. opacity: 1,
  38. height: "1.5em",
  39. // 延迟执行,每个li都比上一个多0.5秒
  40. // 所有绑定的属性(如例子里li的data-index属性),都会绑定在el.dataset里面
  41. delay: el.dataset.index * 0.5,
  42. onComplete: done
  43. })
  44. },
  45. leave(el, done) {
  46. gsap.to(el, {
  47. opacity: 0,
  48. height: 0,
  49. // 延迟执行,每个li都比上一个多0.5秒
  50. // 所有绑定的属性(如例子里li的data-index属性),都会绑定在el.dataset里面
  51. delay: el.dataset.index * 0.5,
  52. onComplete: done
  53. })
  54. }
  55. }
  56. }
  57. </script>
  58. <style scoped>
  59. /* .why-enter-from,
  60. .why-leave-to {
  61. opacity: 0;
  62. }
  63. .why-enter-active,
  64. .why-leave-active {
  65. transition: opacity 1s ease;
  66. } */
  67. </style>