スクロールに連動したアニメーションの作成(スクラブアニメーション)

説明が難しいですが、スクロールに連動してアニメーションが実行されます。

どんなアニメーションなのか?

通常のスクロールアニメーション

  1. 実行させたい位置までスクロールすると、アニメーションが最初から最後まで実行されます。

今回のスクロールアニメーション(スクラブアニメーション)

  1. 実行させたい位置までスクロールすると、アニメーションが始まります。
    しかしその時点では始まっただけです。終わってはいません。
  2. さらにスクロールすると、アニメーションが続いて実行されます。
  3. さらにスクロールすると、アニメーションが最後まで実行されます。

このようなアニメーションをスクラブアニメーションというらしいです。

スクラブアニメーションのサンプルを作りました

サンプルを見て頂くとニュアンスが理解できると思います。

別ウィンドウで開く

出来れば別ウィンドウで開いて頂きたいです。スクロールするとニュアンスが分かると思います。

サンプルの説明です。

  • スクロールするとメインビジュアルの雲が動く
  • スクロールすると画像一覧が動く
  • スクロールを止めてもアニメーションは動いていて、3秒後に止まります。

実装が難しそうですが、GSAPを使えば簡単です。

スクラブアニメーションの実装

GSAPを設定

GSAPの公式サイトはコチラ。GSAPを読み込みます。CDN or ダウンロードはお好みで。

<script src="https://cdn.jsdelivr.net/npm/gsap@3.12.5/dist/gsap.min.js"></script>

スクロール系のプラグイン「ScrollTrigger」を追加

ScrollTriggerを読み込みます。

<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.11.0/ScrollTrigger.min.js"></script>

プラグインを登録します。

<script>
  gsap.registerPlugin(ScrollTrigger);
</script>

scrubの設定

scrub: true ・・・スクラブアニメーションになります。

gsap.to(".mv__img-layer-wrap", {
  y: -600,
  scrollTrigger: {
    trigger: '.mv__img-layer-wrap', //スクロールしたいID,Classなど
    start: "top top",
    scrub: true, // trueを設定するとスクラブアニメーションになります
  },
});

scrub: 秒(単位は不要) ・・・スクラブアニメーションに加えて、スクロールを停止した時に遅延して止まります。

gsap.to(".mv__img-layer-wrap", {
  y: -600,
  scrollTrigger: {
    trigger: '.mv__img-layer-wrap', 
    start: "top top",
    scrub: 3, // スクラブアニメーション and 3秒後に止まる
  },
});

最後にコード全体を載せておきます。

<!DOCTYPE html>
<html lang="en">

  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script src="https://cdn.jsdelivr.net/npm/gsap@3.12.5/dist/gsap.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.11.0/ScrollTrigger.min.js"></script>
    <title>スクラブアニメーションのサンプル</title>
    <style>
      * {
        padding: 0;
        margin: 0;
        box-sizing: border-box;
      }

      body {
        margin: 0;
        padding: 0;
      }

      img {
        max-width: 100%;
        vertical-align: bottom;
      }

      .container {
        width: 1940px;
        max-width: 100%;
        margin: auto;
        padding: 0 20px;
      }

      .mv {
        position: relative;
        text-align: center;
        overflow: hidden;
        margin-bottom: 100px;
      }

      .mv__img-wrap {
        width: 100%;
      }

      .mv__img {
        width: 100%;
      }

      .mv__img-layer-wrap {
        position: absolute;
        inset: 0;
        z-index: 2;
        width: 100%;
        margin: auto;
      }

      .mv__img-layer {
        width: 100%;
      }

      .sample01 {
        height: 1500px;
        margin: 20px 0 200px 0;
        overflow: hidden;
      }

      .sample01-block01 {
        display: grid;
        gap: 20px;
        grid-template-columns: 1fr 1fr 1fr;
      }

      .sample01-block01__img {
        margin-bottom: 20px;
      }
    </style>
  </head>

  <body>
    <section class="mv">
      <p class="mv__img-wrap">
        <img class="mv__img" src="./img/mv01.webp" alt="メイン画像">
      </p>
      <p class="mv__img-layer-wrap">
        <img class="mv__img-layer" src="./img/mv02.webp" alt="雲">
      </p>
    </section>

    <section class="sample01">
      <div class="container">
        <div class="sample01-block01">
          <p class="sample01-block01__item js-item01">
            <img class="sample01-block01__img" src="./img/img01.webp">
            <img class="sample01-block01__img" src="./img/img04.webp">
            <img class="sample01-block01__img" src="./img/img01.webp">
            <img class="sample01-block01__img" src="./img/img04.webp">
          </p>
          <p class="sample01-block01__item js-item02">
            <img class="sample01-block01__img" src="./img/img02.webp">
            <img class="sample01-block01__img" src="./img/img05.webp">
            <img class="sample01-block01__img" src="./img/img02.webp">
            <img class="sample01-block01__img" src="./img/img05.webp">
          </p>
          <p class="sample01-block01__item js-item03">
            <img class="sample01-block01__img" src="./img/img03.webp">
            <img class="sample01-block01__img" src="./img/img06.webp">
            <img class="sample01-block01__img" src="./img/img03.webp">
            <img class="sample01-block01__img" src="./img/img06.webp">
          </p>
        </div>
      </div>
    </section>

    <script>
      // プラグインを登録
      gsap.registerPlugin(ScrollTrigger);

      document.addEventListener("DOMContentLoaded", () => {

        // 初期設定
        gsap.set(".mv__img-layer-wrap", {
          y: 0,
        });

        gsap.set(".js-item01", {
          y: 0,
        });
        gsap.set(".js-item02", {
          y: 0,
        });
        gsap.set(".js-item03", {
          y: 0,
        });


        // アニメーション実行
        gsap.to(".mv__img-layer-wrap", {
          y: -600,
          // スクロールトリガーの設定
          scrollTrigger: {
            trigger: '.mv__img-layer-wrap',
            start: "top top",
            scrub: 3,
          },
        });

        gsap.to(".js-item01", {
          y: -1000,
          // スクロールトリガーの設定
          scrollTrigger: {
            trigger: ".js-item01",
            scrub: 3,
            start: "top bottom",
          },
        });

        gsap.to(".js-item02", {
          y: -600,
          // スクロールトリガーの設定
          scrollTrigger: {
            trigger: ".js-item02",
            scrub: 3,
            start: "top bottom-=600px",
          },
        });

        gsap.to(".js-item03", {
          y: -1500,
          // スクロールトリガーの設定
          scrollTrigger: {
            trigger: ".js-item03",
            scrub: 3,
            start: "top bottom-=300px",
          },
        });

      });
    </script>
  </body>

</html>
facebook
twitter
line
hatena
pocket