スクロールで見せる(Scroll Reveal)

スクロールに連動して要素をアニメーションさせる実装をFramer Motion / React Spring / GSAP / Intersection Observer APIで比較

🔗 このページはFramer Motion / React Spring / GSAP / Intersection Observer APIを使ったReact実装です。バニラJSでの実装はスクロールアニメーションページを参照してください。
Framer Motion

読み込み中...

tsx
'use client';
import { useRef } from 'react';
import { motion } from 'framer-motion';

export default function ScrollDemo() {
  const containerRef = useRef<HTMLDivElement>(null);

  return (
    <div ref={containerRef} style={{ height: '400px', overflowY: 'auto' }}>
      {/* フェードイン */}
      <motion.div
        initial={{ opacity: 0 }}
        whileInView={{ opacity: 1 }}
        transition={{ duration: 0.6, ease: 'easeOut' }}
        viewport={{ once: true, amount: 0.3, root: containerRef }}
      >
        フェードイン
      </motion.div>

      {/* スライドイン(下から) */}
      <motion.div
        initial={{ opacity: 0, y: 40 }}
        whileInView={{ opacity: 1, y: 0 }}
        transition={{ duration: 0.6, ease: 'easeOut' }}
        viewport={{ once: true, amount: 0.3, root: containerRef }}
      >
        スライドイン
      </motion.div>

      {/* スケールイン */}
      <motion.div
        initial={{ opacity: 0, scale: 0.8 }}
        whileInView={{ opacity: 1, scale: 1 }}
        transition={{ duration: 0.6, ease: 'easeOut' }}
        viewport={{ once: true, amount: 0.3, root: containerRef }}
      >
        スケールイン
      </motion.div>

      {/* スタガー(複数要素を順番に) */}
      {[0, 1, 2].map((i) => (
        <motion.div
          key={i}
          initial={{ opacity: 0, y: 20 }}
          whileInView={{ opacity: 1, y: 0 }}
          transition={{ duration: 0.5, delay: i * 0.1 }}
          viewport={{ once: true, root: containerRef }}
        >
          スタガー {i + 1}
        </motion.div>
      ))}
    </div>
  );
}

jQueryで書くとこうなる

jQueryではスクロールイベントを監視して要素の位置を毎回計算する方法が一般的でしたが、パフォーマンスの問題がありました。Reactでは Framer Motion の whileInView・React Spring の useSpring・GSAP の ScrollTrigger・ブラウザネイティブの IntersectionObserver を使うことで、より効率的にスクロールで見せるアニメーションを実装できます。
js
// scroll イベントで要素の位置を判定してアニメーション
$(window).on('scroll', function () {
  $('.animate-on-scroll').each(function () {
    const elementTop = $(this).offset().top;
    const viewportBottom = $(window).scrollTop() + $(window).height();
    if (elementTop < viewportBottom - 50) {
      $(this).addClass('is-visible');
    }
  });
});

// waypoints プラグインを使った方法
$('.animate-on-scroll').waypoint(function () {
  $(this.element).addClass('is-visible');
}, { offset: '80%' });

// スクロール量に連動して透明度を変える
$(window).on('scroll', function () {
  const scrollTop = $(window).scrollTop();
  const opacity = Math.min(scrollTop / 300, 1);
  $('#hero').css('opacity', 1 - opacity);
});
💡 上記のデモは、React / Next.jsです。Next.jsの基本セットアップは 公式ドキュメントを参照してください。

実装方法の比較

項目Framer MotionReact SpringGSAPIntersection Observer
記述量whileInViewで1要素に数行useSpring + IntersectionObserverで各要素にフックScrollTriggerで各要素に数行(DOMをquerySelectorで取得)useRef + useEffectのセットアップが必要
スクロール量連動useScroll + useTransformで簡単に実現useScrollフック(@react-spring/web)で実現可能ScrollTrigger scrubオプションで簡単に実現自前でscrollイベント + 計算が必要
onceオプションviewport={{ once: true }}で一度だけ実行IntersectionObserver内でdisconnect()を呼ぶtoggleActions: 'play none none none'で一度だけ実行disconnect()で手動解除が必要
パフォーマンス内部でIntersectionObserverを使用IntersectionObserver + 物理スプリング計算高度に最適化されたScrollTrigger APIネイティブAPIで軽量
設定コスト高:framer-motionのインストールが必要高:@react-spring/webのインストールが必要中:gsap + @gsap/reactのインストールが必要低:追加ライブラリ不要

whileInView のポイント

  • viewport={{ once: true }} で一度表示されたら再度アニメーションしない
  • viewport={{ amount: 0.3 }} で要素の30%が見えたときにトリガー
  • • スクロール可能なコンテナ内で使う場合は viewport={{ root: containerRef }} を指定する
  • useScroll + useTransform でスクロール量に応じた連続的な値変化が可能
  • • jQueryの $(window).on('scroll') と違いメインスレッドをブロックしない

🤖 AIプロンプトテンプレート

Next.js + Framer Motion でスクロールで見せるアニメーションを実装してください。
- 要素がビューポートに入ったときにフェードイン+スライドアップ
- whileInView と viewport={{ once: true }} を使用
- 複数の要素が順番に現れるスタガー効果も加える
- TypeScript / Tailwind CSS v4 を使用

⚠️ このプロンプトはあくまでたたき台です。AIの回答はモデルやバージョン、会話の文脈によって毎回異なります。意図通りに動かない場合は、条件を追記・修正してお使いください。

React Spring のポイント

  • useSpring(反応型)と IntersectionObserver を組み合わせると、ビューポート進入時にスプリングアニメーションが起動する
  • @react-spring/web には useScroll フックがあり、スクロール量に連動した値変化を実装できる
  • • 複数要素をアニメーションさせる場合は各要素を独立したコンポーネントに分けてそれぞれ useSpring を持たせる
  • • スプリング物理により、ビューポート進入時のアニメーションにオーバーシュートのような自然な動きを加えられる

🤖 AIプロンプトテンプレート

Next.js + React Spring でスクロールで見せるアニメーションを実装してください。
- useSpring と IntersectionObserver を組み合わせて実装
- 要素がビューポートに入ったときにフェードイン+スライドアップ
- 複数要素を個別コンポーネントに分けてそれぞれフックを持たせる
- 使用ライブラリ: @react-spring/web
- TypeScript / Tailwind CSS v4 を使用

⚠️ このプロンプトはあくまでたたき台です。AIの回答はモデルやバージョン、会話の文脈によって毎回異なります。意図通りに動かない場合は、条件を追記・修正してお使いください。

GSAP ScrollTrigger のポイント

  • gsap.registerPlugin(useGSAP, ScrollTrigger) でプラグインを登録してから使用する
  • scrollTrigger.scroller にコンテナのrefを渡すと、ページ全体ではなく特定コンテナ内のスクロールに連動できる
  • toggleActions: 'play none none none' は「進入時に再生・それ以外は何もしない」という意味で一度だけ実行される(Framer の once: true 相当)
  • scrub: true を追加するとスクロール量に連動してアニメーションが進む(パーラックス効果など)
  • useGSAP でセットアップしたScrollTriggerはコンポーネントのアンマウント時に自動でクリーンアップされる

🤖 AIプロンプトテンプレート

Next.js + GSAP ScrollTrigger でスクロールで見せるアニメーションを実装してください。
- useGSAP フックでスクロールコンテナ内の要素を querySelectorAll で取得
- gsap.fromTo() と ScrollTrigger でビューポート進入時にフェードイン+スライドアップ
- toggleActions: 'play none none none' で一度だけアニメーションを実行
- scroller オプションでカスタムスクロールコンテナを指定する
- 使用ライブラリ: gsap, @gsap/react
- TypeScript / Tailwind CSS v4 を使用

⚠️ このプロンプトはあくまでたたき台です。AIの回答はモデルやバージョン、会話の文脈によって毎回異なります。意図通りに動かない場合は、条件を追記・修正してお使いください。

スクロールで見せる(Scroll Reveal)は、ページのスクロール量や要素のビューポート進入に連動して発動するアニメーション効果。

ランディングページのセクション表示・データの積み上げアニメーション・プログレスバーの進捗表示・パララックス背景など、スクロールをインタラクションとして活用する場面で使われる。

主なバリエーション
  • スクロール進入でフェードイン型(IntersectionObserver)
  • パララックス型(スクロール量に比例して移動)
  • スクロールプログレス型(進行率バー)
  • スクロールトリガーアニメーション型(GSAP ScrollTrigger)
  • スティッキーアニメーション型