Zcompress

ブラウザ互換性と画像フォーマット

Web開発において、画像フォーマットの選択はブラウザの互換性を考慮する必要があります。新しい画像フォーマットは優れた圧縮効率を提供しますが、すべてのブラウザでサポートされているわけではありません。本記事では、各画像フォーマットのブラウザ対応状況と、互換性を保ちながら最新技術を活用する方法を詳しく解説します。

主要画像フォーマットの対応状況(2024年6月現在)

従来のフォーマット

フォーマット Chrome Firefox Safari Edge IE 11
JPEG ✅ 全バージョン ✅ 全バージョン ✅ 全バージョン ✅ 全バージョン ✅ 対応
PNG ✅ 全バージョン ✅ 全バージョン ✅ 全バージョン ✅ 全バージョン ✅ 対応
GIF ✅ 全バージョン ✅ 全バージョン ✅ 全バージョン ✅ 全バージョン ✅ 対応
SVG ✅ 全バージョン ✅ 全バージョン ✅ 全バージョン ✅ 全バージョン ⚠️ 部分的

次世代フォーマット

フォーマット Chrome Firefox Safari Edge 対応開始
WebP ✅ v23+ ✅ v65+ ✅ v14.1+ ✅ v18+ 2010年
AVIF ✅ v85+ ✅ v93+ ✅ v16.0+ ✅ v85+ 2020年
JPEG XL ❌ 削除 ⚠️ フラグ ❌ 未対応 ❌ 未対応 開発中
HEIF/HEIC ❌ 未対応 ❌ 未対応 ✅ v11+ ❌ 未対応 2017年

フォーマット検出とフォールバック実装

HTML picture要素による実装

<picture>
  <!-- AVIF(最新・最高効率) -->
  <source srcset="image.avif" type="image/avif">
  
  <!-- WebP(広くサポート) -->
  <source srcset="image.webp" type="image/webp">
  
  <!-- JPEG(フォールバック) -->
  <img src="image.jpg" alt="説明文" loading="lazy">
</picture>

JavaScriptによる対応確認

// 画像フォーマットのサポート検出
function checkImageFormatSupport() {
  const formats = {
    webp: 'data:image/webp;base64,UklGRiIAAABXRUJQVlA4IBYAAAAwAQCdASoBAAEADsD+JaQAA3AAAAAA',
    avif: 'data:image/avif;base64,AAAAIGZ0eXBhdmlmAAAAAGF2aWZtaWYxbWlhZk1BMUIAAADybWV0YQAAAAAAAAAoaGRscgAAAAAAAAAAcGljdAAAAAAAAAAAAAAAAGxpYmF2aWYAAAAADnBpdG0AAAAAAAEAAAAeaWxvYwAAAABEAAABAAEAAAABAAABGgAAABcAAAAoaWluZgAAAAAAAQAAABppbmZlAgAAAAABAABhdjAxQ29sb3IAAAAAamlwcnAAAABLaXBjbwAAABRpc3BlAAAAAAAAAAEAAAABAAAAEHBpeGkAAAAAAwgICAAAAAxhdjFDgQAMAAAAABNjb2xybmNseAACAAIABoAAAAAXaXBtYQAAAAAAAAABAAEEAQKDBAAAAB9tZGF0EgAKCBgABogQEDQgMgkQAAAAB8dSLfI=',
    jpegxl: 'data:image/jxl;base64,/woIELASCAgQAFwASxLFgkWAHL0xqnCBCV0qDp901Te/5QM='
  };

  const support = {};

  return Promise.all(
    Object.entries(formats).map(([format, data]) => {
      return new Promise(resolve => {
        const img = new Image();
        img.onload = () => {
          support[format] = img.width > 0 && img.height > 0;
          resolve();
        };
        img.onerror = () => {
          support[format] = false;
          resolve();
        };
        img.src = data;
      });
    })
  ).then(() => support);
}

// 使用例
checkImageFormatSupport().then(support => {
  console.log('ブラウザサポート状況:', support);
  // 結果: { webp: true, avif: true, jpegxl: false }
});

CSSでの条件分岐

/* WebP対応ブラウザ向け */
.webp .hero-image {
  background-image: url('hero.webp');
}

/* WebP非対応ブラウザ向け */
.no-webp .hero-image {
  background-image: url('hero.jpg');
}

/* AVIFサポートの検出(CSS Feature Query) */
@supports (content: url('image.avif')) {
  .modern-image {
    background-image: url('background.avif');
  }
}

サーバーサイドでの対応

Accept ヘッダーの活用

# Nginx設定例
location ~* \.(jpg|jpeg|png)$ {
  # WebP対応確認
  if ($http_accept ~* "webp") {
    set $webp_suffix ".webp";
  }
  
  # AVIFサポート確認
  if ($http_accept ~* "avif") {
    set $avif_suffix ".avif";
  }
  
  # 優先順位: AVIF > WebP > オリジナル
  try_files $uri$avif_suffix $uri$webp_suffix $uri =404;
}

# Apache .htaccess例
<IfModule mod_rewrite.c>
  RewriteEngine On
  
  # AVIFサポート
  RewriteCond %{HTTP_ACCEPT} image/avif
  RewriteCond %{REQUEST_FILENAME}.avif -f
  RewriteRule ^(.+)\.(jpe?g|png)$ $1.$2.avif [T=image/avif,E=accept:1]
  
  # WebPサポート
  RewriteCond %{HTTP_ACCEPT} image/webp
  RewriteCond %{REQUEST_FILENAME}.webp -f
  RewriteRule ^(.+)\.(jpe?g|png)$ $1.$2.webp [T=image/webp,E=accept:1]
</IfModule>

動的変換の実装

// Node.js (Express) での実装例
const express = require('express');
const sharp = require('sharp');
const app = express();

app.get('/images/:filename', async (req, res) => {
  const { filename } = req.params;
  const accept = req.headers.accept || '';
  
  // サポートフォーマットの優先順位
  let format = 'jpeg'; // デフォルト
  let contentType = 'image/jpeg';
  
  if (accept.includes('image/avif')) {
    format = 'avif';
    contentType = 'image/avif';
  } else if (accept.includes('image/webp')) {
    format = 'webp';
    contentType = 'image/webp';
  }
  
  try {
    const image = await sharp(`./images/${filename}`)
      .toFormat(format, {
        quality: format === 'avif' ? 50 : 85
      })
      .toBuffer();
    
    res.set('Content-Type', contentType);
    res.set('Cache-Control', 'public, max-age=31536000');
    res.send(image);
  } catch (error) {
    res.status(404).send('Image not found');
  }
});

モバイルブラウザの特殊対応

iOS Safari の制限事項

Android Chrome の最適化

// Data Saver モードの検出と対応
if (navigator.connection && navigator.connection.saveData) {
  // 低品質画像を使用
  document.querySelectorAll('img[data-low-src]').forEach(img => {
    img.src = img.dataset.lowSrc;
  });
}

// ネットワーク速度に応じた画像選択
function getOptimalImageSource() {
  const connection = navigator.connection;
  if (!connection) return 'high';
  
  const effectiveType = connection.effectiveType;
  const saveData = connection.saveData;
  
  if (saveData || effectiveType === 'slow-2g' || effectiveType === '2g') {
    return 'low';
  } else if (effectiveType === '3g') {
    return 'medium';
  }
  
  return 'high';
}

プログレッシブエンハンスメント戦略

基本方針

  1. ベースライン:すべてのブラウザで動作するJPEG/PNGを基本とする
  2. 段階的改善:対応ブラウザに応じて高効率フォーマットを提供
  3. 機能検出:ブラウザの能力を事前に確認
  4. フォールバック:失敗時の代替手段を必ず用意

実装例

class AdaptiveImageLoader {
  constructor() {
    this.supportedFormats = {};
    this.checkFormatSupport();
  }

  async checkFormatSupport() {
    // フォーマットサポートチェック
    const tests = {
      webp: 'UklGRiIAAABXRUJQVlA4IBYAAAAwAQCdASoBAAEADsD+JaQAA3AAAAAA',
      avif: 'AAAAIGZ0eXBhdmlmAAAAAGF2aWZtaWYxbWlhZk1BMUIAAADybWV0YQAAAAAAAAAoaGRscgAAAAAAAAAAcGljdAAAAAAAAAAAAAAAAGxpYmF2aWYAAAAADnBpdG0AAAAAAAEAAAAeaWxvYwAAAABEAAABAAEAAAABAAABGgAAABcAAAAoaWluZgAAAAAAAQAAABppbmZlAgAAAAABAABhdjAxQ29sb3IAAAAAamlwcnAAAABLaXBjbwAAABRpc3BlAAAAAAAAAAEAAAABAAAAEHBpeGkAAAAAAwgICAAAAAxhdjFDgQAMAAAAABNjb2xybmNseAACAAIABoAAAAAXaXBtYQAAAAAAAAABAAEEAQKDBAAAAB9tZGF0EgAKCBgABogQEDQgMgkQAAAAB8dSLfI='
    };

    for (const [format, base64] of Object.entries(tests)) {
      this.supportedFormats[format] = await this.testFormat(format, base64);
    }
  }

  testFormat(format, base64) {
    return new Promise(resolve => {
      const img = new Image();
      img.onload = () => resolve(true);
      img.onerror = () => resolve(false);
      img.src = `data:image/${format};base64,${base64}`;
    });
  }

  getBestFormat(availableFormats) {
    // 優先順位: AVIF > WebP > JPEG/PNG
    const priority = ['avif', 'webp', 'jpeg', 'png'];
    
    for (const format of priority) {
      if (this.supportedFormats[format] && availableFormats.includes(format)) {
        return format;
      }
    }
    
    return availableFormats[0] || 'jpeg';
  }

  loadImage(sources) {
    const bestFormat = this.getBestFormat(Object.keys(sources));
    const img = new Image();
    img.src = sources[bestFormat];
    return img;
  }
}

// 使用例
const loader = new AdaptiveImageLoader();
const img = loader.loadImage({
  avif: '/images/hero.avif',
  webp: '/images/hero.webp',
  jpeg: '/images/hero.jpg'
});

パフォーマンス考慮事項

フォーマット選択の影響

フォーマット デコード速度 メモリ使用量 CPU負荷
JPEG 高速
PNG 中速
WebP 中速
AVIF 低速

将来への備え

新フォーマットへの対応準備

まとめ

ブラウザの画像フォーマット対応は常に進化しています。新しいフォーマットは優れた圧縮効率を提供しますが、互換性の確保も重要です。picture要素やサーバーサイドの対応により、すべてのユーザーに最適な画像を提供できます。Zcompressのようなツールを活用して複数フォーマットの画像を準備し、プログレッシブエンハンスメントの原則に従って実装することで、パフォーマンスと互換性を両立させることができます。定期的に対応状況をチェックし、新技術の採用タイミングを適切に判断することが、成功の鍵となります。

関連記事:WebP形式完全ガイド | 画像フォーマットの未来