Intersection Observerで実現する「1回だけ」画像ブラー演出
スクロール連動のアニメーションは、適切に使えばページに動きと
ストーリー性を与える強力なアクセントになります。
一方で、過度な演出は表示パフォーマンスを損ねたり、ユーザーを迷わせたりする危険も伴います。
そこで “画像が10 %画面に入った瞬間だけふわっとブラーが解除される” 程度の控えめなエフェクトは、
目に優しくサイトをスマートに演出できる絶妙な落としどころです。
本記事では、以下の JavaScript スニペットを題材に、
「Intersection Observer を使って一度きりで解除されるブラー効果」
を実装する方法とポイントを解説します。
document.addEventListener('DOMContentLoaded', () => {
const targets = document.querySelectorAll('.js-blur-target');
const io = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
// 最初に 10 %以上見えた瞬間にクラスを付与
entry.target.classList.add('img-blur');
// これ以降この要素は監視しない ⇒ 「1 回だけ」
observer.unobserve(entry.target);
}
});
}, { threshold: 0.1 });
targets.forEach(el => io.observe(el));
});
Intersection Observer とは
Intersection Observer API は、指定した要素がビューポート(または親要素)と
交差した瞬間を検知できるブラウザネイティブの監視仕組みです。
従来の scroll
イベント+座標計算の手法に比べて、次の利点があります。
利点 | 説明 |
---|---|
パフォーマンス | ブラウザ内部の最適化ループで動くため、レイアウトスラス/リフローを引き起こさず高速 |
簡潔なコード | 条件分岐や座標計算が不要で、監視の開始・停止もワンライナー |
細かな閾値制御 | threshold オプションで「◯% 見えたら発火」を柔軟に指定可能 |
単発 or 常時監視 | unobserve() を呼ぶだけで“1回だけ”仕様にできる |
今回のケースでは、「画像が10 %でも画面に入ったら blur を解除し、その後は監視をやめる」
という条件をシンプルに満たせます。
コード解説 ― 5 ステップで理解
手順 | 役割 |
---|---|
① DOMContentLoaded | HTML 解析完了直後に実行し、画像の準備を待たないため高速 |
② .js-blur-target を取得 | エフェクト対象を限定。複数あっても NodeList で取得 |
③ IntersectionObserver インスタンス生成 | コールバックとオプション (threshold: 0.1 ) を渡す |
④ コールバック内処理 | isIntersecting が true になった瞬間 img-blur クラスを付与 |
⑤ 監視解除 | observer.unobserve(entry.target) で再検知を停止 |
threshold: 0.1
の意味
- 0.0 … 1 px でも交差した瞬間発火
- 1.0 … 完全に要素全体が表示されたら発火
- 0.1 … 要素面積の 10 % が交差したら発火(体感として“ちょっと見切れた時”)
60fps を守る unobserve 戦略
繰り返しアニメーションが不要なら 必ず監視解除 しましょう。
無駄なオブザーバーはメモリリークや再計算コストを招きます。
CSS でブラー解除アニメーションを作る
JS が付与するクラスは img-blur
のみ。
あとは CSS で「初期状態 = ぼかし」「クラス付いたら原寸」を定義すれば完成です。
/* 初期状態(まだ見えていない) */
.js-blur-target {
filter: blur(8px);
transition: filter .8s ease-out;
}
/* 10 % 見えた瞬間に JS が付与 → ブラー解除 */
.img-blur {
filter: blur(0);
}
ポイント
transition: filter .8s
で緩やかに解像度が上がる“フォーカスイン”演出- filter ブラーは GPU 処理されるため比較的コストが低い
- 画像の読み込み前でも低解像度のシルエットだけが先にぼけて表示され、CLS を抑制
より実践的な応用例
- LazyLoad × Blur→Sharp
loading="lazy"
に加え、低解像度プレースホルダを数 kB で読み込み →
Intersection Observer で高解像度を差し替え。
- カラー画像→モノクロへのトランジション
filter: grayscale(1)
を初期に、交差したらgrayscale(0)
+scale(1.05)
などで立体感を演出。
- コンテンツフェードインと同期
- 画像だけでなく、テキストブロックに
opacity:0
を当て、
同じ Observer に登録すれば遅延フェードインを統合管理可能。
- 画像だけでなく、テキストブロックに
パフォーマンスとアクセシビリティ
観点 | 実装ヒント |
---|---|
アクセシビリティ | motion/opacity の切替は prefers-reduced-motion メディアクエリで無効化できる設計に |
画像サイズ最適化 | srcset と sizes を併用して端末解像度に応じた最適サイズを配信 |
再フロー対策 | 親コンテナに min-height を指定し、レイアウトシフト(CLS)を未然に防止 |
ブラウザ対応状況とフォールバック
- Chrome / Edge / Firefox / Safari 12+ / Samsung Internet … すべて実装済み
- IE11 … 使用不可。必要なら
<picture>
やscroll
イベントを用いたポリフィル投入を検討しましょう。
まとめ
- Intersection Observer を使えば、スクロール位置の判定とアニメーションの単発化を数行で実現できる
- threshold 0.1 により、ユーザーが“気付き始めた瞬間”にブラーを解除してエンゲージメントを高められる
- CSS の
filter
とtransition
だけでリッチなフォーカスイン演出が完成し、JS 側の責務を最小限に抑えられる - パフォーマンス・アクセシビリティ配慮を忘れず、
prefers-reduced-motion
や画像最適化を組み合わせると UX を落とさずに実装できる
ぜひ自社サイトやクライアントワークで試し、“さりげない驚き” をユーザーに届けてみてください。
【解説】data-ajax=”false”とは?意味・使い方・スムーススクロールとの関係まで徹底解説
4月 11, 2025フォームのチェックボックスをクリックすると別のテキストボックスにテキストを挿入する方法
3月 6, 2025ハンバーガーメニューでナビゲーションリンクをクリックするとメニューを閉じる方法
3月 2, 2025