定義
画像が複数枚ある横型カルーセルで、今画面に表示されていないスライド画像まで一度に取得してしまい初回描画が遅くなるときに使う手法である。カルーセルビューポート画像遅延とは、横型スライダーで現在のビューポート(今画面に実際に見えている領域)に入っていないスライドについて、<img>(または画像コンポーネント)をそもそもDOMに追加せず(マウントせず)、固定高さのplaceholder(場所だけ確保する空のボックス)だけを置くことである。最初に表示されるスライド(通常はLCP=最大コンテンツ要素の候補)はeager(遅延なしで即座に読み込み)・high priority(高いダウンロード優先度)を維持する。
なぜ必要か
カルーセル最初のカードのサムネイルは、そのページで最も大きなコンテンツ(LCP)要素であることが多い。ところがスライドごとに画像をすべて追加すると、ブラウザとpreload scanner(レンダー前に文書を走査して先に取得すべきリソースを見つける仕組み)が大容量画像を複数同時にリクエストし、総転送量とメインスレッドの画像デコード負荷が増える — 肝心の最初の画像の到着が遅れる。loading="lazy"(ビューポート付近に来るまで読み込みを遅らせる属性)だけでは、DOMに<img>がすでにあるとブラウザがそれを発見(discover)して優先度の競合が残ることがあるため、画面外のスライドは条件付きでそもそもマウントしない方が確実である。
動作原理
- カルーセルライブラリ(Embla、Swiperなど)または
IntersectionObserverでin-viewインデックスを求める。 shouldLoad = inViewSet.has(index)のときだけImageコンポーネントをレンダーする。- 外側のwrapperは固定height — 画像がないときもレイアウトを維持する(pop-in CLS防止)。
- index 0(最初のvisible):
fetchPriority="high"、loading="eager"。
| スライド | Image mount | Priority |
|---|---|---|
| First visible | Yes | high |
| Scrolled into view | Yes | lazy/auto |
| Out of view | No (placeholder) | — |
実務での適用
Embla例: emblaApi.slidesInView() + slidesInViewイベントでstateを更新する。
検証: Network transferのbefore/after、LCP要素が依然として最初のサムネイルかをtraceで確認する。
トレードオフ
| 選択 | 利点 | コスト |
|---|---|---|
| slidesInView defer | Transfer·decode ↓ | スクロール時のplaceholder→image pop-in |
| すべてlazy | 実装が簡単 | mount·discoverコストが残る |
| すべてeager | スクロールプレビューが速い | LCP·帯域幅が悪化 |
pop-inを減らすには、placeholderと画像が同一のaspect box(absolute inset-0)を共有する必要がある。
使ってはいけない場合
- スライドが1〜2枚だけ(利得が僅少)。
- すべてのスライドがnarrow viewportで同時に見える。
- サムネイルがすでに非常に小さく総transferが無視できる水準。
よくある間違い
- lazyだけを適用し、off-viewportでもImageノードをマウントする。
- すべてのカードに
priority— LCP候補は1つだけをhighにする。 - placeholderの高さなしでdefer → スクロール時のCLS。
関連概念
- largest-contentful-paint — LCP候補の識別
- resource-priority — fetchpriority·preloadの範囲
- cls-skeleton-layout-reservation — placeholderのボックスモデル