The HTML5 Canvas tag and Javascript
New to HTML5 is the CANVAS tag. This is a blank surface that allows you to add things like shapes, images, text, and perform animation. In fact, whole games have been written in Javascript with the CANVAS tag. Let's have a look at the basics. First, though, download our template file for the canvas section: The HTML 5 Canvas Template. This will save you from having to type it all out for this and the subsequent lessons.
Getting Started with the Canvas Tag
The HTML for the canvas tag is fairly simple. It's just this:
<CANVAS></CANVAS>
You then add height and width attributes:
<CANVAS HEIGHT="400" WIDTH="500"></CANVAS>
To get at the canvas with Javascript, add an ID:
<CANVAS HEIGHT="400" WIDTH="500" ID="canvas_1"></CANVAS>
For browsers that don't support the canvas tag, you can add some text between the two tags:
<CANVAS HEIGHT="400" WIDTH="500" ID="canvas_1">
Your browser is like really old, dude - no canvas support!
</CANVAS>
Your browser is like really old, dude - no canvas support!
</CANVAS>
Because the HTML5 canvas is a blank space, we'll add a border round it:
<SECTION style="border-style: solid; border-width: 2px; width: 500px;">
<CANVAS HEIGHT="400" WIDTH="500" ID="canvas_1">
Your browser is like really old, dude - no canvas support!
</CANVAS>
Your browser is like really old, dude - no canvas support!
</CANVAS>
</SECTION>
Let's also add a button to the page. When the button is clicked a function can be called that draws to the canvas:
<P>
<INPUT TYPE ="Button" VALUE=" Draw " onClick="drawOnCanvas()">
</P>
So all of the basic code you should have is this:
The first line for your drawCanvas function is this:
var canvas = document.getElementById("canvas_1");
This just gets a reference to the HTML canvas in betwen the two BODY tags.
The next thing you need to do is a Javascript check to see if the browser supports a method calledgetContext:
if ( canvas.getContext ) {
}
The context is a 2D or 3D drawing surface (there's not much support for the 3D context in any browser, at the moment). If the getContext method is not supported, you can add an error message with an ELSE part. We'll leave it off, though.
If getContext is supported you can get a reference to the 2D context like this:
if ( canvas.getContext ) {
var canvas_context = canvas.getContext("2d");
}
After the equal sign, we have our canvas variable from the first line. After a dot comes thegetContext method. In between the round brackets of getContext we have 2D between quote marks. (You would change this to 3D if there was support for it, and you fancied doing a bit of 3D drawing.)
You can add an alert box in there, just to see if everything is working properly. You can always comment it out, once we get going:
if ( canvas.getContext ) {
var canvas_context = canvas.getContext("2d");
alert("2D context supported");
alert("2D context supported");
}
Click your DRAW button and you should see the alert message popup. If not, go through your code and locate your errors.
HTML5 Canvas: Drawing Rectangles
To draw rectangles on a HTML5 canvas, three methods are available. They are:
strokeRect
fillRect
clearRect
fillRect
clearRect
(There is actually a fourth rectangle method called rect. But this is used in conjunction with beginPath and fill. You'll see how to use these later.)
In between the round brackets of each method you need four things:
x, y, width, height
The X argument is how far from the left you want to start drawing your rectangle, while the Y argument is how far down from the top of your canvas. Height and width are obviously the size you want your rectangle.
To try out rectangles, add the following line to your code from the previous section:
canvas_context.strokeRect( 20, 30, 100, 50 );
A stroke rectangle is one that just had a border, with no colour. The one above starts at 20 pixels from the left edge of the canvas and 30 pixels down from the top. The width is 100 pixels wide and 50 high.
Save your work and refresh your web page in the browser. When you click the button, it should look like this:
If you want a filled rectangle then the method to use is fillRect. Add the following line to your code, just below the others:
canvas_context.fillRect( 20, 100, 100, 50 );
This will draw a filled rectangle 100 pixels down and 20 from the left. If you don't specify a fill colour (which we'll do in a moment) then you get a black rectangle. But here's what your canvas should look like when you click the button after a refresh:
You can clear areas of a filled rectangle. You do so with the clearRect method. Add the next line to your code:
canvas_context.clearRect( 35, 110, 50, 30 );
This clears a rectangular area starting at 35 pixels from the left and 110 pixels down. The size of the cleared area is 50 pixels by 30 pixels.
Your function should now look like this, though:
Save your work and refresh. When you click the button, your rectangles should look like this:
You can fill your rectangles with a colour. For this you need a fillStyle method. There are two to choose from:
canvas_context.fillStyle = "rgb( 255,0,0 )";
canvas_context.fillStyle = "rgba( 255, 0,0 , 0.5 )";
canvas_context.fillStyle = "rgba( 255, 0,0 , 0.5 )";
The first one is for solid colour. The three sets of numbers are for the values Red, Green and Blue. The numbers go from 0 to 255. A value of zero means switch off that colour, while a value of 255 means switch the colour full on.
(As well as using RGB colours, you can use a named colour like "blue" or "red". You can also use a hexadecimal colour value like "#345678".)
The second colour style again gives you an RGB colour, but this time with an alpha value option (the "a" after "rgb"). It means, how transparent do you want the fill? The values go from 0 to 1.
Try a fill style out for yourself. Add the following two lines to your code:
canvas_context.fillStyle = "rgba( 255,0,0, 0.5 )";
canvas_context.fillRect( 20, 170, 100, 50 );
canvas_context.fillRect( 20, 170, 100, 50 );
The first line sets up an rgba colour value. The alpha value is set to 0.5. The rectangle is just below the first two. The results in a browser look like this:
Now change the fillRectangle Y value to this:
canvas_context.fillRect( 20, 100, 100, 50 );
So we're putting it on top of the second rectangle, the black one. The results in a browser will be this:
The black rectangle is now a reddish colour, the result of the transparent red one being overlaid.
Now take out the "a" from "rgba". And delete the 0.5. So just this for the fillStyle:
canvas_context.fillStyle = "rgb( 255, 0, 0 )";
When you click the button you'll see a red rectangle. The black one is hidden beneath it.
Draw Circles and Arcs on a HTML5 Canvas
You can draw circles and arcs on a HTML5 canvas. This is done with the arc method. Between the round brackets of arc, you need 6 arguments:
x, y, radius, start_angle, end_angle, anticlockwise
You would use them like this:
canvas_context.arc( 75, 75, 50, start_angle, end_angle, true );
The X and Y are the coordinates for the centre of your circle. In the code above, we want the centre of the circle to be 75 pixels from the left edge of the canvas and 75 pixels from the top. We want the radius to be 50 pixels.
To get the start and end angles take a look at the following diagram:
A starting angle of 0 degrees is on the right, in the middle. An end angle of 180 degrees would be on the left, opposite it. However, angles are measured in radians. To convert from degrees to radians, you can use this:
var start_degrees = 0;
var start_angle = ( Math.PI/180 ) * start_degrees;
var start_angle = ( Math.PI/180 ) * start_degrees;
var end_degrees = 180;
var end_angle = ( Math.PI/180 ) * end_degrees;
var end_angle = ( Math.PI/180 ) * end_degrees;
The Math.PI /180 part is where you do the converting to radians. You then multiply by how many degrees you actually want.
The other part of the arc method is anticlockwise. This is a Boolean value you set as either true or false. True means draw the arc anticlockwise, and false mean clockwise.
Unlike rectangles, however, you need to call another method of the 2D context object before you can draw circles and arcs, the beginPath method:
canvas_context.beginPath();
This just tells the 2D context object that you want to start drawing a path.
After you have drawn your path, and used the arc method, you need to add a fill or stroke:
canvas_context.stroke();
canvas_context.fill();
canvas_context.fill();
You can also add a stroke style:
canvas_context.strokeStyle = "rgb( 0,222,0 )";
canvas_context.stroke();
canvas_context.stroke();
Or a fill style:
canvas_context.fillStyle = "rgb( 0,222,0 )";
canvas_context.fill();
canvas_context.fill();
Let's get some practice done.
You can create a new page for this. Locate your canvas template.txt file in Windows Explorer, or Finder on a Mac. Double click the file to open it. Now save it with the name circles.html.
Once you have your new page, add the following code:
You don't have to have the same stroke colour as us. You can choose different RGB values, if you want. Or try a hexadecimal or named colour value .
Save your work and view the results in your browser. When you click the button, you should see this:
Because the numbers for the start and end angles were 0 and 360, a full circle is drawn.
Change your start and end angles to 180 and 270. Save the changes, refresh the page, and click the button again. The result will be this:
Because we had anticlockwise set to true the arc is drawn from 180 anticlockwise to 270. Change the anticlockwise Boolean value to false. When you click the button, you'll see this:
The arc is now shorter. This is because it is being drawn clockwise form 180 degrees to 360.
If you want to start drawing a new circle or arc (or any shape, really) you should call beginPathagain. What this does is to erase all the current paths stored in memory. It doesn't erase anything on the canvas, however, just whatever was previously on the list to be drawn. With that in mind, try these exercises. First, though, it might be easier to create a separate function to convert degrees to radians. You could then make the call like this:
var start_angle = getRadians(180);
We've called our function getRadians, and then passed it an angle in degrees. The function would be this:
function getRadians(dgrs) {
var degrees = ( Math.PI/180 ) * dgrs;
return degrees;
return degrees;
}
OK, here are the exercises.
Exercise
Draw a filled circle to the right of the arc you already have. Set the fill colour to blue.
Draw a filled circle to the right of the arc you already have. Set the fill colour to blue.
Exercise
Draw a filled, yellow semi circle.
Draw a filled, yellow semi circle.
Exercise
Draw another semi circle, make it the bottom (or top) half of the one you already have. Use any colour for the fill style.
Draw another semi circle, make it the bottom (or top) half of the one you already have. Use any colour for the fill style.
When you complete all three exercises, your canvas should look something like this:
Drawing Paths and Lines
To make shapes other than circles and rectangles you can create paths on a HTML5 canvas. These are lines that are connected together. You then close the lines and specify a fill or stroke. The methods available when drawing paths are these:
moveTo
lineTo
closePath
lineTo
closePath
The first one, moveTo, is like lifting your pencil from a piece of paper. Move the pencil to a new area of the paper and you can start drawing a new shape. In between the round brackets of the method you need two arguments:
moveTo( X, Y )
X is how far from the left edge of the canvas you want to start from; Y is how far down from the top of the canvas. The value is in pixels.
The second one, lineTo, is used for drawing straight lines. Again, the method takes two arguments:
lineTo( X, Y)
X and Y are the left an top positions of where you want the line drawn to.
The third method, closePath, is used to draw a line from your last point to the first one, thus closing your shape.
If that's not too clear, let's draw a triangle to illustrate what it all means.
You can create a new page for this from your template. Or with your circle code open, just clickFile > Save As in your text editor. Then type a new file name, something like paths.html. When the new page is created, delete the code between the curly brackets of the IF statement for the drawOnCanvas function. Now add the following code in its place:
You've met the beginPath method before. It clears the list of paths already in memory, resetting things for you to start again. The next line is this:
canvas_context.moveTo( 75, 100 );
We're moving our "pencil" to a position of 75 pixels left and 100 pixels down. Imagine that the point of the pencil is now on the paper.
Next, we use lineTo to move to a position of 150 pixels left and 200 down. A line is then drawn from the starting position (the one from moveTo) to the end position (the one from lineTo).
The next line moves the pencil to a new position with lineTo( 150, 25 ). Again, a line is drawn. This time from the previous lineTo X and Y positions to the new ones.
The second last line closes the path. You don't need anything between the round brackets because the 2D context has all your line points in a list. It just draws a line from the last point to the first one.
Save you work and try it out. Load your web page in a browser. When you click the button, you should see this:
To see what closePath does, comment the line out like this:
//canvas_context.closePath();
Save the change and refresh the page in your browser. Click the button again and you'll see your triangle change to this:
In the image above, a line is now not drawn from the end point to the first point. So if you don't want to close your paths, leave out the closePath method to keep them open.
If you want a filled triangle (or any other shape) use the fill method with a fill style: (If you don't include a fill style the colour used to fill the shape will be black.)
canvas_context.fillStyle = "rgb(255,0,0)";
canvas_context.fill();
canvas_context.fill();
The above two lines will get you a red triangle.
You can have a fill and a stroke:
canvas_context.fillStyle = "rgb( 255,0,0 )";
canvas_context.stroke();
canvas_context.fill();
canvas_context.stroke();
canvas_context.fill();
A stroke colour can be specified, too:
canvas_context.fillStyle = "rgb(255,0,0)";
canvas_context.strokeStyle = "rgb( 0, 0, 255 )";
canvas_context.stroke();
canvas_context.fill();
canvas_context.strokeStyle = "rgb( 0, 0, 255 )";
canvas_context.stroke();
canvas_context.fill();
The strokeStyle method works just like fillStyle. The effect of the code above is to add a 1 pixel blue border around the triangle. A value of 1 pixel is the default. However, you can set a different stroke width. This is done with the lineWidth method:
canvas_context.fillStyle = "rgb(255,0,0)";
canvas_context.strokeStyle = "rgb( 0, 0, 255 )";
canvas_context.lineWidth = 5;
canvas_context.stroke();
canvas_context.fill();
canvas_context.strokeStyle = "rgb( 0, 0, 255 )";
canvas_context.lineWidth = 5;
canvas_context.stroke();
canvas_context.fill();
The new triangle would then look like this:
Line ends and Line joins
As well as setting the width of your lines you can also set the line ends (caps) and the line joins. Use your code from the previous section for this lesson, or create a new web page from your template.
Line Caps
A line cap is what you want the end of your lines to look like. To see what choices you have, study the following image.
The first one is the default cap, and is called a butt. You don't have specify a line cap if you only want the default. The second one in the image above is if you want rounded ends on your lines. The final line has square ends. Notice how the round and square ends add length to each line.
The code for line caps is this:
canvas_context.lineCap = "round";
Instead of a value of "round" you can have "square" or "butt". If you want to try them out, here's some code for you:
canvas_context.beginPath();
canvas_context.lineWidth = 20;
canvas_context.lineCap = "round";
canvas_context.lineWidth = 20;
canvas_context.lineCap = "round";
canvas_context.moveTo(50, 100);
canvas_context.lineTo( 250, 100);
canvas_context.stroke();
canvas_context.lineTo( 250, 100);
canvas_context.stroke();
The code above also sets a line width with this:
canvas_context.lineWidth = 20;
To get just the one line select a starting position with moveTo You can then use LineTo for the end of your line.
Exercise
In the code above, the Y values are the same - 100. What do you think would happen if you had a different value for Y in lineTo( 250, 100)?
In the code above, the Y values are the same - 100. What do you think would happen if you had a different value for Y in lineTo( 250, 100)?
Exercise
Experiment with the square and butt values. Try to reproduce our image from above.
Experiment with the square and butt values. Try to reproduce our image from above.
Line Joins
You can also have a different style for when two or more of your lines meet. The choices you have are miter (American spelling), round and bevel. Miter is the default. Here's what they look like:
If you look at the bottom of the V you'll see the three different styles. The first is a miter join, the second is a round join, and the third is a bevel.
The code for a join is this:
The code for a join is this:
canvas_context.lineJoin = "round";
And here's the code for the round V above:
canvas_context.beginPath();
canvas_context.lineWidth = 20;
canvas_context.lineJoin = "round";
canvas_context.moveTo(75, 150);
canvas_context.lineTo(150, 250);
canvas_context.lineTo(230, 150);
canvas_context.stroke();
canvas_context.lineWidth = 20;
canvas_context.lineJoin = "round";
canvas_context.moveTo(75, 150);
canvas_context.lineTo(150, 250);
canvas_context.lineTo(230, 150);
canvas_context.stroke();
Exercise
Experiment with the other two joins. Try to reproduce our image from above.
Experiment with the other two joins. Try to reproduce our image from above.
Linear and Radial Gradient Fill Styles
Instead of filling your shapes with a solid colour you can fill them with a gradient. There are two types of gradient for the HTML5 canvas: linear and radial. To go from one colour to another in your gradient you set colour stops. If you're not sure what a colour stop is, take a look at this screenshot:
The colour stops are the three pink squares at the bottom. The one on the left is the darker colour, and the one on the right is the lighter colour. There's a colour midway between the two shades, halfway along. In your code, you type the different shades of colours you want to use. You don't have to use three stops, you can use more if you want (or fewer).
Let's have a look at some code for a liner gradient:
var canvas_context = canvas.getContext("2d");
var lin_grad = canvas_context.createLinearGradient( 0, 0, 0, 400 );
lin_grad.addColorStop(0, "#FC00F9" );
lin_grad.addColorStop(0.5, "#FB8AFA" );
lin_grad.addColorStop(1, "#FAE3FA" );
lin_grad.addColorStop(0.5, "#FB8AFA" );
lin_grad.addColorStop(1, "#FAE3FA" );
canvas_context.fillStyle = lin_grad;
canvas_context.fillRect(0, 0, 400, 400);
canvas_context.fillRect(0, 0, 400, 400);
So the method to use for a liner gradient is createLinearGradient. In between the round brackets of the method you need four numbers separated by commas. The first two are the X an Y positions of where you want to start the gradient. The second two numbers are, again, X and Y positions; but the these last two numbers are end points for the gradient. In our code we have 0, 0 for the start, and 0, 400 for the end. This will get you a gradient from top to bottom.
To add a colour stop the method to use is addColourStop. It goes after your 2D context object. In between the round brackets of addColourStop you need a position for you colour stop, and the actual colour. The positions go from 0 to 1. A position of 0.5 is half way between the two. The colour can be a hexadecimal colour, a RGB one, or a named colour like black, white red, etc. We're using hexadecimal values.
Once you have set up your gradient, you assign it to fillStyle. So you're just setting a gradient instead of solid colour.
The effect of the above code is this:
Radial Gradients
If you want a radial gradient instead of a linear one, the method to use is this:
createRadialGradient( x1, y1, radius1, x2, y2, radius2 );
You're setting up two circles, here. The first three arguments are x1, y1, radius1. The X and Y are for the centre of your first circle, the outer one. The inner circle is defined by using the final three arguments x2, y2, radius2. Again, the X and Y are for the centre of the circle. Here's some code:
You've already seen how to draw a circle. The line that sets up the radial gradient is this:
var rad_grad = canvas_context.createRadialGradient( 75, 75, 50, 60, 75, 30 );
For the first three numbers, a circle with a radius of 50 is defined. The position is 75 pixels from the left of the canvas and 75 pixels down from the top. For the second circle, the left ( X ) position is 60. The top position is the same, 75. The radius is smaller at 30 pixels.
The code to set up the colour stop positions uses RGB values, but you can use hexadecimal or named colours, if you prefer. Finally, we assign the radial gradient to the fillStyle method, then use fill.
The result of the above code is a radial gradient that looks like this:
Pattern Fill Styles for your HTM5 Canvas Shapes
You can turn images into patterns that you can then use for a fill style. To create a pattern, you use the method createPattern:
createPattern( image, pattern_type )
Between the round brackets of the method you need two thing separated by a comma: an image to use for your pattern, and a pattern type. The pattern type can be one of four values: repeat,repeat-x, repeat-y, no-repeat. A value of repeat mean "fill the space with the pattern"; a value of repeat-x means "fill across the page once, from left to right"; a value of repeat-y means "fill down from top to bottom, once only"; a value of no-repeat Is pretty self-explanatory.
The image can be created from a new image object, or can be a reference to one already in the BODY of your HTML (the image can also reside on another domain).
Study the code below to see how createPattern works.
After we get a 2D canvas context, a new image object is created:
var heart = new Image();
heart.src = "heart.gif";
heart.src = "heart.gif";
You saw how to create images like this in a previous section. The image we're using is in the GIF format, and is called heart. We're assigning this to the src property of the image object we created.
The next line is this:
heart.onload = function() {
Image objects also have an onload event you can use. This will delay execution of the code until any images have loaded. To the right of the equal sign we have
= function() {
We're using a function expression here (or anonymous function). A function expression goes on the right of the equal sign. It doesn't need a name (but can have one), just the word function followed by round brackets. Function expression are useful when you only want to execute a piece of code once and you're not going to be calling it again from elsewhere.
In between the curly brackets of the function, we have this:
var pattern = canvas_context.createPattern( heart, "repeat" );
canvas_context.fillStyle = pattern;
canvas_context.fillRect(0, 0, 500, 400);
canvas_context.fillStyle = pattern;
canvas_context.fillRect(0, 0, 500, 400);
We use the createPattern method with our heart image and a value of repeat. This is then assigned to a variable called pattern. The pattern is then handed to the fillStyle method. The final line fills a rectangle with a width of 500 and a height of 400. The starting position for the fill is the top left of the canvas ( 0, 0 ).
The result of the code is this:
If we change the pattern style to repeat-x we get this instead:
Drawing, Scaling, Slicing Images on a HTML5 Canvas
One useful method available to you is drawImage. There are three ways to use this method. If all you want to do is to place an image on your canvas then you can use drawImage like this:
drawImage( image, x, y );
In between the round brackets of drawImage you need an image to use, then a pair of X and Y coordinates in order to position it on your canvas:
var heart = new Image();
heart.src = "heart.gif";
heart.src = "heart.gif";
heart.onload = function() {
canvas_context.drawImage( heart, 10, 10 );
}
In the code above, we're placing the heart image 10 pixels from the left and 10 down from the top.
Image Scaling
The second way to use drawImage is for scaling images. It has an extra two arguments, width and height:
drawImage( image, x, y, width, height );
The first three arguments are the same. The other two are for how big you want your new version:
var heart = new Image();
heart.src = "heart.gif";
heart.src = "heart.gif";
heart.onload = function() {
canvas_context.drawImage( heart, 10, 10, 200, 200 );
}
The code above increases the size of the image to 200 by 200.
In the image below, the smaller heart has been placed on the canvas with the first use of drawImage. The bigger heart uses the second drawImage method:
Image Slicing
The third way to use drawImage is used for slicing images. It's a little trickier to use:
drawImage( image, source_x, source_y, w, h, dest_x, dest_y, w, h );
The idea is that you select a starting position for your source image (source_x and source_y, above). This is the image you want to take a slice from. You also specify how big a slice you want to take (width and height). You then choose a place on your canvas that you want to place the slice (dest_x and dest_y, above). Finally, type a width and height for the new slice.
We have placed an image on a web page with HTML. When the button is clicked we want to take three slices from it and make a triptych. We want to create this effect:
The HTML for the image is just this:
<IMG SRC="london.jpg" ID="london_eye">
We can get a reference to it with Javascript by using getElementById:
var img = document.getElementById( "london_eye" );
To draw the first slice, we used this line of code:
canvas_context.drawImage( img, 0, 0, 180, 300, 20, 20, 150, 300 );
The first four sets of numbers mean, "Grab a slice of the source image starting at 0, 0. The slice should be 180 pixels by 300". To place the slice on the canvas, the next four numbers are used. They mean, "Place the new slice at 20 pixels from the left and 20 pixels down from the top. The new slice should be 150 by 300". Here's the whole code:
Javascript Timers and the HTML5 Canvas
You can do simple animations with the canvas tag. There are, however, limitations that make coding busy animated scenes quite difficult with Javascript and the canvas tag. This is mainly because everything has to be cleared before you can draw a new scene. For example, suppose you wanted to move a black rectangle. You can't just move it to a new position. If you do, you'll have a black rectangle at the new position and one at the old position. You need to clear the entire canvas to erase the old one. (You can save the canvas state, however, and then restore it. But this is just used for static, non-moving parts of the scene.)
To see what all this means, create a new web page. Add the following as the HTML for the BODY section:
<BODY>
<SECTION style="border-style: solid; border-width: 2px; width: 500px;">
<CANVAS WIDTH="400" HEIGHT="400" ID="canvas_1">
Canvas tag not supported
</CANVAS>
Canvas tag not supported
</CANVAS>
</SECTION>
<P><INPUT TYPE ="Button" VALUE=" START " onClick="drawOnCanvas()"></P>
<P><INPUT TYPE="Button" VALUE=" STOP " onclick="stopTimer()"></P>
</BODY>
It's the same as before but with a new button to stop a timer.
For your drawOnCanvas function, add the following:
function drawOnCanvas() {
var canvas = document.getElementById("canvas_1");
if ( canvas.getContext ) {
canvas_context = canvas.getContext("2d");
doTimer();
doTimer();
}
}
So we just want to call a doTimer function. Here it is:
function doTimer() {
x_pos = 0;
timerID = setInterval( "moveBox()", 200 );
timerID = setInterval( "moveBox()", 200 );
}
We're setting up a variable called x_pos to begin with. We'll use this to move a black rectangle. Note that the variable doesn't start with var. This is because we want to make it a global function, meaning we need other functions to have access to it. If we set it up with var then x_pos would be local to the doTimer function.
For the setInterval timer we want to call a function with the name of moveBox. We want to call it every 200 milliseconds.
Here's the moveBox function to add to your code:
function moveBox() {
//canvas_context.clearRect( 0, 0, 400, 400 );
canvas_context.fillRect( x, 25, 50, 30 );
x = x + 10;
canvas_context.fillRect( x, 25, 50, 30 );
x = x + 10;
}
Notice the two comments before the call to clearRect:
//canvas_context.clearRect( 0, 0, 400, 400 );
You'll see why in a moment. To move the box, though, we have these two lines:
canvas_context.fillRect( x_pos, 25, 50, 30 );
x_pos = x_pos + 10;
x_pos = x_pos + 10;
The first number for fillRect is the X coordinate - how far from the left you want your rectangle. We're replacing this with our x_pos variable. We're then adding to 10 to x_pos each time the moveBox function is called by the setInterval timer.
The final part of the code to add is this:
function stopTimer() {
clearInterval( timerID );
}
This function stops the timer, and thus the animation.
Once you've finished typing the code, load the page into a browser and try it out. Click the START button. Click STOP after a few seconds. What you should see on your canvas is something like this:
The reason you get a black line stretching from left to right is because the canvas hasn't been cleared before each rectangle was drawn. So the previous one is still there.
Take the comments out from clearRect:
canvas_context.clearRect( 0, 0, 400, 400 );
Save the change and reload the page. Now click the START button again. This time you should see a black rectangle moving from left to right, rather than a solid line. If you have a HTML5 capable browser, you can try out the two scripts above by clicking these two links:
Another way to clear the canvas is by resetting the width property. Like this:
var cvs = document.getElementById("canvas_1");
cvs.width = cvs.width;
cvs.width = cvs.width;
This code gets a reference to the canvas again, with getElementById. The width is then rest. Resetting the width results in the canvas being erased.
HTML5 Canvas and Keydown Events
Instead of moving the black rectangle with a timer as we did in the last lesson, a shape can be moved by pressing keys on the keyboard. What we'll do is to move it left when the A key is pressed and move it right when the D key is pressed. We'll move the black rectangle up when the W key is pressed and down when the S key is pressed. To see what your web page will look like at the end of this lesson, click the link below (opens in a new window or tab):
Detecting a Key Press
Things like buttons, text boxes, images and hyperlinks have events such as onClick attached to them. Unfortunately, the canvas tag doesn't. So there's no handy onKeyPress event you can use. You can, however, write your own events and attach them to the canvas tag. This is done with the Javascript method addEventListener. Here's the syntax for the method:
addEventListener( event_type, event_handler, capture )
The event_type argument is the type of event you want: click, mouseover, keypress, and lots more besides. The event_handler is the name of the function you want to call for the event. Thecapture argument is a Boolean value. It can usually be set to false. This means you don't want to capture any other events.
If you want the entire browser window to accept a key press event, the code would be this:
window.addEventListener( "keypress", doKeyDown, false )
So the first arguments tells addEventListener that we want to activate the keypress event. The second argument is a function we've called doKeyDown. This is the function that will be called whenever a key is pressed on the keyboard.
We want the canvas tag rather than the entire browser window to accept key presses, though. So we can change the code to this:
var canvas = document.getElementById("canvas_1");
canvas.addEventListener( "keydown", doKeyDown, true);
canvas.addEventListener( "keydown", doKeyDown, true);
We first get a reference to the canvas with getElementById. This reference is placed in a variable we've called canvas. It is then used with the addEventListener method.
Now that we have an event set up for when the key pressed, let's add the doKeyDown function:
function doKeyDown(e) {
alert( e.keyCode )
}
In between the round brackets of your event function you can type a variable name. We've called ours e. This variable contains the keyboard event itself. To get at the key that was pressed you can use the inbuilt property keyCode. This returns a number relating to the key that pressed. The A key on the keyboard, whether you have CAPLOCK on or not, has a key code number of 65. The other key codes we're interested in are these:
W = 87
S = 83
D = 68
S = 83
D = 68
With these key codes, then, we can write a series of IF Statements for each key:
if ( e.keyCode == 87 ) {
}
}
if ( e.keyCode == 83 ) {
}
}
if ( e.keyCode == 65 ) {
}
}
if ( e.keyCode == 68 ) {
}
}
The code for each IF Statement can move the rectangle. The entire code for our doKeyDown function is this:
When the W key is pressed we have a call to a clearCanvas function. You can add this one:
function clearCanvas() {
canvas.width = canvas.width;
}
We then take 10 away from the y variable:
y = y - 10;
The first time the setInterval timer activates the y variable will then by 90. The x variable will still be on 100. These figures are used in the next line:
canvas_context.fillRect( x, y, 50, 30);
So the filled rectangle will be at X, Y position 100, 90 when the W key is pressed. Press it again and they will be 100 80.
The same holds for the other three keys: change the X or Y value and use them to get a new position for the rectangle. All this happened when keys are pressed on the keyboard. Try it yourself. You should find that you can move the rectangle around the canvas by pressed the W, S, A, or D keys.
You can get the whole code for this lesson here (opens in a new window or tab): Keydown code for the canvas tag.
Mouse Events and the HTML5 Canvas
You can also detect a mouse click on your canvas. Again, this is done with addEventListener. There are quite a few mouse events you can detect: mousedown, mouseup, mousemove, mouseout and mouseover. As an example, here's some code that detects the mousedown event:
The BODY tag calls an onLoad function. Inside of this function we have this:
var canvas = document.getElementById("canvas_1");
canvas.addEventListener("mousedown", doMouseDown, false);
canvas.addEventListener("mousedown", doMouseDown, false);
So we get a reference to the canvas tag then add a mousedown event listener. This event calls a function with the name of doMouseDown. (Instead of detecting mousedown, you can replace it with other mouse events like mousemove and mouseup.)
The pageX and pageY properties are used to get the X and Y coordinates of the mouse when a user left clicks on the canvas. These coordinates are then displayed in an alert box.
The HTML5 canvas and touch events
If you want to target a touch-enabled device like an iPad, iPhone, Android tablet or phone, etc, then you need the touch events. These events are touchstart, touchend, touchcancel, touchleave, touchmove. The addEventListener is set up in the same way as previously, though. Here's some code:
This code again gets a pair of X and Y coordinates. This time they are displayed when a user touches the canvas rather than clicks on it.
The two lines that get a reference to the canvas and add the listener are these:
var canvas = document.getElementById("canvas_1");
canvas.addEventListener("touchstart", doTouchStart, false);
canvas.addEventListener("touchstart", doTouchStart, false);
The touch event is more complex to capture than the mouse event. The first line of code is this:
event.preventDefault();
The default behaviour for touch events is a continuous monitoring of touches, scrolls and gestures. We only want it to detect one touch so are preventing the default behaviour.
Touches are stored in an array called targetTouches. The first of these can be used to get the X and Y coordinates of the touch:
canvas_x = event.targetTouches[0].pageX;
canvas_y = event.targetTouches[0].pageY;
canvas_y = event.targetTouches[0].pageY;
Again, we then display the X and Y position in an alert box.
Add Text to the HTML5 Canvas
You can add text to a HTML5 canvas. This can either be as filled letters or stroked ones. The two methods you need are:
fillText
strokeText
strokeText
In between the round brackets of both you need some text between quote marks, an X position and a Y position:
fillText( "Home and Learn", 20, 100 );
strokeText ("Home and Learn", 20, 200 );
strokeText ("Home and Learn", 20, 200 );
The three arguments are separated by commas.
To go with the two text methods, you can specify a font. This is done with the FONT property:
canvas_context.font = "34pt Arial";
To the right of the equal sign you can keep things simple and just specify a font size in points (pt) and the name of a font (separate the two with a space). Or you can add italics and a font weight:
canvas_context.font = "Bold 34pt Arial";
canvas_context.font = "Italic Bold 34pt Arial";
canvas_context.font = "Italic Bold 34pt Arial";
You can also specify what generic font to use if your visitor does not have your named font on his/her computer. This should go after a comma:
canvas_context.font = "Italic Bold 34pt Arial, sans-serif";
You should definitely add serif or sans-serif if you're going to be using a fancy font. This is because it's the user's system that will dictate which font will appear - if it's not on you user's PC then he or she won't see it, you'll just get a default font (probably a 10 point sans-serif).
Here's some code to try out:
canvas_context.fillStyle = "black";
canvas_context.font = "34pt Arial";
canvas_context.fillText("Home and Learn", 20, 100);
canvas_context.font = "34pt Arial";
canvas_context.fillText("Home and Learn", 20, 100);
Change the font to something else. To see what happens. Try different fill styles (you can also use a pattern or a gradient for the fills).
The code for a stroke style type of font is very similar:
canvas_context.strokeStyle = "blue";
canvas_context.font = "Italic Bold 34pt Times, serif";
canvas_context.strokeText("Home and Learn", 20, 200);
canvas_context.font = "Italic Bold 34pt Times, serif";
canvas_context.strokeText("Home and Learn", 20, 200);
HTML5 Canvas - Text Alignment
You can specify an alignment for your HTML5 canvas fonts. This is done with the textAlignproperty. You can use left, right or center (there's also a start and end, but we won't discuss these as they are almost the same as left and right). The default is left. So you don't need to add an alignment value unless you want something other than left:
canvas_context.textAlign = "right";
Text alignment is based on the X value of your fillText method. As an example, take a look at the image below:
The blue line shows that the X value for the text "Home" is at 138 pixels from the left of the canvas (the X position). The first one is left-aligned. The one in the middle is right-aligned. This means the last letter of "Home" will start at 138. The final one is centre-aligned. The code for this is:
canvas_context.fillStyle = "black";
canvas_context.font = "24pt Arial";
canvas_context.textAlign = "center";
canvas_context.fillText("Home", 138, 160);
canvas_context.font = "24pt Arial";
canvas_context.textAlign = "center";
canvas_context.fillText("Home", 138, 160);
(Note the American spelling of "center", you Brits!)
Text Baseline
You can also specify a text baseline. The default is alphabetic, meaning the bottom of the text in the roman alphabet. The letters that go below the roman alphabetic baseline are lowercase g, j, p, q, and y. Other values you can have for the baseline are: middle, top, bottom, hanging, ideographic.
To set a text baseline you use the textBaseline property:
canvas_context.textBaseline = "top";
Here's what all the baselines look like in a browser.
Note where the red lines are in the image above. The top one is the default. The others are shown in relation to this baseline. So if you specified middle then this would make your text jump up a bit.
In the roman alphabet, there's not much difference between the alphabetic and ideographic positions. Likewise, top and hanging are just about the same.
Text and Image Drop Shadows
You can add shadows to the items that you place on a HTML5 canvas. This not only includes text but images, shapes and lines, as well. We'll take a look at four shadow properties:
shadowColor
shadowOffsetX
shadowOffsetY
shadowBlur
shadowOffsetX
shadowOffsetY
shadowBlur
The first one is shadowColor. This is obviously the colour you want for your shadow. The colour should be a named colour (like red, black, etc), a hexadecimal colour, or a RGB/RGBA one. Let's look at some code:
canvas_context.fillStyle = "red";
canvas_context.font = "bold 34pt Arial";
canvas_context.font = "bold 34pt Arial";
canvas_context.shadowColor = "black";
canvas_context.shadowOffsetX = 4;
canvas_context.shadowOffsetY = 4;
canvas_context.fillText("Home and Learn", 20, 50);
canvas_context.shadowOffsetX = 4;
canvas_context.shadowOffsetY = 4;
canvas_context.fillText("Home and Learn", 20, 50);
The first two lines set a fill style to red, and the font to 34 point bold Arial. Next comes the shadow color:
canvas_context.shadowColor = "black";
You type the colour you want after an equal sign. The next two lines are these:
canvas_context.shadowOffsetX = 4;
canvas_context.shadowOffsetY = 4;
canvas_context.shadowOffsetY = 4;
The offset is how far from the text or image you want the shadow to be. There is an offset for the X value and one for the Y value (you don't have to use both). Setting an offset to a value of 0 switches it off.
The above code would get you a shadow like this:
If you don't want the shadow to be so hard then you can use rgba for your colour value, and set an alpha value. For example:
canvas_context.shadowColor = "rgba( 0, 0, 0, 0.2 )";
The first three values between the round brackets of rgba set the colour. The fourth one sets the alpha value (how transparent something is). We're setting it to a value of 0.2. Alpha values go from 0 to 1. To switch off shadows altogether you set the alpha value to 0.
The rgba colour value above would result in this for your text:
As you can see, this makes the shadow a lot softer.
If you want blurred text then the property to use is shadowBlur. Like this:
canvas_context.shadowBlur = 7;
Values for shadowBlue can go from 0, which means no blur and is the default, to larger numbers like 30 or more, which spreads the blur out, getting fainter the higher the value.
Here's what a value of 7 looks like, using the same text as above:
You can produce quite a lot of sophisticated text effects just using shadow and blur techniques. For a flavour of just what you can do, have a look at Mike Deal's excellent article here (it is a bit advanced, though): http://www.html5rocks.com/en/tutorials/canvas/texteffects/.
Image Shadows
Shadows can be applied to images, as well as text. For this to work, you need to use an image format with a transparent background like GIF and PNG. Here's some code to study:
var brolly = new Image();
brolly.src = "brolly.gif";
brolly.src = "brolly.gif";
canvas_context.shadowColor = "rgba( 0, 0, 0, 0.3 )";
canvas_context.shadowOffsetX = 6;
canvas_context.shadowOffsetY = 6;
canvas_context.shadowBlur = 3;
canvas_context.drawImage( brolly, 25, 250 );
canvas_context.shadowOffsetX = 6;
canvas_context.shadowOffsetY = 6;
canvas_context.shadowBlur = 3;
canvas_context.drawImage( brolly, 25, 250 );
Note that you don't have to do anything special to get an image shadow. The code above just creates an image object which is then loaded with the drawImage method in the usual way. Because we've set shadow and blur options, the image will look like this in a browser:
If you didn't use an image with a transparent background you wouldn't get the shadow around your shape. It would look like this:
Because the JPEG image format can't do transparent backgrounds the shadow is created around the image as a whole, rather than the picture in the image.
But play around with shadows and blurs. See what you can come up with.
Comments
Post a Comment