GSAP动画插件-Flip Plugin(一)

本篇文章是GSAP系列的一篇文章,关于web动画,目前我已经发布了以下文章:

web动画篇:

GSAP篇:

其中web动画也可能会用到一定的GSAP动画库的知识。

GSAP是一个非常优秀的动画库,但是不知道为什么中文资料非常少,几乎很少有人会提到GSAP,并且它拥有非常宽裕的使用协议,几乎大部分商业用途都可以免费使用,它的英文文档也非常健全,可以说只要是你见过的web动画,都可以使用GSAP进行实现。


今天的主角是GSAP中的Flip Plugin插件,该插件主要用途是使一个DOM能够平滑的从一个状态转变成另一个状态,具体是什么意思呢?看下面的动图就可以明白:

gsap-flip

这不是一个视频,就是官方给出的一个使用Flip Plugin插件做的一个案例。

我们尝试去掉Flip Plugin插件所生成的效果,看看会有什么样的展示:

gsap-noflip

是不是黯然失色,完全没有过渡动画。

其实说白了Flip这个插件就是实现过渡动画,和CSS属性中的transition有几分相似之处,不过transition要实现过渡有着许多的限制,比如必须要指定需要过渡的属性值。

1. Flip.getState()

Flip.getState( targets:String | Element | Array, vars:Object ) : Object

捕获需要操作的目标尺寸、倾斜角度、旋转程度、不透明度、目标在视口中的位置这一系列的信息,后面可以接一个vars对象,表示额外需要捕捉的信息。

作用就是让Flip知道应该操作哪个目标,以及记录它们的信息,在后面自动填充相关的过渡动画。

2. 几个属性

在Flip插件中,有几个非常重要的属性,接下来一一进行讲解。

2.1 scale

通常情况下,是通过设置元素的宽高来实现元素的缩放,如果你想要更好的动画效果或者更好的性能,可以设置scale: true,它可以通过元素的scale属性来控制元素的大小。

2.2 absolute

在Flex和Grid布局中,如果过渡动画看起来比较生硬,可以设置absolute: true

设置前:

gsap-absolute-false

设置后:

gsap-absolute-true

2.3 nested

类型:Boolean

如果需要过渡的元素上有嵌套的子级元素,设置该属性为true则可以保证嵌套的子级元素的过渡动画也会正常进行显示。

未设置该属性:

gsap-nested-false

设置该属性:

gsap-nested-true

2.4 spin

值类型:Boolean | Number | Function

如果该值为true则在元素的过渡中会额外旋转360°,也可以用数字来表示旋转方向-1会反向旋转一次,而1为正向旋转。

当然也可以通过一个函数来控制旋转方向,比如:

Flip.from(state, {
  spin: (index, target) => {
    if (target.classList.contains("clockwise")) {
      return 1;
    } else if (target.classList.contains("counter-clockwise")) {
      return -1;
    } else {
      return 0;
    }
  }
});

gsap-spin

2.5 zIndex

该属性是用来设置你需要过渡元素的z-index属性,因为有时候因为各种原因,导致过渡的时候元素没有表现在最顶层,被其它元素遮挡了一部分,这个时候你就可以通过设置该属性来将元素调整到最顶层。在过渡开始时会立即设置z-index,而过渡结束后z-index会被复原。

3. 官方示例的完整代码

这是文章开始时的,那个动画的完整代码,因为动画这玩意使用文字描述起来非常的不容易,所以放出完整的代码可能比较直观,重要的地方我都做了注释。

<!DOCTYPE html>
<html lang="zh">
  <head>
    <meta charset="UTF-8" />
    <meta content="width=device-width, initial-scale=1.0" name="viewport" />
    <meta content="ie=edge" http-equiv="X-UA-Compatible" />
    <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.6.0/gsap.min.js"></script>
    <script src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/16327/Flip.min.js"></script>
    <title>Flip Plugin</title>
    <style>
      * {
        box-sizing: border-box;
      }

      body {
        background: black;
        padding: 0;
        margin: 0;
        font-family: "Signika Negative", sans-serif, Arial;
        font-weight: 300;
        height: 100vh;
        overflow: hidden;
      }

      .container {
        display: flex;
        height: 100%;
        width: 100%;
        justify-content: center;
        align-items: center;
        overflow: hidden;
      }

      .container.grid,
      .container.columns {
        align-content: stretch;
        align-items: stretch;
        flex-wrap: wrap;
      }

      .letter {
        text-align: center;
        color: black;
        font-size: 10vmax;
        font-weight: 400;
        display: flex;
        align-items: center;
        justify-content: center;
        padding: 2px 6px;
      }

      .container.grid .letter {
        flex-basis: 50%;
      }

      .container.columns .letter {
        flex-basis: 25%;
      }

      .for,
      .gsap {
        font-size: 5vmax;
        color: white;
      }

      .for {
        padding: 2px 1.6vmax;
        font-weight: 300;
        display: none;
      }

      .gsap {
        padding: 2px 0;
        font-weight: 600;
        display: none;
      }

      .container.final .for,
      .container.final .gsap {
        display: block;
      }

      .F {
        background: rgba(0, 188, 212, 0.7);
      }

      .l {
        background: rgba(40, 150, 255, 0.7);
      }

      .i {
        background: rgba(153, 80, 220, 0.7);
      }

      .p {
        background: rgba(90, 108, 225, 0.7);
      }

      .container.plain .letter {
        background: transparent;
        color: white;
        padding: 0;
      }
    </style>
  </head>
  <body>
    <div class="container final">
      <div class="letter F">F</div>
      <div class="letter l">l</div>
      <div class="letter i">i</div>
      <div class="letter p">p</div>
      <div class="for">for</div>
      <div class="gsap">GSAP</div>
    </div>
    <script>
      gsap.registerPlugin(Flip);

      let layouts = ["final", "plain", "columns", "grid"],
        container = document.querySelector(".container"),
        curLayout = 0; // 现在动画布局

      function nextState() {
        // 获取元素信息
        const state = Flip.getState(".letter, .for, .gsap", {
          props: "color,backgroundColor",
          simple: true,
        }); 

        container.classList.remove(layouts[curLayout]); // 移除旧的类名
        curLayout = (curLayout + 1) % layouts.length; // 增量,如果在末尾,则回到起点,即实现动画循环
        container.classList.add(layouts[curLayout]); // 增加新的类名,使元素动起来

        Flip.from(state, {
          absolute: true, // 在过渡动画运行的过程中,将元素的position设置为true
          stagger: 0.07, // 错开属性,让一个元素的动画相对于前一个元素有延迟
          duration: 0.7, // 持续时间
          ease: "power2.inOut", // 运动轨迹
          spin: curLayout === 0, // 控制元素是否旋转
          simple: true,  // 设置为true时,就不再监听 缩放、旋转、倾斜属性,一般情况下不建议开启,因为性能差别不大
          // 对其它元素设置单独动画
          onEnter: (elements, animation) =>
            gsap.fromTo(
              elements,
              { opacity: 0 },
              { opacity: 1, delay: animation.duration() - 0.1 }
            ),
          // 对其它元素设置单独动画
          onLeave: (elements) => gsap.to(elements, { opacity: 0 }),
        });

        // 为0的时候则表示播放完成
        gsap.delayedCall(curLayout === 0 ? 3.5 : 1.5, nextState);
      }
      // gsap.delayedCall相当于专门针对动画的setTimeout。
      gsap.delayedCall(1, nextState);
    </script>
  </body>
</html>

4. 最后

至于该插件更多的用法可以参考:Flip插件文档

Flip Plugin插件使用起来并不难,就那么几个api,但是将这几个api正确使用,创造出炫酷的动画就是一个比较困难的事情。

所以HTML中动画的制作是一件非常耗时的过程,同时也非常考验一个开发者对于动画的积累程度,因为大多数关于动画的CSS属性在我们平时编写网页的过程中都不会用到。

所以平时积累HTML动画相关的知识就显得尤为重要,万一哪一天你要开发的某个网页就会用到这些知识呢。