logo hsb.horse
← Voltar para o índice do blog

Blog

Implementando processamento de imagem com OffscreenCanvas e Web Worker

Como processar imagens com Web Worker e OffscreenCanvas sem bloquear a thread principal. Este artigo organiza um exemplo concreto de conversão para WebP.

Publicado:

Ao combinar OffscreenCanvas com Web Worker, é possível processar imagens sem bloquear a thread principal.

Tecnologias usadas

  • Web Worker
  • Offscreen Canvas
  • Blob
  • Bitmap

Interface

export interface WorkerRequestBody {
buffer: ArrayBuffer;
type: string;
size: {
height: number;
width: number;
};
}
export interface WorkerResponseBody {
buffer: ArrayBuffer;
contentType: string;
}

Processando uma imagem dentro de um Web Worker

async function imageProcessing(
data: WorkerRequestBody
): Promise<WorkerResponseBody> {
const { buffer, type, size } = data;
// Converter o ArrayBuffer de volta em Blob
const blob = new Blob([buffer], { type });
// Criar um OffscreenCanvas
const canvas = new OffscreenCanvas(size.width, size.height);
// Criar um Bitmap a partir do Blob
const bitmap = await createImageBitmap(blob);
// Obter um CanvasRenderingContext2D
const ctx = canvas.getContext("2d");
if (!ctx) throw new Error("Context2D is not defined");
// Desenhar a imagem no canvas
ctx.drawImage(bitmap, 0, 0);
// Converter para WebP
const webp = await canvas.convertToBlob({
type: "image/webp",
quality: 0.85,
});
// Converter o Blob WebP em 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);
// Passar o segundo argumento para transferir a posse do ArrayBuffer para a thread de UI
self.postMessage(output, [output.buffer]);
});

Enviando a imagem para o worker e recebendo o resultado convertido

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", async (e) => {
const [file] = Array.from(fileInput.files);
if (!file) return;
const buffer = await file.arrayBuffer();
const type = file.type;
const size = { height: 1080, width: 1980 }; // obter o tamanho da imagem
// Enviar para o Web Worker e transferir a posse
worker.postMessage(
{
buffer,
type,
size
},
[buffer]
);
});

Pontos principais

  1. OffscreenCanvas: uma API de Canvas que funciona dentro de Web Workers
  2. Transferência de ArrayBuffer: passagem eficiente de dados com transferência de posse
  3. convertToBlob: conversão para formatos como WebP usando OffscreenCanvas

Resumo

Combinando OffscreenCanvas e Web Worker, é possível executar processamento de imagem sem bloquear a thread principal.

Quando há muitas imagens ou conversões pesadas para processar, essa abordagem mantém a UI responsiva e melhora a experiência do usuário.