アニメーション画像の軽量化:短尺ループを WebP/MP4 最小コスト化
GIF を“なんとなく”書き出して埋め込むと容量過大・デコード遅延・CPUスパイクが発生し、INP や LCP を押し下げます。本記事は短尺ループ (2〜6秒) の UI / 装飾 / 状態説明アニメを 70% 以上軽量化 する最短ワークフローを示します。
先に結論
- fps 正規化 (60→30 / 30→24) でフレーム数を半減。
- 往復動きは
reverse + concat
で ping-pong 化しループ境界を自然に。 - 透過不要なら MP4(H.264) / 透過必要なら Animated WebP に決め打ち。
- poster 1枚 (先頭フレーム以外推奨) で 初期視認性 と CLS 安定。
- 画質閾値: SSIM ≥ 0.98 / ΔBytes -70% 以上で合格。
1. 現状課題の分解
- GIF は フレーム全差分(圧縮効率低)かつ 8bit パレット制約。
- PNG 連番 → CSS/JS 合成は HTTP/パース負荷が高い。
- WebP/AVIF アニメは高圧縮だが透過/再生互換の妥協点を理解しないと破綻。
- fps 過剰とループ境界のジャンプで UX 品質低下。
2. 最短フロー概要
- 素材分類と目的定義(装飾 / 手順説明 / 状態指示)。
- fps 正規化と余分フレーム削除。
- ループ境界調整(ping-pong / cross fade は最小限)。
- 形式判定 & エンコード(WebP or MP4)。
- poster 生成+自動再生安全化 (muted, playsinline)。
- 差分/SSIM 検証と Bytes 減少率の記録。
3. ffmpeg 基本レシピ
3.1 前処理とループ生成
# 1) 60fps 録画素材を 30fps へ正規化 + クロップ + ループ分トリム
ffmpeg -i input.mov -vf "fps=30,scale=iw:-2,trim=0:2.4" -an normalized.mp4
# 2) 往復(首振り)アニメを ping-pong 化(逆順連結)
ffmpeg -i normalized.mp4 -filter_complex "[0:v]split[v1][v2];[v2]reverse[r];[v1][r]concat=n=2:v=1:a=0" pingpong.mp4
3.2 Animated WebP
# 透過が必要な場合の Animated WebP (lossy, q=80 相当)
ffmpeg -i pingpong.mp4 -vf "fps=30,format=rgba,scale=640:-2" -loop 0 -c:v libwebp_anim -quality 80 loop.webp
3.3 MP4 (H.264)
# 透過不要なら MP4 (H.264) + poster
ffmpeg -i pingpong.mp4 -c:v libx264 -pix_fmt yuv420p -profile:v high -crf 26 -movflags +faststart loop.mp4
ffmpeg -i loop.mp4 -vf "select=eq(n\,0)" -frames:v 1 poster.jpg
3.4 形式判定ルール
/* 形式判定最小ルール
- 透明/アルファ変化あり: Animated WebP
- 輪郭クッキリ/静止区間が多い: 動画 (MP4/WebM) Inter-frame で高圧縮
- 2秒以下シネマ風 or 連続UIハイライト: Animated WebP も可 (差分圧縮効く)
- 端末CPUが弱い & 複数同時再生: MP4(H.264 baseline/high) を優先
*/
4. 埋め込みと初期表示
短尺ループは 早期視認 がクリック率に直結します。poster は最も“動きの中で静止している”フレームを選ぶと差異違和感が減ります。
<!-- 自動再生安全条件: muted + playsinline + 無限ループ -->
<video
src="/media/loop.mp4"
poster="/media/poster.jpg"
autoplay
muted
loop
playsinline
width="640" height="360"
fetchpriority="low"
></video>
自動再生の落とし穴
音声付き/長尺/視認領域外の自動再生は ユーザー制御逸脱 と見なされる可能性。短尺UIデモ以外はポスター+クリック再生へ。
5. 品質と回帰検証
画質評価は主観 + 客観を組み合わせます。Animated WebP は輪郭/テキストのにじみ、MP4 は色帯域とブロックノイズを重点確認。
// 画質/差分計測 (例: node + ssim library 擬似コード)
import { compare } from 'ssim';
const base = await loadImage('baseline.png');
const latest = await loadImage('latest.png');
const { mssim } = compare(base, latest);
if (mssim < 0.98) throw new Error('Quality regression: ' + mssim);
- SSIM (>=0.98) 未満 → fps か 品質パラメータ再検討。
- Bytes 削減 < 50% → 形式判定 / 解像度 / ループ長が過剰。
- 初回ペイント遅延 → poster 欠落 or fetchpriority 誤設定。
6. 公開前チェックリスト
- 形式: 透過要件に沿った選択。
- fps: 30 以下 (高速表現除く)。
- poster: 生成済み & CLS 影響なし。
- 画質: SSIM >= 0.98 / ノイズ・リングング許容内。
- アクセシビリティ: 動きが意味を持つ場合は
aria-label
/ 代替説明。
7. まとめ
“fps 正規化 → 形式判定 → poster → 品質検証” をテンプレ化するだけで、既存 GIF 群を安全に段階移行できます。次は GIF→動画移行 の詳細手順も参照し自動化まで到達してください。
FAQ
FAQ(よくある質問)
1画像形式の基本方針は?(写真/スクショ/透過)
写真は AVIF / WebP(画質80–85%目安)、UIやスクショはPNG / WebP Lossless、単色ロゴはSVGが基本です。 実装の詳細は srcset/sizes設計ガイド と スクショ最適化 を参照してください。
2圧縮しても画質を落とさないコツは?
実表示幅に合わせたリサイズ → 過大ダウンロードを防ぎ、
srcset/sizes
を 実描画幅に一致させます。画質は写真で 80–85% を起点に、ノイズやエッジを目視確認。 仕上げは /compare で Before/After を見比べるのがおすすめです。3CLSを悪化させない画像の置き方は?
公開日: 2025-09-07編集: gazou-compressor.jp