スクロール連動(Scroll Animation)
スクロールに連動して要素をアニメーションさせる実装をFramer MotionとIntersection Observer APIで比較
🔗 このページはFramer Motion / 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 やブラウザネイティブの 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 Motion | Intersection Observer |
|---|---|---|
| 記述量 | whileInViewで1要素に数行 | useRef + useEffectのセットアップが必要 |
| スクロール量連動 | useScroll + useTransformで簡単に実現 | 自前でscrollイベント + 計算が必要 |
| onceオプション | viewport={{ once: true }}で一度だけ実行 | disconnect()で手動解除が必要 |
| パフォーマンス | 内部でIntersectionObserverを使用 | ネイティブAPIで軽量 |
| 設定コスト | 高:framer-motionのインストールが必要 | 低:追加ライブラリ不要 |
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の回答はモデルやバージョン、会話の文脈によって毎回異なります。意図通りに動かない場合は、条件を追記・修正してお使いください。