logo hsb.horse
← Retour au blog

Blog

Mettre en place un traitement d’image avec OffscreenCanvas et Web Worker

Comment traiter des images avec Web Worker et OffscreenCanvas sans bloquer le thread principal. L’article s’appuie sur un exemple concret de conversion en WebP.

Publié:

En combinant OffscreenCanvas et Web Worker, on peut traiter des images sans bloquer le thread principal.

Technologies utilisées

  • 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;
}

Traiter une image dans un Web Worker

async function imageProcessing(
data: WorkerRequestBody
): Promise<WorkerResponseBody> {
const { buffer, type, size } = data;
// Reconvertir l'ArrayBuffer en Blob
const blob = new Blob([buffer], { type });
// Créer un OffscreenCanvas
const canvas = new OffscreenCanvas(size.width, size.height);
// Créer un Bitmap à partir du Blob
const bitmap = await createImageBitmap(blob);
// Récupérer un CanvasRenderingContext2D
const ctx = canvas.getContext("2d");
if (!ctx) throw new Error("Context2D is not defined");
// Dessiner l'image sur le canvas
ctx.drawImage(bitmap, 0, 0);
// Convertir en WebP
const webp = await canvas.convertToBlob({
type: "image/webp",
quality: 0.85,
});
// Convertir le Blob WebP en 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);
// Fournir le deuxième argument pour transférer la propriété de l'ArrayBuffer au thread UI
self.postMessage(output, [output.buffer]);
});

Passer une image au worker et récupérer l’image convertie

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 }; // récupérer la taille de l'image
// Envoyer au Web Worker et transférer la propriété
worker.postMessage(
{
buffer,
type,
size
},
[buffer]
);
});

Points importants

  1. OffscreenCanvas : une API Canvas utilisable dans un Web Worker
  2. Transfert d’ArrayBuffer : on passe les données efficacement en transférant la propriété
  3. convertToBlob : permet de convertir vers WebP et d’autres formats via OffscreenCanvas

Résumé

En combinant OffscreenCanvas et Web Worker, on peut exécuter un traitement d’image sans bloquer le thread principal.

Quand il faut traiter beaucoup d’images ou exécuter des conversions coûteuses, cette approche conserve la réactivité de l’interface et améliore l’expérience utilisateur.