Udemyセール!最大95%オフ!1,200円~Udemy公式サイト >

この記事にはプロモーションが含まれています。

【コピペOK】横スクロール・スライドアニメーション完全ガイド【GSAP実装】

【コピペOK】横スクロール・スライドアニメーション完全ガイド【GSAP実装】


ケケンタ

縦スクロールで横スクロールを実装したい……

ケケンタ

GSAPでスムーズな横スクロールを作りたい……

今回はこのようなお悩みをお持ちの方へ向けて

Web制作において人気の高いアニメーション効果
横スクロール・スライドアニメーション

GSAPを使用して実装する方法をご紹介します。

3種類の横スクロール効果をご紹介しているので、いままさに「GSAPで横スクロールを実装したい!」という方は丸っとコピペしてどうぞご活用ください!

この記事で紹介する横スクロール・スライドアニメーション
  • 基本的な横スクロール(GSAP ScrollTrigger)
  • スナップスクロール(自動スナップ機能)
  • カードスライド(カード形式の横スクロール)
ケケンタ

特にポートフォリオサイトランディングページでは、GSAPを使用した横スクロール・スライドアニメーションが非常に効果的です。この記事のコードをご活用いただきWeb制作の効率化に繋がれば何よりです。

なお、今回ご紹介するアニメーションはGSAP ScrollTriggerを使用して実装するので、より高度でスムーズなインタラクションを実現できます。

あわせて読みたい



ケケンタ

ケケンタのITブログでは、WebアプリPHPLaravel)やWeb制作WordPressコーディング)について情報を発信しています。
学習中の方や実務をされている方など多くの方にアクセスいただいていますので、ぜひほかの記事も参考にしてみてください!


運動不足、気になっていませんか?

もしプログラミング学習やお仕事で運動不足が気になっているなら

連続屈伸運動がおすすめです!

ボタンにカーソルを合わせるだけ
カウントダウンが始まるタイマーをご用意してみました!

ケケンタ

無理のない範囲で、ぜひ隙間時間に屈伸運動を取り入れてみて下さい!

タイマースタート

3:00

※運動不足だと連続3分で取り組んでもかなり息が切れます
(僕は加えて気分もちょっと悪くなりました……)
絶対にご無理の無い範囲でお取り組みください!



目次

横スクロール・スライドアニメーションとは

横スクロール・スライドアニメーションは、縦スクロールに連動してコンテンツが横方向にスライドするアニメーション効果です。GSAPを使用することで、よりスムーズで高性能なアニメーションを実現できます。

効果的な使用場面

適している場面

  • ポートフォリオサイトの作品ギャラリー
  • ランディングページの特徴紹介
  • 商品カタログの横スクロール表示
  • ストーリーテリングサイト
  • 画像ギャラリーの横スライド

避けるべき場面

  • 重要な情報の表示
  • フォームやナビゲーション
  • アクセシビリティを重視する場面
  • モバイルでの操作性を重視する場面
  • 過度に使用した場合

GSAPの準備

CDNでの読み込み

GSAPを使用するときはライブラリファイルの読み込みが必要です。ここではCDNによる読み込み方法をご紹介します。

以下のコードを<body>閉じタグの直前に配置してください。

<!-- GSAP本体 -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js"></script>

<!-- ScrollTriggerプラグイン -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/ScrollTrigger.min.js"></script>

基本的な横スクロール

① デモ

See the Pen 基本的な横スクロール by ケケンタ (@lgshifbg-the-looper) on CodePen.

この基本的な横スクロールの特徴
  • GSAP ScrollTriggerを使用したスムーズな横スクロール
  • 縦スクロールに連動した自然な動き
  • 高いパフォーマンス
  • すべてのブラウザで対応

② HTML

<!-- 上部コンテンツ -->
<section class="section">
    <div class="container">
        <h2>アイテム 1</h2>
        <p>上部のコンテンツエリアです。</p>
    </div>
</section>

<section class="section">
    <div class="container">
        <h2>アイテム 2</h2>
        <p>横スクロールセクションの前のコンテンツです。</p>
    </div>
</section>

<!-- 横スクロールセクション -->
<section class="horizontal-section">
    <div class="horizontal-container">
        <div class="horizontal-scroll">
            <div class="scroll-item">
                <h3>アイテム 3</h3>
                <p>横スクロールの対象アイテムです。</p>
            </div>
            <div class="scroll-item">
                <h3>アイテム 4</h3>
                <p>縦スクロールに連動して横に移動します。</p>
            </div>
            <div class="scroll-item">
                <h3>アイテム 5</h3>
                <p>GSAPでスムーズなアニメーションを実現。</p>
            </div>
            <div class="scroll-item">
                <h3>アイテム 6</h3>
                <p>ユーザーの操作に自然に反応します。</p>
            </div>
        </div>
    </div>
</section>

<!-- 下部コンテンツ -->
<section class="section">
    <div class="container">
        <h2>アイテム 7</h2>
        <p>横スクロールセクションの後のコンテンツです。</p>
    </div>
</section>

<section class="section">
    <div class="container">
        <h2>アイテム 8</h2>
        <p>下部のコンテンツエリアです。</p>
    </div>
</section>

③ CSS

/* セクション共通スタイル */
.section {
    min-height: 100vh;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 2rem;
    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}

.container {
    max-width: 800px;
    text-align: center;
    color: white;
}

.container h2 {
    font-size: 2.5rem;
    margin-bottom: 1rem;
}

.container p {
    font-size: 1.2rem;
    opacity: 0.9;
}

/* 横スクロールセクション */
.horizontal-section {
    height: 100vh;
    background: #f5f5f5;
    overflow: hidden;
    position: relative;
}

.horizontal-container {
    height: 100%;
    display: flex;
    align-items: center;
}

.horizontal-scroll {
    display: flex;
    gap: 2rem;
    padding: 2rem;
    width: max-content;
}

.scroll-item {
    min-width: 300px;
    background: white;
    padding: 2rem;
    border-radius: 12px;
    box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
    transition: all 0.3s ease;
}

.scroll-item:hover {
    transform: translateY(-10px);
    box-shadow: 0 20px 40px rgba(0, 0, 0, 0.15);
}

.scroll-item h3 {
    color: #333;
    margin-bottom: 1rem;
    font-size: 1.5rem;
}

.scroll-item p {
    color: #666;
    line-height: 1.6;
}

/* レスポンシブ対応 */
@media (max-width: 768px) {
    .scroll-item {
        min-width: 250px;
        padding: 1.5rem;
    }

    .container h2 {
        font-size: 2rem;
    }
}

④ JavaScript

// GSAPプラグインの登録
gsap.registerPlugin(ScrollTrigger);

document.addEventListener('DOMContentLoaded', function() {
    // 横スクロールの実装
    const horizontalScroll = document.querySelector('.horizontal-scroll');
    const scrollItems = document.querySelectorAll('.scroll-item');

    // 横スクロールのアニメーション
    gsap.to(horizontalScroll, {
        x: () => -(horizontalScroll.scrollWidth - window.innerWidth),
        ease: "none",
        scrollTrigger: {
            trigger: ".horizontal-section",
            start: "top top",
            end: () => `+=${horizontalScroll.scrollWidth - window.innerWidth}`,
            scrub: 1,
            pin: true,
            anticipatePin: 1,
            invalidateOnRefresh: true,
        }
    });

    // 各アイテムのフェードインアニメーション
    scrollItems.forEach((item, index) => {
        gsap.fromTo(item, 
            {
                opacity: 0,
                y: 50,
                scale: 0.8
            },
            {
                opacity: 1,
                y: 0,
                scale: 1,
                duration: 0.6,
                delay: index * 0.1,
                ease: "back.out(1.7)",
                scrollTrigger: {
                    trigger: item,
                    start: "top 80%",
                    end: "bottom 20%",
                    toggleActions: "play none none reverse"
                }
            }
        );
    });

    // ホバー効果の強化
    scrollItems.forEach(item => {
        item.addEventListener('mouseenter', function() {
            gsap.to(this, {
                scale: 1.05,
                duration: 0.3,
                ease: "power2.out"
            });
        });

        item.addEventListener('mouseleave', function() {
            gsap.to(this, {
                scale: 1,
                duration: 0.3,
                ease: "power2.out"
            });
        });
    });

    // スクロール進捗の表示(デバッグ用)
    ScrollTrigger.create({
        trigger: ".horizontal-section",
        start: "top top",
        end: () => `+=${horizontalScroll.scrollWidth - window.innerWidth}`,
        onUpdate: (self) => {
            console.log("Scroll Progress:", self.progress);
        }
    });
});

⑤ カスタマイズ例

// スクロール速度の調整
gsap.to(horizontalScroll, {
    x: () => -(horizontalScroll.scrollWidth - window.innerWidth),
    ease: "none",
    scrollTrigger: {
        trigger: ".horizontal-section",
        start: "top top",
        end: () => `+=${horizontalScroll.scrollWidth - window.innerWidth}`,
        scrub: 0.5, // 速度を調整(0.5 = より速い)
        pin: true,
        anticipatePin: 1,
    }
});

// イージングの変更
gsap.to(horizontalScroll, {
    x: () => -(horizontalScroll.scrollWidth - window.innerWidth),
    ease: "power2.inOut", // イージングを変更
    scrollTrigger: {
        trigger: ".horizontal-section",
        start: "top top",
        end: () => `+=${horizontalScroll.scrollWidth - window.innerWidth}`,
        scrub: 1,
        pin: true,
    }
});

// パララックス効果の追加
scrollItems.forEach((item, index) => {
    gsap.to(item, {
        y: -50,
        ease: "none",
        scrollTrigger: {
            trigger: ".horizontal-section",
            start: "top top",
            end: () => `+=${horizontalScroll.scrollWidth - window.innerWidth}`,
            scrub: 1,
            pin: true,
        }
    });
});

スナップスクロール

① デモ

See the Pen スナップスクロール by ケケンタ (@lgshifbg-the-looper) on CodePen.

このスナップスクロールの特徴
  • 自動的にアイテムにスナップ
  • スムーズなスナップ動作
  • ユーザビリティの向上
  • プロフェッショナルな印象

② HTML

<!-- 上部コンテンツ -->
<section class="section">
    <div class="container">
        <h2>アイテム 1</h2>
        <p>上部のコンテンツエリアです。</p>
    </div>
</section>

<section class="section">
    <div class="container">
        <h2>アイテム 2</h2>
        <p>スナップスクロールセクションの前のコンテンツです。</p>
    </div>
</section>

<!-- スナップスクロールセクション -->
<section class="snap-section">
    <div class="snap-container">
        <div class="snap-scroll">
            <div class="snap-item">
                <h3>スナップ 1</h3>
                <p>自動的にスナップする横スクロールです。</p>
            </div>
            <div class="snap-item">
                <h3>スナップ 2</h3>
                <p>各アイテムに自然にスナップします。</p>
            </div>
            <div class="snap-item">
                <h3>スナップ 3</h3>
                <p>スムーズなスナップ動作を実現。</p>
            </div>
            <div class="snap-item">
                <h3>スナップ 4</h3>
                <p>ユーザビリティが向上します。</p>
            </div>
        </div>
    </div>
</section>

<!-- 下部コンテンツ -->
<section class="section">
    <div class="container">
        <h2>アイテム 7</h2>
        <p>スナップスクロールセクションの後のコンテンツです。</p>
    </div>
</section>

<section class="section">
    <div class="container">
        <h2>アイテム 8</h2>
        <p>下部のコンテンツエリアです。</p>
    </div>
</section>

③ CSS

/* セクション共通スタイル */
.section {
    min-height: 100vh;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 2rem;
    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}

.container {
    max-width: 800px;
    text-align: center;
    color: white;
}

.container h2 {
    font-size: 2.5rem;
    margin-bottom: 1rem;
}

.container p {
    font-size: 1.2rem;
    opacity: 0.9;
}

/* スナップスクロールセクション */
.snap-section {
    height: 100vh;
    background: #f5f5f5;
    overflow: hidden;
    position: relative;
}

.snap-container {
    height: 100%;
    display: flex;
    align-items: center;
}

.snap-scroll {
    display: flex;
    gap: 0;
    padding: 2rem 0;
    width: max-content;
}

.snap-item {
    min-width: 100vw;
    height: 60vh;
    background: white;
    margin: 0 1rem;
    border-radius: 12px;
    box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    text-align: center;
    padding: 2rem;
    transition: all 0.3s ease;
}

.snap-item:hover {
    transform: scale(1.02);
    box-shadow: 0 20px 40px rgba(0, 0, 0, 0.15);
}

.snap-item h3 {
    color: #333;
    margin-bottom: 1rem;
    font-size: 2rem;
}

.snap-item p {
    color: #666;
    line-height: 1.6;
    font-size: 1.1rem;
}

/* アクティブアイテムのスタイル */
.snap-item.active {
    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
    color: white;
    transform: scale(1.05);
}

.snap-item.active h3,
.snap-item.active p {
    color: white;
}

/* レスポンシブ対応 */
@media (max-width: 768px) {
    .snap-item {
        min-width: 90vw;
        height: 50vh;
        padding: 1.5rem;
    }

    .snap-item h3 {
        font-size: 1.5rem;
    }
}

④ JavaScript

// GSAPプラグインの登録
gsap.registerPlugin(ScrollTrigger);

document.addEventListener('DOMContentLoaded', function() {
    const snapScroll = document.querySelector('.snap-scroll');
    const snapItems = document.querySelectorAll('.snap-item');

    // スナップスクロールの実装(snapオプションを利用)
    gsap.to(snapScroll, {
        x: () => -(snapScroll.scrollWidth - window.innerWidth),
        ease: "none",
        scrollTrigger: {
            trigger: ".snap-section",
            start: "top top",
            end: () => "+=" + (snapScroll.scrollWidth - window.innerWidth),
            scrub: 1,
            pin: true,
            anticipatePin: 1,
            invalidateOnRefresh: true,
            snap: {
                snapTo: 1 / (snapItems.length - 1),
                duration: {min: 0.2, max: 0.5},
                ease: "power1.inOut"
            },
            onUpdate: (self) => {
                // アクティブクラスの更新
                const progress = self.progress;
                const itemCount = snapItems.length;
                const currentIndex = Math.round(progress * (itemCount - 1));
                snapItems.forEach((item, index) => {
                    if (index === currentIndex) {
                        item.classList.add('active');
                    } else {
                        item.classList.remove('active');
                    }
                });
            }
        }
    });

    // 各アイテムのフェードインアニメーション
    snapItems.forEach((item, index) => {
        gsap.fromTo(item, 
            {
                opacity: 0,
                y: 100,
                scale: 0.8
            },
            {
                opacity: 1,
                y: 0,
                scale: 1,
                duration: 0.8,
                delay: index * 0.2,
                ease: "back.out(1.7)",
                scrollTrigger: {
                    trigger: item,
                    start: "top 80%",
                    end: "bottom 20%",
                    toggleActions: "play none none reverse"
                }
            }
        );
    });

    // ホバー効果の強化
    snapItems.forEach(item => {
        item.addEventListener('mouseenter', function() {
            if (!this.classList.contains('active')) {
                gsap.to(this, {
                    scale: 1.02,
                    duration: 0.3,
                    ease: "power2.out"
                });
            }
        });

        item.addEventListener('mouseleave', function() {
            if (!this.classList.contains('active')) {
                gsap.to(this, {
                    scale: 1,
                    duration: 0.3,
                    ease: "power2.out"
                });
            }
        });
    });
});

⑤ カスタマイズ例

// スナップ速度の調整
gsap.to(snapScroll, {
    x: () => -(snapScroll.scrollWidth - window.innerWidth),
    ease: "none",
    scrollTrigger: {
        trigger: ".snap-section",
        start: "top top",
        end: () => `+=${snapScroll.scrollWidth - window.innerWidth}`,
        scrub: 0.5, // スナップ速度を調整
        pin: true,
        anticipatePin: 1,
    }
});

// スナップ効果の強化
snapItems.forEach((item, index) => {
    gsap.to(item, {
        rotationY: 360,
        ease: "none",
        scrollTrigger: {
            trigger: ".snap-section",
            start: "top top",
            end: () => `+=${snapScroll.scrollWidth - window.innerWidth}`,
            scrub: 1,
            pin: true,
        }
    });
});

// パララックス効果の追加
snapItems.forEach((item, index) => {
    gsap.to(item, {
        y: -50,
        ease: "none",
        scrollTrigger: {
            trigger: ".snap-section",
            start: "top top",
            end: () => `+=${snapScroll.scrollWidth - window.innerWidth}`,
            scrub: 1,
            pin: true,
        }
    });
});

カードスライド

① デモ

See the Pen カードスライド by ケケンタ (@lgshifbg-the-looper) on CodePen.

このカードスライドの特徴
  • カード形式の横スクロール
  • ホバー効果付き
  • レスポンシブ対応

② HTML

<!-- 上部コンテンツ -->
<section class="section">
  <div class="container">
    <h2>カードスライド開始前のセクション</h2>
    <p>ここはカードスライドの前のスペースです。</p>
  </div>
</section>

<!-- カードスライドセクション -->
<section class="card-section">
  <div class="card-scroll">
    <div class="card-item">
      <img src="https://picsum.photos/300/200?random=11" alt="カード1">
      <div class="card-content">
        <h3>カード 1</h3>
        <p>横スクロールするカードです。</p>
      </div>
    </div>
    <div class="card-item">
      <img src="https://picsum.photos/300/200?random=12" alt="カード2">
      <div class="card-content">
        <h3>カード 2</h3>
        <p>ホバーで拡大します。</p>
      </div>
    </div>
    <div class="card-item">
      <img src="https://picsum.photos/300/200?random=13" alt="カード3">
      <div class="card-content">
        <h3>カード 3</h3>
        <p>レスポンシブ対応。</p>
      </div>
    </div>
    <div class="card-item">
      <img src="https://picsum.photos/300/200?random=14" alt="カード4">
      <div class="card-content">
        <h3>カード 4</h3>
        <p>モダンなデザイン。</p>
      </div>
    </div>
    <div class="card-item">
      <img src="https://picsum.photos/300/200?random=15" alt="カード5">
      <div class="card-content">
        <h3>カード 5</h3>
        <p>横スクロールの最後のカード。</p>
      </div>
    </div>
  </div>
</section>

<!-- 下部コンテンツ -->
<section class="section">
  <div class="container">
    <h2>カードスライド終了後のセクション</h2>
    <p>ここはカードスライドの後のスペースです。</p>
  </div>
</section>

③ CSS

.card-section {
  height: 100vh;
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  overflow: hidden;
  position: relative;
  display: flex;
  align-items: center;
}

.card-scroll {
  display: flex;
  gap: 2rem;
  padding: 2rem;
  width: max-content;
}

.card-item {
  min-width: 300px;
  background: white;
  border-radius: 16px;
  overflow: hidden;
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
  transition: all 0.3s ease;
  cursor: pointer;
  display: flex;
  flex-direction: column;
  align-items: center;
  opacity: 0;
  transform: scale(0.8);
}

.card-item img {
  width: 100%;
  height: 200px;
  object-fit: cover;
  transition: transform 0.3s ease;
}

.card-item:hover {
  transform: translateY(-10px) scale(1.05);
  box-shadow: 0 20px 40px rgba(0, 0, 0, 0.15);
}

.card-item:hover img {
  transform: scale(1.1);
}

.card-content {
  padding: 1.5rem;
  width: 100%;
  text-align: center;
}

.card-content h3 {
  color: #333;
  margin-bottom: 0.5rem;
  font-size: 1.3rem;
}

.card-content p {
  color: #666;
  line-height: 1.6;
  font-size: 0.9rem;
}

@media (max-width: 900px) {
  .card-section {
    height: 60vh;
    align-items: flex-start;
  }
  .card-scroll {
    padding: 1rem;
    gap: 1rem;
  }
  .card-item {
    min-width: 220px;
  }
}
@media (max-width: 600px) {
  .card-section {
    height: 40vh;
    align-items: flex-start;
  }
  .card-scroll {
    padding: 0.5rem;
    gap: 0.5rem;
  }
  .card-item {
    min-width: 160px;
  }
  .card-content {
    padding: 0.7rem;
  }
}

④ JavaScript

// GSAPプラグインの登録
gsap.registerPlugin(ScrollTrigger);

document.addEventListener('DOMContentLoaded', function() {
  const cardScroll = document.querySelector('.card-scroll');
  const cardSection = document.querySelector('.card-section');
  const cardItems = document.querySelectorAll('.card-item');

  // 横スクロール分だけ高さを確保
  function setCardSectionHeight() {
    const totalScrollWidth = cardScroll.scrollWidth;
    cardSection.style.height = (totalScrollWidth - window.innerWidth + window.innerHeight) + 'px';
  }
  setCardSectionHeight();
  window.addEventListener('resize', setCardSectionHeight);

  // 横スクロールの実装
  const horizontalTween = gsap.to(cardScroll, {
    x: () => -(cardScroll.scrollWidth - window.innerWidth),
    ease: "none",
    scrollTrigger: {
      trigger: ".card-section",
      start: "top top",
      end: () => "+=" + (cardScroll.scrollWidth - window.innerWidth),
      scrub: 1,
      pin: true,
      anticipatePin: 1,
      invalidateOnRefresh: true,
    }
  });

  // 各カードのフェードインアニメーション(横スクロール進行に連動)
  cardItems.forEach((item, index) => {
    gsap.to(item, {
      opacity: 1,
      scale: 1,
      duration: 0.7,
      delay: index * 0.1,
      ease: "back.out(1.7)",
      scrollTrigger: {
        trigger: item,
        containerAnimation: horizontalTween,
        start: "left 90%",
        end: "left 60%",
        toggleActions: "play none none reverse"
      }
    });
  });

  // ホバー効果の強化
  cardItems.forEach(item => {
    item.addEventListener('mouseenter', function() {
      gsap.to(this, {
        scale: 1.08,
        y: -15,
        duration: 0.3,
        ease: "power2.out"
      });
      const image = this.querySelector('img');
      gsap.to(image, {
        scale: 1.12,
        duration: 0.3,
        ease: "power2.out"
      });
    });
    item.addEventListener('mouseleave', function() {
      gsap.to(this, {
        scale: 1,
        y: 0,
        duration: 0.3,
        ease: "power2.out"
      });
      const image = this.querySelector('img');
      gsap.to(image, {
        scale: 1,
        duration: 0.3,
        ease: "power2.out"
      });
    });
  });
});

まとめ

今回ご紹介した横スクロール・スライドアニメーションは、GSAPを使用することでよりスムーズで高性能なアニメーションを実現できます。

実装のコツ

  • GSAP ScrollTriggerの適切な設定
  • パフォーマンスを考慮した実装
  • スムーズなスクロール動作
  • モバイルデバイスでの操作性
  • アクセシビリティの配慮

避けるべきポイント

  • 過度に複雑なアニメーション
  • 重い処理による遅延
  • ユーザビリティを損なう実装
  • アクセシビリティを無視した設計
  • 過度な使用

おすすめの組み合わせ

  • シンプルなサイト: 基本的な横スクロール
  • モダンなサイト: スナップスクロール
  • ポートフォリオサイト: カードスライド
ケケンタ

特にポートフォリオサイトランディングページでは、GSAPを使用した横スクロール・スライドアニメーションがユーザーエクスペリエンスを大きく左右します。この記事のコードをご活用いただき、より魅力的なWebサイトの制作に繋がれば何よりです!

あわせて読みたい

【コピペOK】横スクロール・スライドアニメーション完全ガイド【GSAP実装】のアイキャッチ画像

この記事が気に入ったら
フォローしてね!

この記事が良いと思ったらシェアしてね!

コメント

コメントする

CAPTCHA


目次