「他社サイトみたいに、スクロールするとふわっと要素が出てくる動きをつけたい」——そう思って調べ始めたものの、jQueryの古い記事やGSAPの英語ドキュメントに当たって手が止まっていませんか。ライブラリを入れるべきなのか、素のJavaScriptで足りるのか、その判断がつかないのが一番のつまずきどころです。
この記事では、スクロールアニメーション実装の二大選択肢である「IntersectionObserver」と「GSAP ScrollTrigger」を、使い分けの基準とコード付きで整理します。
まず結論:8割はIntersectionObserverで足りる
企業サイトやLPでよくある「スクロールで要素がフェードインする」程度の演出なら、ブラウザ標準のIntersectionObserverで十分です。追加のライブラリは不要、ファイルサイズはゼロ。
一方で、スクロール量に連動して動きが変化する表現(進むと戻る、スクロールを止めると動きも止まる)を作りたい場合は、GSAP ScrollTriggerが圧倒的に楽です。
| やりたいこと | おすすめ |
|---|---|
| 画面に入ったら1回フェードイン | IntersectionObserver |
| カウントアップを1回だけ発火 | IntersectionObserver |
| スクロール量に比例して要素が横移動 | ScrollTrigger |
| 要素を画面に固定して中身を切り替える | ScrollTrigger |
| 複数要素を時間差で連続再生 | ScrollTrigger |
判断軸はシンプルで、「入ったら発火(トリガー)」か「スクロールと連動(スクラブ)」か。前者なら標準API、後者ならGSAPです。
IntersectionObserverの実装手順
1. CSSで「動く前」と「動いた後」を定義する
JavaScriptはクラスを付け外しするだけにして、見た目はCSSに任せると保守が楽になります。
.fade-up {
opacity: 0;
transform: translateY(24px);
transition: opacity .6s ease, transform .6s ease;
}
.fade-up.is-visible {
opacity: 1;
transform: translateY(0);
}
移動距離は20〜30px、時間は0.4〜0.8秒あたりが自然です。100px動かすと「重いサイト」に見えるので控えめに。
2. JavaScriptで監視する
const targets = document.querySelectorAll('.fade-up');
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (!entry.isIntersecting) return;
entry.target.classList.add('is-visible');
observer.unobserve(entry.target); // 1回だけ発火
});
}, {
threshold: 0.15, // 15%見えたら発火
rootMargin: '0px 0px -10% 0px' // 少し早めに発火させる
});
targets.forEach((el) => observer.observe(el));
thresholdは0.1〜0.2、rootMarginの下辺を-10%程度にすると「画面下端ギリギリで動く」不自然さを回避できます。unobserveを入れないとスクロールのたびに処理が走るので忘れずに。
3. 時間差で連続表示する
transition-delayをインラインで振るだけで、カード3枚が順に出る演出になります。
targets.forEach((el, i) => {
el.style.transitionDelay = `${i * 0.1}s`;
});
遅延は0.08〜0.15秒刻みが目安。0.3秒を超えると待たされている感じが出ます。
「入ったら1回だけ発火」の実物はこちら。スクロールしてカードが順に立ち上がる動きです。
GSAP ScrollTriggerを使うケース
スクロール位置と動きを紐づけるなら、こちらです。CDNで2ファイル読み込んで使います。
gsap.registerPlugin(ScrollTrigger);
gsap.to('.panel', {
xPercent: -300,
ease: 'none',
scrollTrigger: {
trigger: '.section',
start: 'top top',
end: '+=2000',
pin: true, // 画面に固定
scrub: 1 // スクロールに連動(1は追従の遅れ秒数)
}
});
ポイントはscrubとpin。この2つを自前で書こうとすると、スクロール量の計算・リサイズ対応・慣性処理で数百行になります。ここがGSAPを入れる価値です。逆にフェードインだけのためにライブラリを追加するのは、読み込みコストに見合いません。
スクロール量に連動する「スクラブ」の実物はこちら。スクロールの速さがそのまま動きの速さになります。
実装時に必ず入れたい3つの配慮
- motion-reduce対応:OS側でアニメーション低減を設定しているユーザーには動きを止めます。
@media (prefers-reduced-motion: reduce) { .fade-up { opacity:1; transform:none; transition:none; } } - JS無効・エラー時の保険:初期状態を
opacity:0にすると、JSが落ちたとき本文が真っ白になります。<html>にjs-enabledクラスを付けてから隠すなどの対策を。 - アニメーションさせるプロパティを絞る:
transformとopacityだけに限定します。widthやtopはレイアウト再計算が走り、スマホでカクつきます。
動きの「引き出し」を増やしたいとき
実装方法がわかっても、「どんな動きが自社サイトに合うか」は見てみないと決まりません。動くWebパーツ集のmotionAETHERでは500点のパーツが実際に動く状態で並んでいて、無料で閲覧できます。フェードの距離感やスクロール連動の演出量を比較する参考になるはずです。
まとめ
「入ったら発火」ならIntersectionObserver、「スクロールと連動」ならGSAP ScrollTrigger。まずは上のCSSとJSをコピーして、自社サイトの見出し1つにfade-upクラスを付けるところから試してみてください。10分で体感が変わります。