Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Issue on Drawing Multiple Circles on HTML5 Canvas

Can you please take a look at this demo and let me know how I can draw multiple circles in a canvas with different coordinate without repeating bunch of codes?

As you can see on Demo and following code

var ctx = $('#canvas')[0].getContext("2d");
ctx.fillStyle = "#00A308";
ctx.beginPath();
ctx.arc(150, 50, 5, 0, Math.PI * 2, true);
ctx.arc(20, 85, 5, 0, Math.PI * 2, true);
ctx.arc(160, 95, 5, 0, Math.PI * 2, true);
ctx.closePath();
ctx.fill();

I tried to have them under ctx but it is not correct so I tried to use for loop to create 50 points but I have issue on repeating and adding code like ctx.fill(); for all of them. Can you please let me know how I can fix this?

Thanks

like image 359
Suffii Avatar asked May 15 '14 02:05

Suffii


1 Answers

Constantly creating and closing new paths is not good advice.

You should batch together all fills / strokes of the same style, and execute them in a single draw call. The performance difference between these approaches becomes apparent very quickly with increasing polygon count.

The way to go about it is to move the pen and make the path construction call for each circle; stroke/fill in the end in one shot. However, there's a quirk involved here. When you have the point moved to the center and draw the circle, you'd still see a horizontal radius line, drawn from the center of the circle, to the circumference.

To avoid this artefact, instead of moving to the center, we move to the circumference. This skips the radius drawing. Essentially all these commands are for tracing a path and there's no way to describe a discontinuity without calling closePath; usually moveTo does it but HTML5 canvas API it doesn't. This is a simple workaround to counter that.

const pi2 = Math.PI * 2;
const radius = 5;
ctx.fillStyle = '#00a308';
ctx.beginPath();

for( let i=0, l=coords.length; i < l; i++ )
{
    const p = coords[i],
    x = p.x,
    y = p.y;

    ctx.moveTo( x + radius, y ); // This was the line you were looking for
    ctx.arc( x, y, radius, 0, pi2 );
}

// Finally, draw outside loop
ctx.stroke();
ctx.fill();

Also worth considering, is using transformations, and drawing everything relative to a local frame of reference.

ctx.fillStyle = '#00a308';
ctx.beginPath();

for( let i=0, l=coords.length; i < l; i++ )
{
    const p = coords[i];

    ctx.save();
        ctx.translate( p.x + radius, p.y );
        ctx.moveTo( 0, 0 );
        ctx.arc( 0, 0, radius, 0, pi2 );
    ctx.restore();
}

ctx.stroke();
ctx.fill();
like image 91
Adria Avatar answered Oct 02 '22 23:10

Adria