Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Speed up the drawing of many points on a HTML5 canvas element

I need to draw a lot of points on a HTML5 canvas, and it is taking pretty long. My code looks like this:

var points = getPoints() // Array of {x,y,color}
var ctx = canvas.getContext("2d");

for (var i = 0; i < points.length; i++) {
   ctx.fillStyle = points[i].color
   ctx.beginPath()
   ctx.arc(points[i].x, points[i].y, radius, 0, Math.PI * 2, true)
   ctx.fill() }

I am wondering what performance tweaks I could do to speed this up. I only have 5 different colors. For example, would I benefit form sorting the points list on-the-fly to change ctx.fillStyle only 5 times instead of one time per point?

like image 386
Nicolas Avatar asked Dec 17 '12 14:12

Nicolas


Video Answer


1 Answers

For example, would I benefit form sorting the points list on-the-fly to change ctx.fillStyle only 5 times instead of one time per point?

In my experience, yes - changing .fillStyle frequently is quite expensive.

I had code that was plotting a large number of rectangles in a canvas and the time to plot rectangles with only two infrequently varying colours was significantly better than plotting with many frequently changing colours.

Anyhow, since you only have five different colours:

  1. Create an off-screen canvas into which you can draw five circles
  2. Use .drawImage() to blit the right colour circle into your destination canvas without having to recalculate the arc coordinates
  3. Assign points[i] to an local variable inside the loop to avoid dereferencing it over and over.

On my laptop this code is drawing 3000 circles on a 400x400 canvas in 7 milliseconds:

var colours = ['red', 'green', 'blue', 'yellow', 'magenta'];
var n = colours.length;
var r = 10;
var d = r * 2;

var off = document.createElement('canvas');
off.width = n * d;
off.height = d;
var ctx = off.getContext('2d');  

for (var i = 0; i < n; ++i) {
    ctx.fillStyle = colours[i];
    ctx.beginPath();
    ctx.arc(i * d + r, r, r, 0, 2 * Math.PI);
    ctx.closePath();
    ctx.fill();
}

var canvas = document.getElementById('canvas');
var ctx2 = canvas.getContext('2d');
var t0 = Date.now();
for (var i = 0; i < 3000; ++i) {
    var c = Math.floor(n * Math.random());
    var x = Math.floor(canvas.width * Math.random());
    var y = Math.floor(canvas.height * Math.random());
    ctx2.drawImage(off, c * d, d, d, x - r, y - r, d, d);
}
var t1 = Date.now();
alert((t1 - t0) + "ms");

​See http://jsfiddle.net/alnitak/Dpgts/

like image 119
Alnitak Avatar answered Oct 28 '22 14:10

Alnitak