なぜフォーマット変換が必要になったか
リサイズはできるようになったけど、出力が 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 情報の削除について書きます。