HTML Canvas
HTML5 gave us something new to play with. It is called the <canvas> element.
What can you do with the <canvas> element? Well, you can draw text and graphics on the <canvas>. You can display images. You can animate the <canvas>, and the <canvas> responds to events making it interactive. All, of this makes the <canvas> the perfect element for making games playable on the Web. These games have come to be known as HTML5 games.
Now comes the bad news. HTML only puts the <canvas> on a Web page. It does not control the <canvas>. To control the <canvas> you need some other code.
When working in 2d, we will use JavaScript. If working in 3d, we use WebGL. But WebGL is also a JavaScript API. So... JavaScript.
Let's jump right to it then. Here is how to make the animation up above. I will explain it line by line in as much boring detail as I can possibly muster.
<!doctype html>
<html>
<head>
<title>My Canvas</title>
</head>
<body>
<canvas id="canvas" width="400" height="400">
Your browser does not support the canvas element.
</canvas>
<script>
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var box1 = 0;
var box2 = 0;
function clearScreen() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
}
function drawbox1 () {
ctx.translate(200, 200);
ctx.rotate(-box1 * Math.PI / 180);
ctx.lineWidth = 2;
ctx.strokeStyle = "black";
ctx.strokeRect(-70,-70,140,140);
ctx.rotate(box1 * Math.PI / 180);
box1 +=10;
ctx.translate(-200, -200);
}
function drawbox2 () {
ctx.translate(250, 100);
ctx.rotate(box2 * Math.PI / 180);
ctx.lineWidth = 2;
ctx.strokeStyle = "red";
ctx.beginPath();
ctx.moveTo(50,0);
ctx.lineTo(0,-50);
ctx.lineTo(-50,0);
ctx.lineTo(0,50);
ctx.lineTo(50,0);
ctx.stroke();
ctx.rotate(-box2 * Math.PI / 180);
box2 += 3;
ctx.translate(-250, -100);
}
function drawScreen() {
clearScreen();
drawbox1();
drawbox2();
}
setInterval(drawScreen, 50);
</script>
</body>
</html>
First we add a <canvas> element to our Web page.
<!doctype html>
<html>
<head>
<title>My Canvas</title>
</head>
<body>
<canvas id="canvas" width="400" height="400">
Your browser does not support the canvas element.
</canvas>
</body>
</html>
It is important to give an id attribute to your <canvas>. We use it later. I would also define the width and height attributes. The text nested inside the <canvas> tags will only display if someone is still using an older, uncool, out of date browser.
Now it is time to start controlling the <canvas> element with some script.
<body>
<canvas id="canvas" width="400" height="400">
Your browser does not support the canvas element.
</canvas>
<script>
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
</script>
</body>
Inside a <script> element, I declare two variables; one called canvas and one called ctx.
The canvas variable is assigned the value returned from a document.getElementById method call. The document is an object referring to your Web page, of course. And the getElementById method gives you the element with the supplied id. This is why it is important for elements to have unique id's.
The second variable I called ctx. I assigned it the value returned from calling canvas's getContext method. What was canvas again? That was my first variable, which now refers to the <canvas> element. The getContext method gives me access to all the built in drawing functions that make up the canvas's API (application programming interface). We will use some of those drawing functions coming up. For the 3d API we would call getContext("webgl").
Even though I used two variables in the above example, you could just as easily have used only one. Like this.
var ctx = document.getElementById("canvas").getContext("2d");
Now that we have access to the drawing functions, let's draw a box.
<body>
<canvas id="canvas" width="400" height="400">
Your browser does not support the canvas element.
</canvas>
<script>
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
ctx.lineWidth = 2;
ctx.strokeStyle = "red";
ctx.beginPath();
ctx.moveTo(50,0);
ctx.lineTo(0,50);
ctx.lineTo(50,100);
ctx.lineTo(100,50);
ctx.lineTo(50,0);
ctx.stroke();
</script>
</body>
Wow, a lovely red box. The ctx that I keep typing over and over is my second variable. I keep calling it's built in functions and variables. The lineWidth variable sets the thickness of the lines I will draw. I set it to 2 pixels wide. The strokeStyle variable lets me set a color. The beginPath function starts a new drawing path.
The moveTo function moves to a spot on the canvas without making any lines. The 50 and the 0 that I supplied as argument values moved to my starting point of 50 pixels from the left side and 0 pixels down from the top.
The lineTo function is like the moveTo function, only it will draw lines as we move around.
Last I called the stroke function. This is the call to "draw it". And just like that, we get a red box.
There is another way to draw a box. We could use the strokeRect function like this.
<body>
<canvas id="canvas" width="400" height="400">
Your browser does not support the canvas element.
</canvas>
<script>
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
ctx.lineWidth = 2;
ctx.strokeStyle = "black";
ctx.strokeRect(10,10,140,140);
</script>
</body>
Well, that seems a lot shorter. Why didn't I just do that? Because.
The strokeRect function takes four arguments. The first two values are the location of the top left corner of the rectangle. It is 10 pixels from the left edge of the canvas and 10 pixels down from the top edge of the canvas. The next two values are the length and the height of the rectangle.
Notice that with the strokeRect function, you do not need to call beginPath or stroke. It is all taken care of for you with the strokeRect function.
Okay, so now we know how to draw boxes. How do we get them to rotate?
I have written two functions for drawing the boxes, drawbox1 and drawbox2. Let's look at drawbox1.
function drawbox1 () {
ctx.translate(200, 200);
ctx.rotate(-box1 * Math.PI / 180);
ctx.lineWidth = 2;
ctx.strokeStyle = "black";
ctx.strokeRect(-70,-70,140,140);
ctx.rotate(box1 * Math.PI / 180);
box1 +=10;
ctx.translate(-200, -200);
}
The first function call inside of drawbox1 is ctx.translate. This function moves the origin of the canvas. Normally the 0,0 origin point of the canvas is the top left corner. But now the 0,0 origin point is 200 pixels right of the left edge and 200 pixels down from the top.
I moved the origin point so that when I call the strokeRect function, I can draw the box centered around the new origin point and have it show up on screen completely.
After the call to translate is a new function call, ctx.rotate. This function just rotates the drawing area. The supplied argument should be a value in radians. So what is this "multiplied by Math.PI divided by 180" nonsence? It is just me converting radians to degrees, which is absolutely unnecessary. If you just work in radians, you avoid unnecessary extra computation. I could for example remove the "* Math.PI / 180" from the rotate functions and change "box1 +=10" to "box1 += .17".
As for that box1... it is just a variable. I declared it at the beginning of my script. First there was var canvas and then var ctx. Then came two more variables, box1 and box2. They have a start value of 0, but I increase their values each time the drawbox functions are called. These increasing variables determine how much each box rotates with each function call. By increasing the rotation value each time, the boxes spin.
So to recap, I translate origin, then rotate, then draw a box. Then I rotate back by the same amount. Next I increase the value of my rotation variables (box1 or box2). Then I translate origin back. And all this happens in the drawbox frunctions.
After the drawbox functions I have a drawScreen function.
function drawScreen() {
clearScreen();
drawbox1();
drawbox2();
}
Inside the drawScreen function I make a call to a function called clearScreen.
function clearScreen() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
}
I declared this function near the top of my script just after I declared my variables. I bet you figured out what it does.
So, my drawScreen function starts by calling clearScreen, which oddly enough gives me a fresh clear canvas to draw on. Then drawScreen calls the two drawbox functions and that leaves me with my two boxes on the canvas.
To animate the spin, we just need to keep calling the drawScreen function. That is what this line of code does.
setInterval(drawScreen, 50);
I have the setInterval function call the drawScreen function every 50 milliseconds. Remember 1000 milliseconds is equal to 1 second. This means my animation is happening 20 times per second.
And this concludes the introduction to the <canvas> element tutorial. In my next tutorials we will begin using the <canvas> element to make games!
Comments
Post a Comment