Sunday, October 28, 2007

This is just an example of using "canvas" tag into html.

In this post I want to show some sample on drawing vector objects on HTML Canvas element. Canvas element is new HTML element which allows us to write some graphics objects into the browser through the JavaScript code. This element is supported by FireFox, Safary and some other browsers. Internet Explorer does not support this element directly, but you can use excanvas library which implements Canvas functionality through VML. To read more about Canvas you may visit Mozilla Developer Center.

In the following sample I want to draw an image of boy (I call this image “The boy from South Park:)). I try to do it using just some parts of such figures as circle, rectangle and some base transformations of canvas: translation, rotation and scale.

Let’s define some auxiliary functions:

drawCircle function draws filled circle.
ctx
is Canvas context
x
is a horizontal position of circle center
y
is a vertical position of circle center
r
is a radius of circle
color
is a color of circle

Example:

drawCircle(ctx, 100, 135, 50, "#00aa00");

drawEllipse function draws ellipse with center in point (x,y), the smallest radius equal w and proportion between radiuses equal to scale. Ellipse is rotated with angle and filled with color.

Example:

drawEllipse(ctx, 100, 135, 50, 1.5, Math.PI/4, "#00aa00");

drawArc function draws part of circle with center in point (x,y) radius w started at angle1 and finished at angle2. Boolean parameter fill describes should this part of circle be filled or striked. lineWidth means the width of line in the case of striking.

Example:

drawArc(ctx, 100, 135, 50, (3/4)*Math.PI, (1/4)*Math.PI, "#00aa00", false, 10);


Next functions just draw some parts of the boy.


drawTrousers(ctx, 100, 200, 80, 20);

Just draws a blue rectangle. J

drawShoes(ctx, 85, 305, 115, 305);

Just draws two small parts of circles.

drawJacket(ctx, 100, 180, 50, 60, "#7C4940");

This function is more complex. It draws half part of filled ellipse. Bottom of the jacket is a part of arc with lineWidth equal 10. And, of cause, here we can see 3 lines.

drawHands(ctx, 100, 180, 45, "#CC0000");

Every hand is just two red circles. One for hand and one for thumb.

drawCollar(ctx, 100, 120, 30, "#CC0000");

Collar is an ellipse which will be covered with head.

drawFace(ctx, 100, 100, 50, 45);

Face is a combination of filled ellipses.

drawPompon(ctx, 100, 55, 10);

To draw it I just write line and rotate it several times.

drawPartOfCap(ctx, 100, 300, 215, 10, "#CC0000");

You can see just part of an arc.

drawMouth(ctx, 98, 155);

And one more part of an arc.

So, finally, applying this drawings one by one we will get the image of boy and be happy.



Here you can see the source HTML file with JavaScript code which draws an image of the boy.

<html>
<head>
</head>
<body>
<canvas id="Canvas1" width=200 height=270 style="border:1px solid red;">
</canvas>
<script>
window.onload = function() {
var canvas = document.getElementById("Canvas1");
var ctx = canvas.getContext('2d');

drawTrousers(ctx, 100, 200, 80, 20);
drawShoes(ctx, 85, 305, 115, 305);
drawJacket(ctx, 100, 180, 50, 60, "#7C4940");
drawHands(ctx, 100, 180, 45, "#CC0000");
drawCollar(ctx, 100, 120, 30, "#CC0000");
drawFace(ctx, 100, 100, 50, 45);
drawPompon(ctx, 100, 55, 10);
drawPartOfCap(ctx, 100, 300, 215, 10, "#CC0000");
drawMouth(ctx, 98, 155);


function drawHands(ctx, x, y, r, color) {
ctx.save();
drawCircle(ctx, x + r, y, 10, color);
drawCircle(ctx, x + r - 6, y - 4, 5, color);
drawCircle(ctx, x - r, y, 10, color);
drawCircle(ctx, x - r + 6, y - 4, 5, color);
ctx.restore();
}

function drawJacket(ctx, x, y, w, h, color) {
ctx.save();
ctx.scale(w/h, 1);
drawArc(ctx, x * (h/w), y, h, 0, Math.PI, color, true);
ctx.restore();

drawArc(ctx, x, y - h*2 + 8, h*2, Math.PI*(10/16), Math.PI*(6/16), color, false, w/3);

drawLine(ctx, x,y + 15,x,y-h, "#000000", 2);
drawLine(ctx, x - w*(0.6),y - h*(0.4),x - w*(0.7),y - h*(0.1),"#000000", 1);
drawLine(ctx, x + w*(0.6),y - h*(0.4),x + w*(0.7),y - h*(0.1),"#000000", 1);
}

function drawCollar(ctx, x, y, r, color) {
drawEllipse(ctx,x,y,r,1.3,0,color);
}

function drawTrousers(ctx, x, y, w, h) {
ctx.save();
ctx.translate(x,y);
ctx.fillStyle = "#000099";
ctx.fillRect(-w/2, -h/2, w, h);
ctx.restore();
}

function drawShoes(ctx, x, y, x1, y1) {
drawArc(ctx, x, y, 100, -Math.PI*(6/16), -Math.PI*(10/16), "#000000", true);
drawArc(ctx, x1, y1, 100, -Math.PI*(6/16), -Math.PI*(10/16), "#000000", true);
}

function drawMouth(ctx, x, y) {
drawArc(ctx, x, y, 25, -Math.PI*(6/16), -Math.PI*(9/16), "#000000", false, 1);
}

function drawLine(ctx, x1, y1, x2, y2, color, lineWidth) {
ctx.save();
ctx.strokeStyle = color;
ctx.lineWidth = lineWidth;
ctx.beginPath();
ctx.moveTo(x1,y1);
ctx.lineTo(x2,y2);
ctx.stroke();
ctx.restore();
}

function drawFace(ctx, x, y, w, h) {
ctx.save();
ctx.translate(x,y);
ctx.scale(w/h, 1);
drawCircle(ctx, 0, 0, h, "#FEC6A5");
drawArc(ctx, 0, 0, h, - Math.PI/12, - (11/12)*Math.PI, "#000099", true)

// draw left eye
drawEllipse(ctx, w/4, w/10, 12, 1.2, Math.PI/4, "#FFFFFF");
drawCircle(ctx, w/6, w/10, 2, "#000000");

// draw right eye
drawEllipse(ctx, -w/4, w/10, 12, 1.2, -Math.PI/4, "#FFFFFF");
drawCircle(ctx, -w/6, w/10, 2, "#000000");

ctx.restore();
}

function drawPartOfCap(ctx, x, y, r, height, color) {
ctx.save();
ctx.lineCap = "round";
drawArc(ctx,x, y, r, -(15/35)*Math.PI, -Math.PI*(20/35), color, false, height);
ctx.restore();
}

function drawPompon(ctx, x, y, r) {
ctx.save();
ctx.translate(x,y);
ctx.lineWidth = r / 6;
ctx.lineCap = "round";
ctx.strokeStyle = "#CC0000";
for (var i = 0; i < 8; i++) {
ctx.rotate(Math.PI/8);
ctx.beginPath();
ctx.moveTo(-r, 0);
ctx.lineTo(r, 0);
ctx.stroke();
}
ctx.restore();
}

function drawArc(ctx, x, y, w, angle1, angle2, color, fill, lineWidth) {
ctx.save();
if (fill)
ctx.fillStyle = color;
else {
ctx.strokeStyle = color;
if (lineWidth) ctx.lineWidth = lineWidth;
}
ctx.translate(x,y);
ctx.beginPath();
ctx.arc(0, 0, w, angle1, angle2, true);
if (fill)
ctx.fill();
else
ctx.stroke();
ctx.restore();
}

function drawEllipse(ctx, x, y, w, scale, angle, color) {
ctx.save();
ctx.translate(x,y);
ctx.rotate(angle);
ctx.scale(scale, 1);
drawCircle(ctx, 0, 0, w, color);
ctx.restore();
}

function drawCircle(ctx, x, y, r, color) {
ctx.save();
ctx.beginPath();
ctx.fillStyle = color;
ctx.arc(x, y, r, 0, Math.PI*2, true);
ctx.fill();
ctx.restore();
}
}
</script>
</body>
</html>

5 comments:

Historiador said...

Really cool graphic!.
So I am new to this Canvas thing, seems very powerfull.

Anonymous said...

What is the memory usage of the canvas tag?

Is it the same in FF and in the IE implementation?

Thanks

serp said...

Unfortunately, I have no information about memory usage. As for realisations for IE and FF it seemed to be the same but I found some unnecessary differences. By default IE have not Canvas tag. This functionality added by means of IECanvas library from Google, which uses standard VML plugin for IE.

Anonymous said...

where is a demo?

serp said...

vitalic, you should just copy Html code from my post and insert it to your, for example, index.html file. Then you should open it with FireFox browser.

I added the source of html to my post because I don't know how to add html file and link for it to my post using this blogging system (if this is actually possible).

You should use FireFox browser, because just FireFox has direct implementation of Canvas tag. (To use Canvas in IE you should also use ExCanvas JavaScript library from Google).