Web活のロゴ

Web制作Output&初心者のための徹底ガイド

【Gsap】
SVGテキストアニメーション

2024.06.19
はじめに
概要

このコードは、GSAPのScrollTriggerを使用して、スクロールイベントに基づいてSVGテキストの色を動的に変更するアニメーションを設定しています。ユーザーがページをスクロールすると、SVGテキストと黄色のボックスの位置とサイズを取得し、SVGテキストの下端が黄色のボックスの上端に重なるかどうかを確認します。テキストがボックスに重なる場合にはテキストの色を黒に、重ならない場合には白に変更することで、背景に対してテキストが読みやすい色で表示されるようにしています。この動的な色の変更により、スクロールに応じた視覚効果を提供し、ユーザー体験を向上させています。

※本記事の情報は執筆時点のものであり、閲覧時点では変更されている可能性があります。また、ご利用の環境によっては、本記事の内容が正常に動作しないことがあります。最新の情報については、公式サイトなどでご確認ください。
スポンサーリンク

実装内容

パターン1

スクロールとテキストを紐づけて表示し画面を固定

これはSVGテキスト01です

パターン2

startの位置でアニメーションを一回のみ実装

これはSVGテキスト02です

パターン3

パターン1の応用SVGテキストを徐々に白色に変化させる

これはSVGテキスト03ですこれはSVGテキスト03です

コード

全体のコード

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

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    .pin-wrap {
      position: relative;
      /* 子要素の位置を相対的に配置 */
      margin-bottom: 100px;
    }

    .top-img {
      background-image: url(https://picsum.photos/1000/500?random=3);
      /* ランダムな画像を背景画像として設定 */
      background-repeat: no-repeat;
      /* 背景画像の繰り返しを無効化 */
      background-size: cover;
      /* 背景画像を要素の大きさに合わせてカバー */
      background-position: center;
      /* 背景画像を中央に配置 */
      width: 100%;
      /* 幅を100%に設定 */
      height: 50vh;
      /* 高さをビューポートの50%に設定 */
    }

    svg {
      font-size: clamp(10px, 1.8vw, 16px);
      position: absolute;
      /* 絶対位置に配置 */
      left: 50%;
      /* 左端から50%の位置に配置 */
      top: 30%;
      /* 上端から30%の位置に配置 */
      transform: translate(-50%, -50%);
      /* 中央に揃えるために位置を調整 */
    }

    text {
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
    }

    .yellow-box {
      background-color: yellow;
      /* 背景色を黄色に設定 */
      width: 100%;
      /* 幅を100%に設定 */
      height: 50vh;
      /* 高さをビューポートの50%に設定 */
    }

    .txt-black {
      clip-path: inset(0px 0px 0px 0px);
      transition: clip-path 0.3s;
    }
  </style>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.11.2/gsap.min.js"></script>
  <!-- GSAPのメインライブラリを読み込み -->
  <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.11.2/ScrollTrigger.min.js"></script>
  <!-- GSAPのScrollTriggerプラグインを読み込み -->
</head>

<body>
  <h1>スクロールとテキストを紐づけて表示し<br>画面を固定</h1>
  <div class="pin-wrap js-cont-01">
    <svg width="100%" height="100%" xmlns="http://www.w3.org/2000/svg">
      <text x="50%" y="50%" dominant-baseline="middle" text-anchor="middle" font-family="Arial" font-size="3em"
        font-weight="700" fill="#fff" class="svg-text">
        <!-- SVGテキストを中央に配置し、スタイルを設定 -->
        これはSVGテキスト01です
      </text>
    </svg>
    <div class="top-img"></div><!-- 背景画像を表示する要素 -->
    <div class="yellow-box"></div><!-- 黄色のボックス -->
  </div><!-- ラップ要素の終了 -->



  <h1>startの位置でアニメーションを一回のみ実装</h1>
  <div class="pin-wrap js-cont-02">
    <svg width="100%" height="100%" xmlns="http://www.w3.org/2000/svg">
      <text x="50%" y="50%" dominant-baseline="middle" text-anchor="middle" font-family="Arial" font-size="3em"
        font-weight="700" fill="#fff" class="svg-text">
        <!-- SVGテキストを中央に配置し、スタイルを設定 -->
        これはSVGテキスト02です
      </text>
    </svg>
    <div class="top-img"></div><!-- 背景画像を表示する要素 -->
    <div class="yellow-box"></div><!-- 黄色のボックス -->
  </div><!-- ラップ要素の終了 -->



  <div class="pin-wrap js-cont-03">
    <svg width="100%" height="100%" xmlns="http://www.w3.org/2000/svg">
      <g class="svg-text">
        <text x="50%" y="50%" class="txt-white" dominant-baseline="middle" text-anchor="middle" font-family="Arial"
          font-size="3em" font-weight="700" fill="white">
          これはSVGテキスト03です
        </text>
        <text x="50%" y="50%" class="txt-black" dominant-baseline="middle" text-anchor="middle" font-family="Arial"
          font-size="3em" font-weight="700" fill="black">
          これはSVGテキスト03です
        </text>
      </g>
    </svg>
    <div class="top-img"></div>
    <div class="yellow-box"></div>
  </div>
  <script>
    gsap.registerPlugin(ScrollTrigger);

    const text01 = document.querySelector('.js-cont-01 .svg-text');
    gsap.to(text01, {
      y: "400px",
      ease: "none",
      scrollTrigger: {
        trigger: ".js-cont-01",
        start: "top top",
        end: "80% 50%",
        scrub: true,
        pin: true,
        markers: true,
        onUpdate: () => {
          const textRect = text01.getBoundingClientRect();
          const box01 = document.querySelector('.js-cont-01 .yellow-box');
          const boxRect = box01.getBoundingClientRect();

          if (textRect.bottom > boxRect.top) {
            text01.setAttribute('fill', 'black');
          } else {
            text01.setAttribute('fill', '#fff');
          }
        }
      }
    });

    const text02 = document.querySelector('.js-cont-02 .svg-text');
    const box02 = document.querySelector('.js-cont-02 .yellow-box');
    // タイムラインを作成し、ScrollTriggerを設定
    const tl = gsap.timeline({
      scrollTrigger: {
        trigger: ".js-cont-02",  // トリガー要素を指定
        start: "50% 50%",  // アニメーションの開始位置を指定
        markers: true,  // デバッグ用マーカーを表示
      }
    });
    // Y軸方向のアニメーションと色変更をチェイン
    tl.to(text02, {
      y: "400px", ease: "none", onUpdate: function () {
        // テキストとボックスの位置を取得
        const textRect = text02.getBoundingClientRect();
        const boxRect = box02.getBoundingClientRect();

        // テキストの下部がボックスの上部に達した場合に色を黒に変更
        if (textRect.bottom >= boxRect.top) {
          text02.setAttribute('fill', 'black');
        }
      }
    });

    const text03 = document.querySelector('.js-cont-03 .svg-text');
    const blackText = document.querySelector('.txt-black');
    const yellowBox = document.querySelector('.js-cont-03 .yellow-box');

    gsap.to(text03, {
      y: "300px",
      ease: "none",
      scrollTrigger: {
        trigger: ".js-cont-03",
        start: "top top",
        scrub: true,
        pin: true,
        markers: true,
        onUpdate: () => {
          const textRect = text03.getBoundingClientRect();
          const yellowBoxTop = yellowBox.getBoundingClientRect().top;
          let clipValue = 0;

          if (yellowBoxTop < textRect.bottom) {
            clipValue = textRect.bottom - yellowBoxTop;
          }

          blackText.style.clipPath = `inset(0px 0px ${clipValue}px 0px)`;
        }
      }
    });
  </script>
</body>

</html>

ポイント解説

GSAPのpinオプションについて

GSAPのpinオプションは、特定の要素をスクロール位置に固定するために使用されます。例えば、要素がビューポート内に入った時にその位置で固定され、他の要素がスクロールしていく間もその位置に留まるようになります。これにより、ユーザーがスクロールする際に特定のコンテンツを強調表示することができます。

ScrollTriggerのscrubオプションについて

scrubオプションは、スクロールの進行に応じてアニメーションをスムーズに同期させるために使用されます。scrub: trueと設定すると、ユーザーがスクロールする速度と位置に基づいてアニメーションが連動します。これにより、スクロールとアニメーションの動きが自然にリンクします。

clip-path: inset(0px 0px 0px 0px);の解説

clip-pathプロパティは、要素の表示領域を制御するために使用されます。insetは、要素の内側からのオフセットを指定します。例えば、inset(0px 0px 0px 0px)は、要素の全領域を表示することを意味します。この値はアニメーションによって動的に変更され、要素の表示領域が部分的に制御されます。例えば、スクロールに応じて特定の部分だけが表示されたり隠れたりする効果を実現できます。

Gsap onUpdateプロパティ

onUpdate: () => {
  // text01要素の現在の位置とサイズを取得
  const textRect = text01.getBoundingClientRect();
  
  // js-cont-01クラスを持つ要素内のyellow-box要素を取得
  const box01 = document.querySelector('.js-cont-01 .yellow-box');
  
  // box01要素の現在の位置とサイズを取得
  const boxRect = box01.getBoundingClientRect();

  // text01の下端がbox01の上端を超えた場合
  if (textRect.bottom > boxRect.top) {
    // text01のfill属性を黒に設定
    text01.setAttribute('fill', 'black');
  } else {
    // text01のfill属性を白に設定
    text01.setAttribute('fill', '#fff');
  }
}

詳細解説

  1. textRectの取得:
    • text01.getBoundingClientRect()を使用して、text01要素(スクロールに応じてアニメーションしているSVGテキスト)の位置とサイズを取得します。textRectは、この要素の位置(上端、下端、左端、右端など)に関する情報を持つオブジェクトです。
  2. box01の取得:
    • document.querySelector('.js-cont-01 .yellow-box')を使用して、.js-cont-01クラスを持つ要素内の.yellow-boxクラスを持つ要素を取得します。この要素は、黄色のボックスです。
  3. boxRectの取得:
    • box01.getBoundingClientRect()を使用して、box01要素の位置とサイズを取得します。boxRectは、この要素の位置に関する情報を持つオブジェクトです。
  4. 条件分岐:
    • if (textRect.bottom > boxRect.top)は、text01要素の下端がbox01要素の上端を超えたかどうかをチェックします。
    • 超えた場合(つまり、text01box01の上に重なる場合)、text01のテキストの色を黒(fill='black')に設定します。
    • そうでない場合(text01box01の上に重ならない場合)、text01のテキストの色を白(fill='#fff')に設定します。

このコードの目的は、スクロールに応じてSVGテキストの色を動的に変更することです。具体的には、テキストが黄色のボックスの上に重なると黒に、重ならないと白に変わるようにしています。これにより、背景に対してテキストが読みやすくなるような視覚効果を実現しています。

スポンサーリンク

まとめ

  • GsapでSVG画像に対してアニメーションを実施できる
  • これらを組み合わせると、魅力的なWebサイトが構築できる
  • Gsapはpin属性とscrubオプションを理解することが重要
  • さらにCSSを組み合わせることで、より差別化が可能