OffscreenCanvasで画像処理する
- Web Worker
- Offscreen Canvas
- Blob
- Bitmap
インターフェース
Section titled “インターフェース”export interface WorkerRequestBody { buffer: ArrayBuffer; type: string; size: { height: number; width: number; };}
export interface WorkerResponseBody { buffer: ArrayBuffer; contentType: string;}
WebWorker で画像を処理する
Section titled “WebWorker で画像を処理する”async function imageProcessing( data: WorkerRequestBody): Promise<WorkerResponseBody> { const { buffer, type, size } = data;
// ArrayBufferからBlobに戻す const blob = new Blob([buffer], { type });
// OffscreenCanvasを生成 const canvas = new OffscreenCanvas(size.width, size.height);
// BlobからBitmapを作成 const bitmap = await createImageBitmap(blob);
// CanvasRenderingContext2Dを取得。(エラー処理が適当なのはサンプルなので) const ctx = canvas.getContext("2d"); if (!ctx) throw new Error("");
// 画像をCanvasに描画。OffscreenCanvasを生成時に画像サイズを指定しているため、第4,5引数を省略しても問題ない ctx.drawImage(bitmap, 0, 0);
// convertToBlob関数で変換。この例ではWebP固定。 const webp = await canvas.convertToBlob({ type: "image/webp", quality: 0.85, });
// WebPのBlobオブジェクトをArrayBufferに変換 const buf = await webp.arrayBuffer();
return { buffer: buf, contentType: "image/webp", };}
self.addEventListener("message", async (e: MessageEvent<WorkerRequestBody>) => { const output = await imageProcessing(e.data); // ArrayBufferをUIスレッドに移譲するため、第二引数を指定し、所有権を渡す self.postMessage(output, [output.buffer]);});
Worker に画像を渡して、変換後の画像を受け取る
Section titled “Worker に画像を渡して、変換後の画像を受け取る”※ 不要なコンテキストは省略しています ※ Vite などモダンな開発環境での実行を想定した書き方です
import MyWorker from "./worker.ts";
const worker = new MyWorker();
const fileInput = document.querySelector<HTMLInputElement>("#js-file-input");
worker.addEventListener("message", (e: MessageEvent<WorkerResponseBody>) => { const { buffer, contentType } = e.data; const blob = new Blob([buffer], { type: contentType });
// do something});
fileInput.addEventListener("change", (e) => { const [file] = Array.from(fileInput.files); if (!file) return;
const buffer = await file.arrayBuffer();
// NOTE: よしなに const type = "";
// NOTE: 画像の取得方法に合わせたサイズの計算処理を使って算出(EXIFとか) const size = { height: 1080, width: 1980 }
// WebWorkerに送信&所有権移譲 worker.postMessage( { buffer, type, size }, [buffer] );});