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:

  1. Thanks, but:

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

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

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

    ReplyDelete
  4. 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.

    ReplyDelete
  5. Is there any similar function to draw a semicircle?

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

    ReplyDelete

Note: Only a member of this blog may post a comment.