【GSAP】ScrollTriggerで実装する、モダンな「横スクロール」アニメーション

近年のWebデザインにおいて、縦にスクロールしていて、あるセクションに来ると突然「横スクロール」に切り替わる表現をよく見かけませんか?

一見すると実装が難しそうに見えるこの「横スクロール演出」ですが、GSAPのScrollTriggerプラグインを使用すれば、非常にシンプルかつ少ないコードで実装することが可能です。

今回は、この横スクロールアニメーションの基本的な仕組みと、実装方法をデモを交えてご紹介します!

デモ動作

See the Pen GSAPのScrollTriggerで横スクロール演出 by mikanbako (@mikanbako) on CodePen.

全画面でのデモ動作は以下のリンクからご確認下さい!(別タブで開かれます)

実装コードの紹介

【HTML】

<!-- スクロール用の上の余白 -->
<div class="l-spacer">
  <p>↓ スクロールしてね ↓</p>
</div>

<!-- 横スクロールさせたい親要素 -->
<div class="p-scroll-wrap js-scroll-wrap">
  <!-- 実際に横に動くコンテナ -->
  <div class="p-scroll-container js-scroll-container">
    <section class="p-scroll-panel">パネル 1</section>
    <section class="p-scroll-panel">パネル 2</section>
    <section class="p-scroll-panel">パネル 3</section>
  </div>
</div>

<!-- スクロール用の下の余白 -->
<div class="l-spacer">
  <p>↑ 横スクロール終わり ↑</p>
</div>

【CSS】

/* 前後のスクロール余白用 */
.l-spacer {
  height: 100vh;
  display: flex;
  justify-content: center;
  align-items: center;
  font-size: 24px;
  font-weight: bold;
  color: #3e3e3e;
}

/* 横スクロールエリアの親(はみ出した部分を隠す) */
.p-scroll-wrap {
  overflow: hidden;
}

/* パネルを横並びにするコンテナ */
.p-scroll-container {
  display: flex;
  width: max-content;
}

/* 各パネルのデザイン */
.p-scroll-panel {
  width: 100vw;
  height: 100vh;
  display: flex;
  justify-content: center;
  align-items: center;
  font-size: 48px;
  font-weight: bold;
  color: #fff;
}

/* パネルごとの色分け */
.p-scroll-panel:nth-child(1) { background-color: #1bb4d4; }
.p-scroll-panel:nth-child(2) { background-color: #fce621; color: #3e3e3e; }
.p-scroll-panel:nth-child(3) { background-color: #c72c4c; }

【JavaScript】

// 【重要】ScrollTriggerプラグインを登録する
gsap.registerPlugin(ScrollTrigger);

// 要素の取得
const wrap = document.querySelector('.js-scroll-wrap');
const container = document.querySelector('.js-scroll-container');

// アニメーションの設定
gsap.to(container, {
  // コンテナを左方向(マイナス方向)に移動させる距離を計算
  x: () => -(container.clientWidth - window.innerWidth),
  ease: "none", // スクロールと連動させるため、イージングはなし
  scrollTrigger: {
    trigger: wrap, // トリガーとなる親要素
    pin: true,     // 画面をその場に固定(ピン留め)
    scrub: 1,      // スクロール量に合わせてアニメーション(1を指定すると少し遅れてなめらかに追従)
    start: "top top", // 要素の上端が画面の上端にきたら開始
    end: () => "+=" + (container.clientWidth - window.innerWidth), // アニメーションの終了位置(スクロール量と移動量を一致させる)
    invalidateOnRefresh: true // 画面幅(リサイズ)変更時に値を再計算する安全設計
  }
});

コードの解説・こだわったポイント

max-content を使った破綻しないCSS設計

横スクロールを実装するには、まずCSSで「パネルが横に並んだ状態」を作ります。 この時、コンテナ(.p-scroll-container)の幅を 300vw のように固定値で指定するのではなく、width: max-content; と指定するのがベストプラクティスです。

/* パネルを横並びにするコンテナ */
.p-scroll-container {
  display: flex;
  width: max-content;
}

これにより、後からパネルの枚数が増減したり、幅の違うパネルが混ざったりしても、自動で全体の幅を計算してくれるため、保守性の高いレイアウトになります。はみ出た部分は親要素に overflow: hidden; をかけて隠しておきましょう。

pinscrub でスクロールをジャックする

ScrollTriggerの肝となるのが、pin: truescrub: 1 の設定です。

pin: true
対象のセクションが画面上部に到達した時点で、一時的に画面の縦スクロールをロック(固定)します。

scrub: 1
ユーザーのスクロール量とアニメーションの進行度をリンクさせます。数値を設定することで、慣性がついたような滑らかな動きになります。

レスポンシブ対応の安全設計

GSAPのプロパティ(xend)には、固定の数値ではなく () => ... という関数形式で値を渡しています。 これに加えて invalidateOnRefresh: true を設定することで、ユーザーがブラウザの画面幅を変えた際に移動距離が自動で再計算され、レイアウト崩れを防ぐことができます!

スクロール量と移動量を一致させる

アニメーションの終了位置を決める end プロパティには、() => "+=" + (container.clientWidth - window.innerWidth) という計算式を指定しています。

gsap.to(container, {
  // コンテナを左方向(マイナス方向)に移動させる距離を計算
  x: () => -(container.clientWidth - window.innerWidth),
  ...
  scrollTrigger: {
    ...
    end: () => "+=" + (container.clientWidth - window.innerWidth), // アニメーションの終了位置(スクロール量と移動量を一致させる)
    ...
  }
});

実際にコンテナが横に移動する距離(x の値)と同じ数値をスクロール量として設定することで、「1px下にスクロールしたら、ぴったり1px横に動く」という、直感的で気持ちの良い1:1の連動を実現しています。

豆知識:clientWidthscrollWidth の使い分け
JavaScriptで要素の幅を取得する際、今回は clientWidth を使用しています。 中身の全体幅を取得する scrollWidth と値は同じになりますが、今回の構造では親要素で overflow: hidden; をかけており、コンテナ自身にスクロールが発生しているわけではないため、「実際の要素の表示幅」を取得する clientWidth を使うのがセマンティクス的に適切です。

【重要】「pin留め」する要素と「動かす」要素は必ず分ける!

GSAPのScrollTriggerを初めて実装する際、一番やりがちなミスが「固定(pin)する要素そのものを、アニメーションで動かそうとしてしまう」ことです。

GSAPは要素を pin する際、裏側で transform などのスタイルを制御して位置を固定しています。もし同じ要素に対して x(横移動)などのアニメーションを同時にかけてしまうと、内部の計算が衝突し、画面がガクガクと震えたりレイアウトが崩壊するバグの原因になります。これは公式ドキュメントでも注意喚起されている鉄則です。

そのため、今回のデモコードのように「トリガーとして画面に固定する親要素(.p-scroll-wrap)」と、「その中で実際に横へ動かす子要素(.p-scroll-container)」をHTML構造の段階でしっかりと分けておくことが、バグを防ぐための非常に重要なポイントになります!

補足:公式ドキュメント

より詳しい仕様や最新の対応状況については、以下の公式ドキュメントをご参照ください。

・GSAP: ScrollTrigger

まとめ

今回はGSAPのScrollTriggerを活用した、横スクロールアニメーションの実装方法をご紹介しました。

一見複雑なアニメーションでも、GSAPの強力な機能を活用しつつ「CSSでの適切なレイアウト設計」と「レスポンシブ時の再計算」をしっかり意識することで、安全で高品質な表現が可能になります。

ユーザーの目を惹きつけるリッチなUIを実装できる技術として、今後も様々なパターンのスクロール連動アニメーションを取り入れていきたいと思います!

Prev
Next