速報

X.Orgのクライアントライブラリーで17件を修正!Linux端末と画像処理の利用範囲を点検

CATEGORY

【GSAP】ScrollTriggerのpinとscrubで横スクロール演出を作る

※本ページのリンクにはプロモーションが含まれています。

濃い背景に配置したGSAP公式ブランドロゴ

目次

情報確認日:2026年10月15日。GSAP 3系の公式資料を確認して作成した、本サイト独自の実装例です。

縦スクロールに合わせて横長のパネルを動かします。実際のはみ出し量から距離を求め、広い画面だけ演出を有効にする構成と、pinの注意点を説明します。

スポンサーリンク

横へ動く演出を使う範囲を限定する

この実装は、実際のスクロール方向は縦のまま、表示中のパネルを横へ動かす演出です。横方向のスクローラーを監視するScrollTriggerのhorizontal: trueとは違います。まず「縦の操作で横へ見せたい」のか「横にスクロールした量を使いたい」のかを区別してください。

短い作品紹介や工程の紹介には候補になりますが、長い本文や重要な入力フォームを閉じ込める用途には向かない場合があります。この例は操作できるリンクを含まない説明パネルに限定し、小画面と動きを減らす設定では、普通の縦並びへ戻します。

パネル数ではなく、実寸から距離を求める

パネルが四枚だから画面幅の三倍、と固定すると、余白やカード幅を変えたときに最後が余ることがあります。distance()はtrack.scrollWidthからviewport.clientWidthを引き、実際にはみ出している分を求めます。負の移動距離にならないようMath.maxで0を下限にします。

この計算は、trackが横一列として正しく幅を持つことが前提です。CSSで折り返している、子要素が縮んでいる、初期化時はdisplay: noneになっている場合などは、意図した距離を取得できません。GSAPの設定より先に、静止状態の横並びを確認します。

スポンサーリンク

pinする外側と、動かす内側を分ける

viewportを画面へ固定し、内部のtrackだけを移動します。pin対象そのものへ同じTweenで大きなtransformを加える構成は避けます。親へtransformや特殊なレイアウトが付いていると、固定の基準が変わる場合があるため、まず単純な親子関係で動かします。

pinは前後のレイアウトにも関わります。通常はスクロール区間のための空間が確保されます。下の本文が重なるからといって、理由を確認せずpinSpacing: falseを追加しないでください。親がflexになっているなど、配置条件によって期待が変わるため、前後の要素まで含めて確認が必要です。

scrubはスクロールと進行を結び付ける

scrub: 0.6は、スクロール位置に応じた進行へ追い付く時間を指定する設定です。固定の0.6秒で全パネルを表示するという意味ではありません。ease: "none"にして、スクロールの変化と横移動の対応が不自然に偏らないようにします。

endには縦方向のスクロール区間を返す関数を指定しています。ここでは移動距離と同じ数値にしていますが、絶対の正解ではありません。長くすれば操作量が増え、短くすれば横移動が速く感じられます。利用者が内容を読むために必要な量として調整します。

スポンサーリンク

resize時に固定値を使い回さない

xとendを関数にし、invalidateOnRefresh: trueを指定しています。画面幅が変わったときに、最初に求めた距離だけを使い続けないためです。とはいえ、これであらゆる非同期の変更を自動検知できるわけではありません。

画像やフォント、後から追加するパネルで幅が変わる場合は、配置が確定した後にrefreshを検討します。大量のResizeObserverで毎フレーム再計算するより、データ追加の完了など、理由の分かる境目から一度呼ぶ設計を優先すると追いやすくなります。

フォールバックの読み順も設計する

小画面とreduced motionではCSSで縦並びにします。matchMediaの条件を外れたとき、GSAPが追加したtransformとoverflowの状態を戻せるよう、生成処理をそのコールバックへまとめています。小画面用CSSだけ書き、デスクトップで付いたインラインスタイルを残す構成にはしません。

キーボード操作できるリンクをパネル内へ追加すると、画面外にあるリンクへフォーカスが進む問題も考える必要があります。overflowで見えなくしただけでは、操作対象として消えるわけではありません。その要件があるサイトでは、通常の横スクロールや縦並びを含め、操作方法そのものを再評価します。

最後まで到達できるかを確認する

最初の位置だけでなく、最後のパネル右端、次の通常本文、戻る操作、画面の縦横切り替えを見ます。固定ヘッダーがあるならstartを調整し、狭い高さでも内容が欠けないことを確認します。デモの70vhを、そのまま文字量の多い実ページへ流用しないでください。

横スクロール演出は、導入しただけで読みやすくなるわけではありません。通常の縦並びで伝わる内容を基準にし、動きを足した結果として理解がよくなる範囲だけ採用すると、端末差や運用中の変更にも対応しやすくなります。

実装例:HTML・CSS・JavaScript

以下は独立したデモ用のコードです。既存のViteなど、npmパッケージを解決できる開発環境で npm install gsap を実行し、HTMLをbody内、CSSをスタイルシート、JavaScriptをmain.jsへ配置してください。main.jsはHTMLの末尾からtype="module"で読み込みます。WordPressの記事本文へそのまま実行用スクリプトを貼る例ではありません。

<header class="spacer"><h1>制作の流れ</h1></header>
<section class="viewport" aria-label="制作の流れ">
  <div class="track">
    <section class="panel"><h2>調査</h2><p>目的と利用者を知る。</p></section>
    <section class="panel"><h2>設計</h2><p>情報を組み立てる。</p></section>
    <section class="panel"><h2>実装</h2><p>動く形にする。</p></section>
    <section class="panel"><h2>検証</h2><p>使えることを確かめる。</p></section>
  </div>
</section>
<footer class="spacer">ここから通常の本文へ戻ります。</footer>
.spacer { min-height:80vh; display:grid; place-items:center; }
.viewport { overflow-x:auto; }
.track { display:flex; width:max-content; }
.panel { box-sizing:border-box; width:80vw; flex:0 0 80vw; min-height:70vh;
  padding:clamp(1rem,5vw,4rem); background:#abff84; }
.panel:nth-child(even) { background:#fec5fb; }
@media (max-width:899px), (prefers-reduced-motion:reduce) {
  .track { display:block; width:auto; }
  .panel { width:auto; min-height:auto; margin-bottom:1rem; }
}
import { gsap } from "gsap";
import { ScrollTrigger } from "gsap/ScrollTrigger";
gsap.registerPlugin(ScrollTrigger);
const viewport = document.querySelector(".viewport");
const track = document.querySelector(".track");
const distance = () => Math.max(0, track.scrollWidth - viewport.clientWidth);
const mm = gsap.matchMedia();
mm.add("(min-width: 900px) and (prefers-reduced-motion: no-preference)", () => {
  if (!distance()) return;
  gsap.set(viewport, { overflow: "hidden" });
  gsap.to(track, {
    x: () => -distance(), ease: "none",
    scrollTrigger: {
      trigger: viewport, start: "top top", end: () => "+=" + Math.max(1, distance()),
      pin: viewport, scrub: 0.6, invalidateOnRefresh: true
    }
  });
});
// ページ破棄時は mm.revert()。追加画像などで配置が変わった後は必要に応じrefresh()。

スポンサーリンク

サンプルを確認するときの条件

APIの使い方は公式資料と照合しています。掲載コードはGSAP 3.15.0とDOMを模した環境で読み込み・操作を確認しましたが、実ブラウザーでの見た目、タッチ操作、描画性能を保証するものではありません。採用前に対象ブラウザーと実機で確認してください。JavaScriptを無効にした状態でも、必要な内容と操作が残るかを確認します。

参考資料

この記事を書いた人

Ryohei

Webエンジニア / アプリ開発者 / 技術ブログ運営者

福岡のWeb制作会社に勤務するWebエンジニアです。10年以上にわたりWeb制作・開発に携わり、これまでに100以上のWordPressサイトの構築・保守を担当してきました。Webサイト制作に加え、アプリ開発やAIを活用した開発にも取り組み、近年はWebセキュリティにも注力しています。

2015年から技術メディア「Into the Program」を運営し、実務や開発で得た知識をもとに、プログラミングの実装方法やWebサイトの運用・セキュリティ情報を発信しています。

主な分野
WordPress / PHP / JavaScript / アプリ開発 / Webサイトの運用・セキュリティ対策
資格・試験合格
情報セキュリティマネジメント試験
基本情報技術者試験
AWS認定ソリューションアーキテクト アソシエイト
メディア運営
2015年〜

記事の作成・更新について

セキュリティ記事は、ベンダーの公式アドバイザリやCVE・JVNなどの一次情報を参照し、出典と情報確認日を記載します。実環境での検証を行った場合は、その環境と確認範囲を明示します。重要な追加情報や誤りを把握した場合は、内容を確認して追記・訂正します。

スポンサーリンク

関連する記事

おすすめの書籍