シームレスループの作り方 2025 — GIF/WEBP/APNG の境界を消す実務

公開: 2025年9月20日 · 読了目安: 1 · 著者: Unified Image Tools 編集部

はじめに

継ぎ目の “チラつき” はユーザー体験を壊します。本稿はシームレス化に効く制作・出力の勘所を整理します。

対象は UI の短尺ループ(ローディング、インジケータ)から、LP のヒーロー演出、SNS 用の軽量ループまで。設計段階の工夫と、コーデックごとのエンコード癖、デプロイ後の検証方法を一気通貫でまとめます。

設計の原則

  • 先頭/末尾フレームの差分を最小化(円運動・クロスフェード・Ping-Pong)
  • カメラの動きは等速/等加速度を厳守
  • ループ周期は UI コンテキストに合わせ 0.8–1.6s を目安

追加の原則:

  • 「常に動く要素」と「静的要素」を分離し、差分量を抑える(後段の圧縮効率・視覚破綻に直結)
  • 前景/背景でレイヤ分けし、背景だけを低フレームレートに落とすなど“段階的な最適化”を可能にする

関連記事: スプライトとアニメーションの軽量化 — Sprite Sheet / WebP / APNG 2025

合成とエンコード

  1. 元フレームを整列・トリムし、透明領域のバラツキを除去
  2. 連番→アニメーション で GIF/WEBP/APNG を比較
  3. スプライトが適する場合は スプライトシート生成 で 1 枚化し CSS/JS 再生

コーデック別の癖

  • GIF: 256 色制限。ディザとパレット選定が品質の大半を決める。透明は 1bit 相当
  • APNG: 可逆 + 高互換。サイズは大きくなりがちだが UI 品質は保ちやすい
  • WebP(有損アニメ): 色数制限なし。低ビットレートで輪郭のにじみ/にょろつきが出ることがある

推奨エンコード(例)

WebP(有損)

img2webp frame_%03d.png -o out.webp -q 80 -m 6 -loop 0 -mt

APNG

apngasm out.png frame_%03d.png 1 10
optipng -o7 out.png

スプライトシート(CSS)

.hero {
  width: 512px; height: 512px;
  background: url(sprite.png) 0 0 / 512px 16384px no-repeat; /* 32 frames */
  animation: run 1.2s steps(32) infinite;
}
@keyframes run { to { background-position-y: -16384px; } }

トラブルと対処

  • ループ境界で一瞬の暗縁: ガンマ・マルチプライの扱いを見直し
  • WebP で色にじみ: サブサンプリング/品質を上げる
  • 破綻が目立つ背景: 前処理ノイズで段差を緩和

追加のよくある問題:

  • alpha 合成の前後でガンマが一致しない → sRGB 前提でマルチプライ/スクリーンの整合性を取る
  • ループ周期と easing の不一致 → "ease-in-out" を避け、linear で設計(Ping-Pong は ease でも可)

チェックリスト

  • [ ] 周期・速度が UI 文脈に適切
  • [ ] 境界でフレームの位置/明度が一致
  • [ ] 容量が許容範囲(ヒーロー < 500KB 目安)

QA プロトコル(3 分)

  1. 200% 拡大で境界 3 ループ分観察(暗縁/明滅/ぶれの有無)
  2. モバイル実機で 60fps 追従かを確認(低端末でドロップがないか)
  3. 明度/色相が周回でズレないかフレーム比較

FAQ

  • Q: GIF, WebP, APNG の基準は?
  • A: 互換性重視なら APNG、軽さは WebP、簡便さは GIF。UI はスプライト+CSSが最軽量なことが多いです。

まとめ

“境界を作らない” 設計が最重要。手法を使い分け、軽く滑らかなループを実現しましょう。


付録: Web 実装の最小例

<div class="wrap">
  <img src="loop.webp" width="512" height="512" alt="loop animation" />
</div>
<style>
.wrap img { image-rendering: auto; }
@media (prefers-reduced-motion: reduce) {
  .wrap img { animation: none !important; }
}
</style>

スプライト版(より軽量)

<div class="hero" aria-hidden="true"></div>
.hero { width: 256px; height: 256px; background: url(sprite.png) 0 0/256px 8192px; animation: run 1.2s steps(32) infinite; }
@keyframes run { to { background-position-y: -8192px; } }

関連記事