0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ブラウザだけで画像をWebP・AVIFに変換する

0
Posted at

なぜフォーマット変換が必要になったか

リサイズはできるようになったけど、出力が PNG のままだとファイルサイズが大きいことがある。

特に写真系の画像は WebP や AVIF にするだけでサイズが30〜60%くらい減ることがあって、
それだけでページの読み込み速度がだいぶ変わります。

ライブラリなしで変換できるか調べたら、canvas.toDataURL
OffscreenCanvas.convertToBlob がフォーマット指定に対応していたので
追加実装なしで行けました。


Canvas での変換方法

// WebP に変換(品質 0.0〜1.0)
const blob = await canvas.convertToBlob({ type: 'image/webp', quality: 0.85 });

// AVIF に変換
const blob = await canvas.convertToBlob({ type: 'image/avif', quality: 0.8 });

// JPEG に変換
const blob = await canvas.convertToBlob({ type: 'image/jpeg', quality: 0.9 });

quality は省略すると実装依存のデフォルト値が使われます。
WebP なら 0.8 あたりが見た目とサイズのバランスが良い印象です。


ブラウザ対応を確認してから使う

WebP と AVIF はブラウザによって出力できない場合があります。

async function getSupportedFormat() {
  const formats = [
    { type: 'image/avif',  ext: 'avif' },
    { type: 'image/webp',  ext: 'webp' },
    { type: 'image/jpeg',  ext: 'jpg'  },
  ];

  for (const format of formats) {
    const canvas = new OffscreenCanvas(1, 1);
    const ctx    = canvas.getContext('2d');
    ctx.fillRect(0, 0, 1, 1);

    try {
      const blob = await canvas.convertToBlob({ type: format.type });
      if (blob && blob.size > 0) return format;
    } catch (e) {
      // 非対応
    }
  }

  return { type: 'image/png', ext: 'png' };
}

1×1px の Canvas を変換してみて、サイズが0より大きければ対応していると判断します。
AVIF → WebP → JPEG の順で試して、使えるフォーマットを返すようにしました。


worker.js に組み込む

前回の worker.js に変換フォーマットを受け取れるように修正します。

self.onmessage = async (e) => {
  const { bitmap, maxWidth, maxHeight, format, quality } = e.data;

  const ratio  = Math.min(maxWidth / bitmap.width, maxHeight / bitmap.height, 1);
  const width  = Math.round(bitmap.width  * ratio);
  const height = Math.round(bitmap.height * ratio);

  const canvas = new OffscreenCanvas(width, height);
  const ctx    = canvas.getContext('2d');
  ctx.drawImage(bitmap, 0, 0, width, height);

  const blob = await canvas.convertToBlob({ type: format, quality });
  self.postMessage({ blob, width, height });
};

呼び出し側で format と quality を渡すだけで切り替えられます。


ファイルサイズの比較

手元の写真(元サイズ 4.2MB、3024×4032px)を 1200px 幅にリサイズして比べました。

フォーマット ファイルサイズ
PNG 2.1MB
JPEG 90% 380KB
WebP 85% 210KB
AVIF 80% 140KB

AVIF が一番小さいですが、Safari の古いバージョンでは表示できないので
用途によって使い分けが必要です。

次は Exif 情報の削除について書きます。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?