GSAPを使ったオープニングアニメーションの作り方

突然ですが、GSAPというライブラリを聞いた事はありますか？
# 今回使うサンプル
![スクリーンショット 2022-10-26 16.05.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116696/08fa9b81-c453-50b3-22f5-15fdfde017e3.png)
- [サンプル](https://gsap-practice-beta.vercel.app/)


# GSAPとは
https://greensock.com/

アニメーションを実装するために特化したライブラリですね。
## GSAPを導入する(NPMで導入する)
```console
$ npm install gsap
```
また、<font color="red">**TypeScriptの型定義ファイルもgsapパッケージに同梱**</font>されているので、`@types`の導入は**不要**です。
## GSAPを導入する(CDNで導入する)
 GSAPをサクッと導入したい場合は下記scriptタグをコピペしてください。

```html
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.11.0/gsap.min.js"></script>
```
# GSAPの基本的な書き方
基本的にGSAPを扱うときは下記のメソッドを使います。

```js
gsap.set();
gsap.to();
```
それぞれ解説していきます。
## ①要素の状態をセットするgsap.set()
まず、はじめにアニメーションさせたい要素にアニメーション前の状態をセットします。

```js
gsap.set(対象のDOM, {
   プロパティ
})
```
## ②要素をある状態に変化させるgsap.to()
アニメーションをここで定義します。

```js
gsap.to(対象のDOM, {
   プロパティ
})
```
## 例(フェード系)
フェード系はCSSで言うところの`opacity`を使います。GSAPでも同じなので早速簡単な例を見ていきましょう。
今回例として取り上げさせていただくのはよくあるローディング画面です。使用しているプロパティは`opacity`と`duration`です。
それ以外のプロパティはご自身で確かめてください。
<p class="codepen" data-height="300" data-default-tab="html,result" data-slug-hash="PoawQMa" data-user="watataku8911" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/watataku8911/pen/PoawQMa">
  gsap-opacity-sample</a> by Watataku (<a href="https://codepen.io/watataku8911">@watataku8911</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

| プロパティ | 説明 | 単位 | デフォルト値（括弧内はデフォルト値） |
|:-:|:-:|:-:|:-:|
| duration  |  時間 | 数値（秒）  | 省略化(1.0)  |
| ease  | 加減速	 | 文字列か関数  | 省略化（power2.inOut）   |
| repeat  | リピート回数  | 数値（秒）  | 省略化(0)  |
| repeatDelay  | リピートするまでの遅延  | 数値 | 省略化(0)  |
| delay  | 遅延秒数  | 数値（秒）  | 省略化(0)  |
| overwrite  | 上書きするか  | 真偽値  | 省略化(false)  |
| paused  | 一時停止した状態にするか  | 真偽値  | 省略化(false)  |
| yoyo  | リピート時に反復挙動にするか  | 真偽値　or "auto"  | 省略化(false)  |

*repeatプロパティを`-1`に指定すると無限に繰り返すになる。*
## 例(スライド系)
スライド系を実装する場合はCSSプロパティの`transform`を使いますよね？
GSAPでも同じです。GSAPではCSSの`transform`を直感的に記述できます。
<p class="codepen" data-height="761.6431884765625" data-default-tab="html,result" data-slug-hash="rNKaJQN" data-user="watataku8911" style="height: 761.6431884765625px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/watataku8911/pen/rNKaJQN">
  gsap-slide-sample</a> by Watataku (<a href="https://codepen.io/watataku8911">@watataku8911</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
※演出上の都合で、全てのボックスに`duration`と`repeat`をかけています

| GSAP | CSS | 説明 |
|:-:|:-:|:-:|
| x: 20  | transform: transformX(20px)  | 水平方向への移動（px）  |
| y: 20  | transform: transformY(20px)  | 垂直方向への移動（px）  |
| rotate: 360  | transform: rotate(360deg)  | 回転角度(角度)  |
| scale: 2  | transform: scale(2, 2)  | 拡大・縮小(1.0が等倍)  |
| scaleX: 2  | transform: scaleX(2)  | 水平方向だけ拡大・縮小  |
| scale:Y 2  | transform: scaleY(2)  | 垂直方向だけ拡大・縮小  |
| xPercent: -50  | transform: translateX(-50%)	  | 水平方向への移動（要素の幅に対する割合） |
| yPercent: -50  | transform: translateY(-50%)	  | 垂直方向への移動（要素の幅に対する割合）  |
## タイムライン
アニメーションを繋げたい時に使います。

```js
const tl = gsap.timeline();
tl.to(".selector1", {
  selector1のアニメーション
}).to(".selector2", {
  selector2のアニメーション
});
```
selector1のアニメーションが終了後、selector2、3とアニメーションする。
## イージング
https://greensock.com/docs/v3/Eases

<hr>

https://ics.media/entry/220822/

# 本題
ここで、ようやく図のようなサンプルを作っていきます。
今回はHTMLとCSSは解説しませんがコードだけ貼っておきます。

```html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="favicon.svg" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vite App</title>
  </head>
  <body>
    <div class="c-loader-bg js-loader-bg">
      <div class="c-loader-dot js-loader-dot-wrap">
        <span></span>
        <span></span>
        <span></span>
      </div>
    </div>
    <!-- ヘッダー -->
    <header class="c-header js-header">
      <div class="c-temp">
        <p class="text">[ GSAP DEMO ]</p>
        <p class="link">
          <a href="https://b-risk.jp/blog/2022/05/gsap/" class="button"
            >Button</a
          >
        </p>
      </div>
    </header>

    <div class="cursor"></div>

    <div class="shapes">
      <div class="shape shape1"></div>
      <div class="shape shape2"></div>
    </div>

    <div class="l-inner">
      <div class="c-mv_title-wrap">
        <div class="c-mv_title">
          <h1 class="c-mv_title-item js-mv_title-item">Hello Would!!</h1>
        </div>
      </div>
    </div>

    <script type="module" src="/main.js"></script>
  </body>
</html>
```
## JS
まず、はじめに要素を取得しておきます。

```js
const jsLoaderBg = ".js-loader-bg";
const jsDot = ".js-loader-dot-wrap > span";
const shapes = ".shape";
const jsText = ".js-mv_title-item span";
const jsHeader = ".js-header";
```
次に、アニメーションをセットし「アニメーション始めま〜す」と宣言します。

```js
gsap.set([shapes, jsText], {
  opacity: 0,
  y: 30,
});
gsap.set(jsHeader, {
  opacity: 0,
  y: -50,
});
gsap.set(jsDot, {
  opacity: 0,
  y: -50,
});
```
最後に、`gsap.to()`でアニメーションの処理を書いていきます。

```js
const tl = gsap.timeline();

/* ドットが左から0.5秒おきに降ってくる */
tl.to(
  jsDot,
  {
    opacity: 1,
    y: 0,
    duration: 0.8,
    delay: 0.8,
    stagger: {
      amount: 0.5, //0.5秒おきに
      from: "start", // 左から
      ease: "power4.inOut",
    },
  }
)

/* 全てのドット(今回3つ)が出尽くした後ドットを消す */
.to(jsDot, {
  opacity: 0,
})
 
/* ドットが消えた後カーテンが上から開く */
.to(jsLoaderBg, {
  y: "100%",
  delay: 0.5,
})

/* カーテンが開くと0.2秒後に下から円が登場 */ 
.to(
  shapes,
    {
      opacity: 1,
      y: 0,
      duration: 0.8,
      stagger: {
        amount: 0.6,
        from: "start",
        ease: "sine.in",
     },
    },
   "+=0.2" // dealy: 0.5と同じ
)

/* 円が登場後「HelloWould」が1文字ずつ登場 */
.to(
   jsText,
    {
      opacity: 1,
      y: 0,
      stagger: {
        amount: 1,
        from: "start",
        ease: "sine.in",
     },
    },
   "-=0.1"  // 前のアニメーションが完了する0.1秒前に実行する書き方
)

/* 文字が登場後ヘッダーが上から参上！！ */
.to(jsHeader, {
  opacity: 1,
  y: 0,
});
```
CSSはGithubをご覧ください。


### 文字列を分割しspanで囲む
これはGSAPと直接関わりはないのですが、`Hello Would`と一文字一文字アニメーションさせるために`spanタグ`で囲みます。

```js
/* 文字列を分割しspanで囲む */
(function () {
  const jsText = document.querySelectorAll(".js-mv_title-item");
  jsText.forEach((target) => {
    let newText = "";
    const text = target.textContent;
    const result = text.split("");
    for (let i = 0; i < result.length; i++) {
      newText += "<span>" + result[i] + "</span>";
    }
    target.innerHTML = newText;
  });
})();
```
https://b-risk.jp/blog/2022/05/gsap/

今回のサンプルの全体的なコード

https://github.com/watataku8911/gsap-practice

# 最後に
このようにGSAPを扱うと複雑なアニメーションの処理も簡単に実装する事ができ、リッチなサイトが簡単に作れるので是非使って見てください。

この記事を読んでGSAPについて少しでもわかっていただけたら幸いです。