Create an image in JavaScript's Web Worker (worker.js)

4.5k views Asked by At

I'm rewritng a small javascript for being able to put it in a worker.js like it is documented here:

Mozilla - Web_Workers_API

The worker.js shall display an image on an OffscreenCanvas like it is documented here:

Mozilla - OfscreenCanvas documentation

The initial script is using the following statement that obviously cannot be used in a worker.js file, because there is no "document":

    var imgElement = document.createElement("img");
    imgElement.src = canvas.toDataURL("image/png");

But how can I substitue the

document.createElement("img");

statement in the worker.js for still being able to use the second statement:

imgElement.src = canvas.toDataURL("image/png");

If anyone has any idea, it would be really appreciated. :)

1

There are 1 answers

2
Kaiido On

Just don't.

Instead of exporting the canvas content and make the browser decode that image only to display it, simply display the HTMLCanvasElement directly.

This advice already stood for before you switched to an OffscreenCanvas, but it still does.

Then how to draw on an OffscreenCanvas in a Worker and still display it? I hear you ask.

Well, you can request an OffscreenCanvas from an HTMLCanvasElement through its transferControlToOffscreen() method.

So the way to go is, in the UI thread, you genereate the <canvas> element that will be used for displaying the image, and you generate an OffscreenCanvas from it. Then you start your Worker to which you'll transfer the OffscreenCanvas.
In the Worker you'll wait for the OffscreenCanvas in the onmessage event and grab the context and draw on it.

UI thread

const canvas = document.createElement("canvas");
const offscreen = canvas.transferControlToOffscreen();
const worker = new Worker(url);
worker.postMessage(offscreen, [offscreen]);
container.append(canvas);

Worker thread

onmessage = (evt) => {
  const canvas = evt.data;
  const ctx = canvas.getContext(ctx_type);
  //...

All the drawings made from the Worker will get painted on the visible canvas, without blocking the UI thread at all.

const canvas = document.querySelector("canvas");
const offscreen = canvas.transferControlToOffscreen();
const worker = new Worker(getWorkerURL());
worker.postMessage(offscreen, [offscreen]);


function getWorkerURL() {
  const worker_script = `
  onmessage = (evt) => {
    const canvas = evt.data;
    const w = canvas.width = 500;
    const h = canvas.height = 500;
    const ctx = canvas.getContext("2d");
    // draw some noise
    const img = new ImageData(w,h);
    const arr = new Uint32Array(img.data.buffer);
    for( let i=0; i<arr.length; i++ ) {
      arr[i] = Math.random() * 0xFFFFFFFF;
    }
    ctx.putImageData(img, 0, 0);
    for( let i = 0; i < 500; i++ ) {
      ctx.arc( Math.random() * w, Math.random() * h, Math.random() * 20, 0, Math.PI*2 );
      ctx.closePath();
    }
    ctx.globalCompositeOperation = "xor";
    ctx.fill();
  };
  `;

  const blob = new Blob( [ worker_script ] );
  return URL.createObjectURL( blob );
}
canvas { border: 1px solid; }
<canvas></canvas>