AVIF 大判画像タイル戦略:遅延分割/LOD/視点適応ロードの実装
巨大な製品画像/地図/年表をモバイルで一括ロードすると 初期LCP/メモリ/帯域 が肥大化します。タイル+LODは "見える所だけ" を逐次供給しUXを維持する定番戦略です。
構成要素
- タイル分割 (512/1024)
- LOD階層 (1x/2x/4x例)
- ビューポート/ズームで差し替え
- 初期軽量プレビュー (LQ AVIF)
1. 目的
初期ビューで 体感応答 を維持しながら詳細要求時のみ高解像度取得。帯域節約とINP劣化回避に寄与します。
2. タイル生成
# 1. タイル分割 (Sharp例)
sharp poster.png \
.tile({ size: 512, layout: 'dz' }) \
.toFile('out');
// 2. manifest.json (抜粋)
{
"+0": { "w":512, "h":512, "tiles": [[0,0],[1,0],[2,0],[3,0]] },
"+1": { "w":1024, "h":1024, "tiles": [[0,0],[1,0],[2,0],[3,0]] }
}
3. LOD階層
// 4. ズームレベル -> 階層選択
function level(scale){
if (scale < 1.3) return 0;
if (scale < 2.1) return 1;
return 2;
}
4. ビューポート適応ロード
// 3. ビューポート適応ロード (pseudo)
const obs = new IntersectionObserver(entries=>{
for (const e of entries){
if (e.isIntersecting) loadTile(e.target.dataset.src);
}
},{ root: viewportEl, rootMargin:'256px' });
5. 実装詳細 (優先度/メモリ)
視点中心から曼荼羅状に 曼延優先ロード することで初期パン時の “未解像タイル” 発生を抑制。Priority Hints(fetchpriority) で中心列を high、周辺は low。
// 優先ロード (中心→周辺)
function sortTiles(tiles, center){
return tiles.sort((a,b)=>dist(a,center)-dist(b,center));
}
ビューポート外タイルは 最後の参照時刻 を記録し猶予後に revoke しメモリ回収。GPUテクスチャ保持実装では VRAM 枯渇防止に必須。
// メモリ解放 (一定時間未参照タイルを撤去)
const LIVE = new Map(); // key -> { lastSeen, url }
setInterval(()=>{
const now = performance.now();
for (const [k,v] of LIVE){
if (now - v.lastSeen > 12000){
URL.revokeObjectURL(v.url);
LIVE.delete(k);
}
}
},4000);
6. 性能計測/ベンチ
- 初期視点 LCP: LQプレビュー → 中心4枚 → 周辺リング
- ズーム遷移: 旧LOD と 新LOD のオーバーラップフェード (opacity)
- 指標: Mean Tile Time (MTT), p95 tile latency, overfetch bytes
# ネットワーク計測 (Chromium Trace -> tiles.json 抽出)
node scripts/extract-tiles-trace.js trace.json > tiles.json
jq '.tiles | group_by(.priority) | map({p:.[0].priority, sum: (map(.bytes)|add)})' tiles.json
7. 運用/監視
総タイル数 / 初期bytes / ズーム切替遅延を継続監視し LOD 設計のドリフトを防止。Prometheus exporter で level 別 bytes を集計し週次トレンド化。
# Grafana メトリクス例 (PromQL)
rate(tile_bytes_total[5m]) by (level)
quantile(0.95, sum(rate(tile_latency_ms_bucket[5m])) by (le,level))
8. 失敗パターン
- LOD 刻み過多 → オーバーヘッド/メモリ圧迫
- 奇数サイズ (768 等) → キャッシュ再利用率低下
- 過剰プリフェッチ → モバイル帯域浪費
9. CI/回帰検知
# CI: 初期ロードbytes上限 & タイル総数回帰検知
node scripts/tiles-metrics.js poster.manifest.json --max-initial-bytes 450000 --max-tiles 160
初期表示bytesの安定は CIパイプライン に統合し監視します。
10. FAQ
- HTTP/3で改善? → 多数並列は恩恵あり。タイル再利用率が低い場合は優先度再調整。
- メモリ溢れ? → 画面外 1.5×viewport 以遠を逐次破棄し最大キャッシュ枚数を上限管理。
11. まとめ
タイル+LOD+視点適応ロードで巨大画像の “必要な部分だけ高解像度” を満たし、全体一括より帯域 40–70% 削減が現実的に狙えます。