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 ステップで理解

手順役割
① DOMContentLoadedHTML 解析完了直後に実行し、画像の準備を待たないため高速
.js-blur-target を取得エフェクト対象を限定。複数あっても NodeList で取得
③ IntersectionObserver インスタンス生成コールバックとオプション (threshold: 0.1) を渡す
④ コールバック内処理isIntersectingtrue になった瞬間 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 を抑制

より実践的な応用例

  1. LazyLoad × Blur→Sharp
    • loading="lazy" に加え、低解像度プレースホルダを数 kB で読み込み →
      Intersection Observer で高解像度を差し替え。
  2. カラー画像→モノクロへのトランジション
    • filter: grayscale(1) を初期に、交差したら grayscale(0)scale(1.05) などで立体感を演出。
  3. コンテンツフェードインと同期
    • 画像だけでなく、テキストブロックに opacity:0 を当て、
      同じ Observer に登録すれば遅延フェードインを統合管理可能。

パフォーマンスとアクセシビリティ

観点実装ヒント
アクセシビリティmotion/opacity の切替は prefers-reduced-motion メディアクエリで無効化できる設計に
画像サイズ最適化srcsetsizes を併用して端末解像度に応じた最適サイズを配信
再フロー対策親コンテナに min-height を指定し、レイアウトシフト(CLS)を未然に防止

ブラウザ対応状況とフォールバック

  • Chrome / Edge / Firefox / Safari 12+ / Samsung Internet … すべて実装済み
  • IE11 … 使用不可。必要なら <picture>scroll イベントを用いたポリフィル投入を検討しましょう。

まとめ

  • Intersection Observer を使えば、スクロール位置の判定とアニメーションの単発化を数行で実現できる
  • threshold 0.1 により、ユーザーが“気付き始めた瞬間”にブラーを解除してエンゲージメントを高められる
  • CSS の filtertransition だけでリッチなフォーカスイン演出が完成し、JS 側の責務を最小限に抑えられる
  • パフォーマンス・アクセシビリティ配慮を忘れず、
    prefers-reduced-motion や画像最適化を組み合わせると UX を落とさずに実装できる

ぜひ自社サイトやクライアントワークで試し、“さりげない驚き” をユーザーに届けてみてください。