1

is it possible to draw onto the canvas element without freezing the browser? I am printing approx. 3.000.000 dots to it, which freezes my browser for 2-3s.

Thank you!

This is what's happening 3 million times in a loop

for (var m = 0; m <= data.length - 1; m++) {
   ....
   ctx_raw.beginPath();
   ctx_raw.fillStyle = DATA_CONFIG.color;
   ctx_raw.fillRect(x_value, y_value, 1, 1);
}

Edit 1: Improvement by Chris G is 30% more efficient

const image_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
const data = image_data;

var index = (x_value + y_value * canvas.width) * 4;

data[index ] = 0;
data[index + 1] = 0;
data[index + 2] = 0;
data[index + 3] = 255;
gear
  • 181
  • 1
  • 11
  • How is that code called? setInterval? requestAnimationFrame? Or just once? –  May 21 '21 at 09:23
  • I updated the question. The loop is called after a button-press – gear May 21 '21 at 09:25
  • 1
    It's just pixels so you could manipulate the canvas's imageData directly. To draw asynchronously, use an interval and split the data into groups. –  May 21 '21 at 09:34
  • Is it faster to manipulate the canvas imageData directly? Is the canvas manipulation async if I move the code into an interval? – gear May 21 '21 at 10:05
  • 1
    I'm pretty sure it's faster than drawing single pixel rectangles, yes. True async can only be achieved with a Worker, but using an interval and drawing the dots on groups should prevent the browser from hanging. It'll obviously take longer to draw all dots that way though. –  May 21 '21 at 10:08
  • 1
    Thank you Chris. I did an benchmark and drawing the pixels, by manipulating the image data is 30% faster!! Later I will try a "worker" solution – gear May 21 '21 at 11:52
  • You can get even more over Chris's improvements by using a Uint32Array view instread of the default Uint8ClampedArray, this way you set each pixel in one setter call instead of requiring four per pixels. But, reading your first snippet, are all these points really the same color? – Kaiido May 22 '21 at 01:26
  • Like in this post? https://stackoverflow.com/a/39220491/15205839 Concerning the Color, I really just need one Color. But I also could pass the Color Code to the function :) thanks!! – gear May 22 '21 at 12:40

1 Answers1

2

You can:

  1. Create a a Worker, which will run in a separate CPU thread, if available,
  2. (optional) Send parameters to the worker using .postMessage(),
  3. Render the image inside the worker using OffscreenCanvas,
  4. Extract the pixel data using .getImageData(),
  5. Send the data to the Window (main thread) using .postMessage(),
  6. Recieve the data on the Window and render it onto a <canvas> using .putImageData().

Another option is to separate the rendering into several event loop tasks. (See How to queue a (macro)task in the JavaScript task queue?)

D. Pardal
  • 6,173
  • 1
  • 17
  • 37
  • Thank you very much. This seems to be a very clever way. In addition I found this tutorial on "workers". https://www.youtube.com/watch?v=0k9erXiSp9Q – gear May 21 '21 at 10:15
  • Why would you get the image data from this OffscreenCanvas? Just render its placeholder canvas. – Kaiido May 21 '21 at 14:20