Nicolas
Nicolas

Reputation: 2367

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?

Upvotes: 12

Views: 10982

Answers (1)

Alnitak
Alnitak

Reputation: 339786

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/

Upvotes: 17

Related Questions