My JavaScript book is out! Don't miss the opportunity to upgrade your beginner or average dev skills.

Thursday, January 15, 2009

ellipse and circle for canvas 2d context

These days I am working inside an ExtJS panel to create some sophisticated drawing stuff, and I discovered (too late ...) that there is almost nothing in canvas 2d context to draw circle or ellipse.

That's why I have extended Google excanvas library and/or native canvas 2d context prototype to let us create circles and ellipse in the same simple way we can create rectangles.

Here the extended proto:

(function(){
// Andrea Giammarchi - Mit Style License
var extend = {
// Circle methods
circle:function(aX, aY, aDiameter){
this.ellipse(aX, aY, aDiameter, aDiameter);
},
fillCircle:function(aX, aY, aDiameter){
this.beginPath();
this.circle(aX, aY, aDiameter);
this.fill();
},
strokeCircle:function(aX, aY, aDiameter){
this.beginPath();
this.circle(aX, aY, aDiameter);
this.stroke();
},
// Ellipse methods
ellipse:function(aX, aY, aWidth, aHeight){
var hB = (aWidth / 2) * .5522848,
vB = (aHeight / 2) * .5522848,
eX = aX + aWidth,
eY = aY + aHeight,
mX = aX + aWidth / 2,
mY = aY + aHeight / 2;
this.moveTo(aX, mY);
this.bezierCurveTo(aX, mY - vB, mX - hB, aY, mX, aY);
this.bezierCurveTo(mX + hB, aY, eX, mY - vB, eX, mY);
this.bezierCurveTo(eX, mY + vB, mX + hB, eY, mX, eY);
this.bezierCurveTo(mX - hB, eY, aX, mY + vB, aX, mY);
this.closePath();
},
fillEllipse:function(aX, aY, aWidth, aHeight){
this.beginPath();
this.ellipse(aX, aY, aWidth, aHeight);
this.fill();
},
strokeEllipse:function(aX, aY, aWidth, aHeight){
this.beginPath();
this.ellipse(aX, aY, aWidth, aHeight);
this.stroke();
}
};
for(var key in extend)
CanvasRenderingContext2D.prototype[key] = extend[key];
if(!this.G_vmlCanvasManager)
G_vmlCanvasManager = {init:function(){}, initElement:function(el){return el}};
})();


The last part is to obtain the same behavior with any browser and runtime create canvas:

var myCanvas = G_vmlCanvasManager.initElement(document.createElement("canvas"));


SImple and efficient? I hope so, since I spent more than a couple of minutes to be able to create a perfect circle via 1/4 of a rectangle (square ... but how could I remember the bloody constant to do that?) :P

P.S. I remind you that the simplest function to create a circle via a center is canvas.arc(x, y, radius, 0, Math.PI*2); and that this proto create a circle inside a specified square but ithout the same arc performances ( anyway, runtime rendering in IE and my personal version of excanvas even faster than FireFox 3 ... anybody interested? :-) )

7 comments:

Anonymous said...

Thanks, but:

s/strokeCircle:function(aX, aY, aWidth, aHeight)/strokeCircle:function(aX, aY, aDiameter)

Andrea Giammarchi said...

oops, cheers :)

Anonymous said...

Why not use arc(To) to draw circles, maybe its faster?

Anonymous said...

Yes, arc seems to be easier. And combining it with scale you'll got an ellipse.

Lars said...

Here's the version I'm using now, which also can rotate the ellipse from its default axis alignment:


// draw ellipse
// r = radius of base circle
// w,h = ratio of ellipse width,height to r
// a = angle of rotation (radians) clockwise from orthogonal
function ellipse(ctx, x, y, r, w, h, a) {
ctx.beginPath();
ctx.save();
ctx.translate(x, y);
ctx.rotate(a);
ctx.scale(w, h);
ctx.arc(0, 0, r, 0, Math.PI * 2);
ctx.restore();
}

It works.

Saranya SukumaR said...

Is there any similar function to draw a semicircle?

Unknown said...

Awesome dude!
Semicircle? you mean like a half one? just remove the two last bezierCurveTo;