## Having fun with HTML5 – Simple painting app using Canvas

It feels like a lit­tle while since I last played around with the <can­vas> ele­ment, so I spent some time over the week­end and put together a sim­ple paint­ing app using the can­vas and here is the end result.

Here’s some screenshots:

#### Spray Tool

One of the inter­est­ing things with this sim­ple paint­ing app is the spray tool, here’s what I did to achieve the effect:

`   1: var _intervalId,    // used to track the current interval ID`

`   2:     _center;        // the current center to spray`

`   3:  `

`   4: function getRandomOffset() {`

`   5:     var randomAngle = Math.random() * 360;`

`   6:     var randomRadius = Math.random() * radius;`

`   7:  `

`   8:     return {`

`   9:         x: Math.cos(randomAngle) * randomRadius,`

`  10:         y: Math.sin(randomAngle) * randomRadius`

`  11:     };`

`  12: }`

`  13:  `

`  14: this.startDrawing = function (position) {`

`  15:     _center = position;`

`  16:  `

`  17:     // spray once every 200 milliseconds`

`  18:     _intervalId = setInterval(this.spray, 10);`

`  19: };`

`  20:  `

`  21: this.finishDrawing = function (position) {`

`  22:     clearInterval(_intervalId);`

`  23: };`

`  24:  `

`  25: this.spray = function () {`

`  26:     var centerX = _center.X, centerY = _center.Y, i;`

`  27:  `

`  28:     for (i = 0; i < density; i++) {`

`  29:         var offset = getRandomOffset();`

`  30:         var x = centerX + offset.x, y = centerY + offset.y;`

`  31:  `

`  32:         drawingCxt.fillRect(x, y, 1, 1);`

`  33:     }`

`  34: };`

The start­Draw­ing func­tion is called when the mouse­Down or a sub­se­quent mouse­Move event is raised, it in turn sets up an inter­val event which is trig­gered every 10 mil­lisec­onds and draws one pixel using the cur­rently con­fig­ured colour within the radius of the mouse pointer.

When the mouseUp event is raised, the inter­val event han­dler is unbound.

#### Sav­ing the Can­vas to an Image

One of the cool things you can do with the <can­vas> ele­ment is the abil­ity to get a data:URL con­tain­ing a rep­re­sen­ta­tion of the image as a PNG file, which you can then use as the source for an <img> ele­ment so that the user can then save.

The code required to do this is as sim­ple as call­ing toDataURL() on the <can­vas> ele­ment and then use the returned value to set the src prop­erty on the <img> element.

#### Demo

You can try out the live demo at http://sketchy.theburningmonk.com or via the short­link http://lnk.by/fghis.

If you’re inter­ested in the source code, you can get it off github here.

Enjoy!

## Test your maths skills at the MathDOJO!

Another pro­duc­tive week­end bares fruit for another mini game! This time I’ve put together a dojo-themed mini game whose sole pur­pose is to test your abil­ity to do sim­ple (well, mostly) arith­metic cal­cu­la­tions in your head and your tol­er­ance for my sense of humour (which is not so bad I hope ;-) ).

#### Screen­shots

Here’s some screen­shots taken from the game:

As you can see, the main game loop is sim­ple: see some dia­logue from your oppo­nent, he asks a ques­tion, you answer, if you answer cor­rectly within time then your oppo­nent loses health point, when the opponent’s HP is depleted then you’ve defeated him and move onto the next opponent.

So far there’re 7 oppo­nents in the game, each with his own set of dia­logues and ques­tions become pro­gres­sively harder, your score is tracked and you can get a big bonus in score if you answer the ques­tions quickly (and cor­rectly of course!) and man­age to string together a suc­ces­sion of cor­rect answers! As the oppo­nents become tougher, their ques­tions will yield greater scores too, and not to men­tion they’ll be harder to beat as they have a higher HP value which means you have more chance to bet­ter your all-time top score!

#### Game Flow

With­out going through all the steps of mak­ing the game (maybe I’ll do that at a later date), the basic flow of the game goes some­thing like this:

The tricky thing is then how do you con­trol this flow eas­ily so that depend­ing on the state you’re in the right action is trig­gered when the play presses the ENTER key.

In the end I set­tled for a mech­a­nism where the DOM’s onkey­down event is han­dled by this bit of code:

`   1: // hook up the onkeydown event to execute the 'next' action whatever it might be`

`   2: document.onkeydown = function(event) {`

`   3:     if (event.keyCode == 13) {`

`   4:         nextAction();`

`   5:     }`

`   6: };`

The nex­tAc­tion vari­able is updated at each step of the game loop so that when the player presses the ENTER key the game does the right thing. The only devi­a­tion from this pat­tern is when I try to get the answer from a player, because there’s a time restric­tion on answer­ing the ques­tions I needed to set up a timer so that when it times out it’ll trig­ger the state tran­si­tion as well.

#### Play

You can play the game at:

http://mathdojo.theburningmonk.com

http://LNK.by/fgzcu

Enjoy! Do let me know if you have any sug­ges­tions, dif­fi­culty level, etc. etc.

## Creating a sticky note app with HTML5, CSS3 and Javascript

I saw this tuto­r­ial the other day, it’s cool but I fan­cied tak­ing it a step fur­ther and make it use­ful as an app rather than just a fancy way to show some struc­tured data on the screen. Here’s a list of the fea­tures which I wanted to add:

• being able to edit the title and con­tent of the notes
• being able to save all your notes
• being able to add new notes
• being able to delete notes you no longer want
• ran­dom­ize the colour of the notes more

#### HTML and CSS changes

I ini­tially con­sid­ered using the new con­tente­d­itable attribute in HTML5 to make the title and con­tent editable, but set­tled on using textarea instead as it offers bet­ter sup­port for mul­ti­ple line text and you can eas­ily add place­holder text as well as set a limit on the length of the title.

I added two images as but­tons to add a new note and save the state of all the notes and put a a bor­der around the notes to make them stand out a lit­tle more.

Here’s the updated HTML:

`   1: <body>`

`   2:     <div id="controls">`

`   3:         <img id="btnNew" src="images/newnote.png"/>`

`   4:         <img id="btnSave" src="images/save.png"/>`

`   5:     </div>`

`   6:`

`   7:     <ul id="notes">`

`   8:     </ul>`

`   9: </body>`

And the CSS to go along with it:

`   1: * {`

`   2:     maring: 0;`

`   3:     padding: 0;`

`   4: }`

`   5:`

`   6: .hide {`

`   7:     display: none;`

`   8: }`

`   9:`

`  10: body {`

`  11:     font-size: 100%;`

`  12:     margin: 15px;`

`  13:     background: #666;`

`  14:     color: #fff;`

`  15: }`

`  16:`

`  17: #controls img {`

`  18:     width: 28px;`

`  19:     height: 30px;`

`  20:     background: rgba(0, 0, 0, 0);`

`  21: }`

`  22:`

`  23: #controls img:hover, #controls img:focus {`

`  24:     -webkit-transform: scale(1.2);`

`  25:       -moz-transform: scale(1.2);`

`  26:       -o-transform: scale(1.2);`

`  27:       position:relative;`

`  28: }`

`  29:`

`  30: ul, li {`

`  31:     list-style: none;`

`  32: }`

`  33:`

`  34: ul {`

`  35:     overflow: hidden;`

`  36:     padding: 15px;`

`  37: }`

`  38:`

`  39: ul li {`

`  40:     margin: 15px;`

`  41:     float: left;`

`  42:     position: relative;`

`  43: }`

`  44:`

`  45: ul li div {`

`  46:     text-decoration: none;`

`  47:     color: #000;`

`  48:     background: #ffc;`

`  49:     display: block;`

`  50:     height: 150px;`

`  51:     width: 150px;`

`  52:     padding: 10px;`

`  53:     -moz-box-shadow: 5px 5px 7px rgba(33, 33, 33, 1);`

`  54:     -webkit-box-shadow: 5px 5px 7px  rgba(33, 33, 33, .7);`

`  55:     box-shadow: 5px 5px 7px rgba(33, 33, 33, .7);`

`  56:     -moz-transition: -moz-transform .15s linear;`

`  57:     -o-transition: -o-transform .15s linear;`

`  58:     -webkit-transition: -webkit-transform .15s linear;`

`  59:     border-style: solid;`

`  60: }`

`  61:`

`  62: ul li div img {`

`  63:     padding: 1px 3px;`

`  64:     margin: 10px;`

`  65:     position: absolute;`

`  66:     top: 0;`

`  67:     right: 0;`

`  68: }`

`  69:`

`  70: ul li textarea {`

`  71:     font-family: 'Chewy', arial, sans-serif;`

`  72:     background: rgba(0, 0, 0, 0); /* transparent background */`

`  73:     resize: none;`

`  74:     padding: 3px;`

`  75:     border-style: none;`

`  76: }`

`  77:`

`  78: .note-title {`

`  79:     font-size: 140%;`

`  80:     font-weight: bold;`

`  81:     height: 30px;`

`  82:     width: 70%;`

`  83: }`

`  84:`

`  85: .note-content {`

`  86:     font-size:120%;`

`  87:     height: 100px;`

`  88:     width: 95%;`

`  89: }`

`  90:`

`  91: ul li:nth-child(even) div {`

`  92:   -o-transform:rotate(4deg);`

`  93:   -webkit-transform:rotate(4deg);`

`  94:   -moz-transform:rotate(4deg);`

`  95:   position:relative;`

`  96:   top:5px;`

`  97: }`

`  98:`

`  99: ul li:nth-child(3n) div {`

` 100:   -o-transform:rotate(-3deg);`

` 101:   -webkit-transform:rotate(-3deg);`

` 102:   -moz-transform:rotate(-3deg);`

` 103:   position:relative;`

` 104:   top:-5px;`

` 105: }`

` 106:`

` 107: ul li:nth-child(5n) div {`

` 108:   -o-transform:rotate(5deg);`

` 109:   -webkit-transform:rotate(5deg);`

` 110:   -moz-transform:rotate(5deg);`

` 111:   position:relative;`

` 112:   top:-10px;`

` 113: }`

` 114:`

` 115: ul li div:hover, ul li div:focus {`

` 116:   -moz-box-shadow:10px 10px 7px rgba(0,0,0,.7);`

` 117:   -webkit-box-shadow: 10px 10px 7px rgba(0,0,0,.7);`

` 118:   box-shadow:10px 10px 7px rgba(0,0,0,.7);`

` 119:   -webkit-transform: scale(1.25);`

` 120:   -moz-transform: scale(1.25);`

` 121:   -o-transform: scale(1.25);`

` 122:   position:relative;`

` 123:   z-index:5;`

` 124: }`

` 125:`

` 126: /* define 3 different colours for the notes */`

` 127: ul li div.colour1 {`

` 128:     background: #ffc;`

` 129: }`

` 130: ul li div.colour2 {`

` 131:     background: #cfc;`

` 132: }`

` 133: ul li div.colour3 {`

` 134:     background: #ccf;`

` 135: }`

There’s a cou­ple of things to note here:

• the textarea ele­ment has a trans­par­ent background
• the textarea ele­ment is not resizable
• the textarea ele­ment has a place­holder text

Here’s what the page looks like now:

In the CSS above, you can see that I had defined three CSS class:

`   1: /* define 3 different colours for the notes */`

`   2: ul li div.colour1 {`

`   3:     background: #ffc;`

`   4: }`

`   5: ul li div.colour2 {`

`   6:     background: #cfc;`

`   7: }`

`   8: ul li div.colour3 {`

`   9:     background: #ccf;`

`  10: }`

when we cre­ate a new note, all we need to do to ran­dom­ize its colour is to add one of these classes to the ele­ment randomly.

Here’s the event han­dler for the ‘new’ image button:

`   1: \$(document).ready(function() {`

`   2:     notes = \$("#notes"); // get references to the 'notes' list`

`   3:     ...`

`   4:     // clicking the 'New Note' button adds a new note to the list`

`   5:     \$("#btnNew").click(function() {`

`   6:         addNewNote();`

`   7:     });`

`   8:     ...`

`   9: });`

`  10:`

`  11: //  adds a new note to the 'notes' list`

`  12: function addNewNote(class, title, content) {`

`  13:     // if class is not specified, use a random colour class`

`  14:     if (!class) {`

`  15:         class = "colour" + Math.ceil(Math.random() * 3);`

`  16:     }`

`  17:`

`  18:     // add a new note to the end of the list`

`  19:     notes.append("<li><div class='" + class + "'>" +`

`  20:                  "<textarea class='note-title' placeholder='Untitled' maxlength='10'/>" +`

`  21:                  "<textarea class='note-content' placeholder='Your content here'/>" +`

`  22:                  "<img class='hide' src='images/close.png'/>" +`

`  23:                  "</div></li>");`

`  24:`

`  25:     // get the new note that's just been added and attach the click event handler to its close button`

`  26:     var newNote = notes.find("li:last");`

`  27:     newNote.find("img").click(function() {`

`  28:         newNote.remove();`

`  29:     });`

`  30:`

`  31:     // hook up event handlers to show/hide close button as appropriate`

`  32:     addNoteEvent(newNote);`

`  33:`

`  34:     // if a title is provided then set the title of the new note`

`  35:     if (title) {`

`  36:         // get the title textarea element and set its value`

`  37:         newNote.find("textarea.note-title").val(title);`

`  38:     }`

`  39:`

`  40:     // if a content is provided then set the content of the new note`

`  41:     if (content) {`

`  42:         // get the content textarea element and set its value`

`  43:         newNote.find("textarea.note-content").val(content);`

`  44:     }`

`  45: }`

`  46:`

`  47: function addNoteEvent(noteElement) {`

`  48:     noteElement.focus(function () {`

`  49:         \$(this).find(".img").removeClass("hide");`

`  50:     }).hover(function() {`

`  51:         \$(this).find("img").removeClass("hide");`

`  52:     }, function () {`

`  53:         \$(this).find("img").addClass("hide");`

`  54:     });`

`  55: }`

as you can see, this event han­dler calls the addNewNote func­tion which is respon­si­ble for adding a new note to the notes unordered list and sets its class, title or con­tent depend­ing on the argu­ments. In this par­tic­u­lar usage it’ll pick one of the three colour classes ran­domly and add the class to the new note.

Once a note has been added, I then get a ref­er­ence to it and cre­ate an event han­dler for its close but­ton so that when its clicked it’ll remove this note from the DOM.

The addNo­teEvent func­tion is then charged with adding the focus and hover in/out event han­dlers to the note to show and hide the close but­ton so that it’s only shown when the note is focused/hovered on.

Sav­ing the notes

I have pre­vi­ously cover the use of HTML5’s local and ses­sion stor­age in this post already, what I’m doing here is very sim­i­lar but a lit­tle more involved.

When the ‘save’ but­ton is clicked, I need to inspect each of the notes and remem­ber the class, title text (remem­ber, you can’t get the text that’s been entered into the textarea by get­ting the HTML of the ele­ment) and con­tent text in a bespoke object, the result­ing array of objects is then JSON seri­al­ized using Dou­glas Crock­ford’s Json.js library. It this is JSON string that’s saved into the local storage.

On the other end, when the page has been loaded and the DOM event fired (i.e. when JQuery.ready fires) I will need to check if the JSON string is in the local stor­age, if it is, then dese­ri­al­ize the string back to an object array and add the notes back in in the same order:

`   1: var notes;`

`   2: var count = 0;`

`   3:`

`   4: \$(document).ready(function() {`

`   5:     notes = \$("#notes"); // get references to the 'notes' list`

`   6:`

`   7:     // load notes from local storage if one's available`

`   8:     var storedNotes = localStorage.getItem("notes");`

`   9:     if (storedNotes)`

`  10:     {`

`  11:         // passes the stored json back into an array of note objects`

`  12:         var notesArray = JSON.parse(storedNotes);`

`  13:         count = notesArray.length;`

`  14:`

`  15:         for (var i = 0; i < count; i++) {`

`  16:             var storedNote = notesArray[i];`

`  17:             addNewNote(storedNote.Class, storedNote.Title, storedNote.Content);`

`  18:         }`

`  19:     }`

`  20:`

`  21:     ...`

`  22:`

`  23:     // clicking the 'Save' button saves the state of the notes board to the local storage                `

`  24:     \$("#btnSave").click(function() {`

`  25:         var notesArray = new Array();`

`  26:`

`  27:         // for each of the notes add a bespoke note object to the array`

`  28:         notes.find("li > div").each(function (i, e) {`

`  29:             // save the class attribute of the div, as well as the text for the title and content text areas`

`  30:             var colourClass = \$(e).attr("class");`

`  31:             var title = \$(e).find("textarea.note-title");`

`  32:             var content = \$(e).find("textarea.note-content");`

`  33:`

`  34:             notesArray.push({ Index: i, Title: title.val(), Content: content.val(), Class: colourClass });`

`  35:         });`

`  36:`

`  37:         // json encode it`

`  38:         var jsonStr = JSON.stringify(notesArray);`

`  39:`

`  40:         // and save the json string into local storage`

`  41:         localStorage.setItem("notes", jsonStr);`

`  42:`

`  43:         // info the user it's done`

`  44:         alert("Notes saved");`

`  45:     });`

`  46:`

`  47:     // add a note to the list if there aren't any`

`  48:     if (count == 0)`

`  49:     {`

`  50:         \$("#btnNew").click();`

`  51:     }`

`  52: });`

#### Demo

You can try out the full demo at:

http://stickynote.theburningmonk.com

http://LNK.by/fhbba

Enjoy!

#### UPDATE 27/02/2011:

Thanks to Joey Beechey for point­ing out to me in the com­ment that the demo doesn’t work in Safari and after a bit of dig­ging I found that the prob­lem is with the addNewNote func­tion which has a para­me­ter named ‘class’ which is a reserved word. I found another ref­er­ence of this prob­lem here too, which men­tions the same prob­lem but with the vari­able name ‘abstract’ instead!

I’ve also put the whole thing into a zip file which you can down­load from here, so feel free to play around with it yourself!

#### Ref­er­ences:

Dou­glas Crockford’s JSON-js project on github

John Resig’s blog post which includes some handy exam­ples on how to use the JSON-js library

Orig­i­nal tuto­r­ial which inspired this blog post

## Having fun with HTML5 — Canvas, part 5

In part 4, I put together the foun­da­tions for a mini shoot­ing game, with tar­gets mov­ing on the screen and you can ‘hit’ them by click­ing inside the tar­gets, etc. etc. I promised an update to make it feel more like a game, so here it is!

The black back­ground was more than a lit­tle dull so I had a look around for some freely avail­able images, and to apply them I had two sim­ple choices:

• draw the image onto the main can­vas on each frame
• use the image as back­ground to the canvas

I opted for the lat­ter as it seems more effi­cient, it does mean that I need to change the way I’m clear­ing the can­vas for each frame though. In part 1 of this series I men­tioned that by set­ting the width or height of the can­vas ele­ment you can erase the con­tents and reset all the prop­er­ties of its draw­ing con­text, which seems to fit the bill of what I’m try­ing to do here.

The clear func­tion is there­fore changed to:

`   1: // clear the canvas page`

`   2: function clear() {`

`   3:     // reset the canvas by resizing the canvas element`

`   4:     canvasElement.width = canvasElement.width;`

`   5: }`

#### Limit the num­ber of Shots

To make the game more chal­leng­ing (hav­ing an unlim­ited num­ber of shots is just too easy), I decided to add a limit on the num­ber of shots you can have and after you’ve used up all of them it’s effec­tively game over.

To show how many shots you have left, I want to show a num­ber of bul­lets in the top left cor­ner and depend­ing on the num­ber of shots you have left the bul­lets are shown differently:

• Five shots or less and you will see a bul­let for each
• More than five shots and you will see a bul­let fol­lowed by the words x N where N is the num­ber of shots left
• If no restric­tion on the num­ber of shots (for time attack mode for instance), you see an infin­ity sign next to a bullet

To facil­i­tate these require­ments, I need to be able to track the num­ber of shots left and decre­ment its value every time you click inside the can­vas ele­ment. Thank­fully, the struc­ture is there already, just need some small modifications:

`   1: // first declare a new global variable`

`   2: /* GLOBAL VARIABLES */`

`   3: var WIDTH,              // width of the canvas area`

`   4:     HEIGHT,             // height of the canvas area`

`   5:     ...`

`   6:     bullets,            // how many shots left`

`   7:     ...`

`   8:`

`   9: // then add some more steps to the mousedown handler`

`  10: \$("#canvas").mousedown(function (mouseEvent) {`

`  11:     // ignore if no more bullets left!`

`  12:     if (bullets <= 0) {`

`  13:         return;`

`  14:     }`

`  15:`

`  16:     // hit test and add message, etc.`

`  17:     ...`

`  18:`

`  19:     // deduct the number of shots left if a cap is defined`

`  20:     if (bullets != undefined) {`

`  21:         bullets--;`

`  22:     }`

`  23: });`

Then I need to take care of the busi­ness of actu­ally show­ing them on the screen, luck­ily it wasn’t too hard to cre­ate a basic bul­let look­ing image and an infin­ity sym­bol. To make it eas­ier to use them over and over in my code, I added two <img> ele­ments to the HTML (along with a third which I will explain later) but they’ll be hidden:

`   1: <div class="hide">`

`   2:     <img id="target" src="images/target.png"/>`

`   3:     <img id="bullet_target" src="images/bullet_target.png"/>`

`   4:     <img id="bullet" src="images/bullet.png"/>`

`   5:     <img id="infinity" src="images/infinity.png"/>`

`   6: </div>`

Back to the Javascript code, there needs to be an extra step in the draw func­tion to add the bul­lets to the scene. To encap­su­late that logic I pulled it out into its own function:

`   1: // redraw the target boards on the canvas`

`   2: function draw() {`

`   3:     // as before`

`   4:     ...`

`   5:`

`   6:     // then the bullets`

`   7:     drawBullets();`

`   8: }`

`   9:`

`  10: // draw the bullets onto the top left hand corner`

`  11: function drawBullets() {`

`  12:     // don't proceed any further if no more bullets left`

`  13:     if (bullets <= 0) {`

`  14:         return;`

`  15:     }`

`  16:`

`  17:     // define basica X and Y offsets`

`  18:     var offsetX = 10, offsetY = 10;`

`  19:`

`  20:     // draw the first bullet`

`  21:     context.drawImage(bulletElement, offsetX, offsetY);`

`  22:     offsetX += bulletElement.width;`

`  23:`

`  24:     if (bullets == undefined) {`

`  25:         // draw the infinity sign next to the bullet with some padding`

`  26:         context.drawImage(infinityElement, offsetX + 5, offsetY + 5);`

`  27:     } else if (bullets > 5) {`

`  28:         context.textBaseline = "top";`

`  29:         context.textAlign = "start";`

`  30:         context.fillStyle = "orange";`

`  31:         context.font = "bold 40px arial";`

`  32:`

`  33:         // draw a count next to the bullet, e.g. "x10"`

`  34:         context.fillText("x"+bullets, offsetX + 5, offsetY);`

`  35:     }`

`  36:     else {`

`  37:         // otherwise, draw the remaining bullets`

`  38:         for (var i = 1 ; i < bullets; i++) {`

`  39:             context.drawImage(bulletElement, offsetX, offsetY);`

`  40:             offsetX += bulletElement.width;`

`  41:         }`

`  42:     }`

`  43: }`

And these are the results:

Looks the part, if I might say so myself :-)

#### Using a Cus­tom Cursor

Though a good approx­i­mate, the crosshair cur­sor just isn’t quite up to the task here, a scope-like cur­sor is needed here, like the one I cre­ated here:

(if you’re inter­ested in how I made the cur­sor, I used Axi­alis Cur­sor­Work­shop which makes the task simple)

All that’s left is to replace the crosshair cur­sor in CSS, but still keep­ing it as fallback:

`   1: #canvas`

`   2: {`

`   3:     cursor: url(../cursors/scope.cur), crosshair;`

`   4:     background: url(../images/canvas_background.jpg);`

`   5: }`

A sim­i­lar change needs to be applied to the HTML:

`   1: <canvas id="canvas" class="block" width="800" height="700"`

`   2:         onSelectStart="this.style.cursor='url(cursors/scope.cur), crosshair'; return false;"/>`

After that you’ll start see­ing the new cur­sor when you run the game.

Thanks to my wife Yinan for giv­ing me the idea of hav­ing bonus tar­gets which are harder to hit but give you extra bul­lets when they’re hit. You have already seen the HTML changes ear­lier, and prob­a­bly noticed that I have switched to using an image rather than draw­ing the reg­u­lar tar­get boards by hand..

Much of the changes to sup­port a dif­fer­ent type of tar­get hap­pened in the Javascript code, namely the Tar­get class, which is now extended by a new Bul­let­Tar­get class:

`   1: // define the Target 'class' to represent an on-screen target`

`   2: var Target = new Class({`

`   3:     initialize: function (x, y, radius, dx, dy) {`

`   4:         // same as before`

`   5:         ...`

`   6:`

`   7:         // hit the target!`

`   8:         this.hit = function () {`

`   9:             deleteTarget(_id);`

`  10:         };`

`  11:     }`

`  12: });`

`  13:`

`  14: // a target which awards the player with an extra bullet when hit`

`  15: var BulletTarget = new Class({`

`  16:     Extends: Target,`

`  17:     initialize: function (x, y, radius, bonusBullets, dx, dy) {`

`  18:         var _bonusBullets = bonusBullets;`

`  19:`

`  20:         // initialize Target's properties`

`  21:         this.parent(x, y, radius, dx, dy);`

`  22:`

`  23:         this.getBonusBullets = function () {`

`  24:             return _bonusBullets;`

`  25:         }`

`  26:`

`  27:         // override the draw function to draw the bullet target instead`

`  28:         this.draw = function () {`

`  29:             context.drawImage(bulletTargetElement, this.getX(), this.getY(), radius*2, radius*2);`

`  30:         };`

`  31:`

`  32:         // override the hit function to the player some bullets back `

`  33:         this.hit = function () {`

`  34:             bullets += _bonusBullets;`

`  35:             deleteTarget(this.getId());`

`  36:         }`

`  37:     }`

`  38: });`

The logic of remov­ing a tar­get from the tar­gets array is now moved out of the Tar­get class and the code which gen­er­ates tar­gets dur­ing ini­tial­iza­tion has also changed to gen­er­ate a bul­let tar­get by a con­fig­urable chance:

`   1: /* GLOBAL VARIABLES */`

`   2: var WIDTH,              // width of the canvas area`

`   3:     HEIGHT,             // height of the canvas area`

`   4:     ...`

`   5:     targetElement,      // the target img element`

`   6:     targetRadius = 50,  // the radius of the regular targets`

`   7:     bulletTargetElement,        // the bullet target img element`

`   8:     bulletTargetRadius = 30,    // the radius of the targets with bullet bonus`

`   9:     bulletTargetBonus = 3,      // the number of extra bullets for hitting a bullet target`

`  10:     bulletTargetChance = 0.1,   // the chance of getting a bullet target`

`  11:     ...`

`  12:`

`  13: // function to delete the target with the specified ID`

`  14: function deleteTarget(id) {`

`  15:     for (var i = 0; i < targets.length; i++) {`

`  16:         var target = targets[i];`

`  17:`

`  18:         if (target.getId() == id) {`

`  19:             targets.splice(i, 1);`

`  20:             break;`

`  21:         }`

`  22:     }`

`  23: }`

`  24:`

`  25: // Add targets to the game`

`  26: function createTargets() {`

`  27:     for (var i = 0; i < 10; i++) {`

`  28:         if (Math.random() < bulletTargetChance) {`

`  29:             targets[i] = new BulletTarget(0, 0, bulletTargetRadius, bulletTargetBonus);`

`  30:         } else {`

`  31:             targets[i] = new Target(0, 0, targetRadius);`

`  32:         }`

`  33:     }`

`  34: }`

#### Demo

The full demo can be found here, hope you like it, I’ll be adding even more fea­tures in the near future so come back later if you’re inter­ested in see­ing how far this lit­tle game can go!

#### Related posts:

Hav­ing fun with HTML5 — Can­vas, part 1

Hav­ing fun with HTML5 — Can­vas, part 2

Hav­ing fun with HTML5 — Can­vas, part 3

Hav­ing fun with HTML5 — Can­vas, part 4

## Having fun with HTML5 — Canvas, part 4

Fol­low­ing on from part 3 where we basi­cally made a lit­tle app that lets you scrib­ble with HTML5’s can­vas ele­ment, let us push on and see what else we can achieve with the can­vas element.

The log­i­cal next step would be to made ani­ma­tions, and give you a way to inter­act with the ani­ma­tion. So how about a lit­tle game that lets you shoot mov­ing tar­gets like those clas­sic Point Blank games? Hands up who’s up for that?

Good good, that’s decided then! :-)

#### To Start Off..

First, we’ll need a sim­ple HTML page, some­thing like my pre­vi­ous demos would do:

`   1: <div id="wrapper">`

`   2:     <h1>HTML5 Canvas Demo</h1>`

`   3:     <h3>Shoot 'em!</h3>`

`   4:     <aside id="message"></aside>`

`   5:`

`   6:     <div class="hide">`

`   7:         <canvas id="target" width="101" height="101" class="hide"/>`

`   8:     </div>`

`   9:`

`  10:     <div class="block">`

`  11:         <canvas id="canvas" class="block" width="800" height="700"`

`  12:                 onSelectStart="this.style.cursor='crosshair'; return false;"/>`

`  13:     </div>`

`  14: </div>`

Noth­ing fancy here, the CSS for this page is also pretty basic too, the only thing worth not­ing is that to get the crosshair cur­sor inside the can­vas I included this in my CSS:

`   1: #canvas`

`   2: {`

`   3:     cursor: crosshair;`

`   4: }`

The onS­e­lect­Start line in the can­vas HTML is to ensure that the crosshair cur­sor is used when you click and hold inside the can­vas element.

You might have also noticed that I had spec­i­fied two can­vas ele­ments in the HTML, this is to sim­plify the task of draw­ing mov­ing tar­gets onto the main can­vas. With the 2D con­text object’s draw­Im­age func­tion, you can draw an img, can­vas, or video ele­ment so I can draw the tar­get onto the tar­get can­vas dur­ing ini­tial­iza­tion and then reuse it in the main canvas.

#### Draw­ing the Tar­get board

Next, I need to draw the tar­get boards, and make them look like this, but with­out the score on the rings:

(Why not just use an image? Because where’s the fun in that! ;-) )

As you can imag­ine, we can cre­ate a tar­get board look­ing like this by draw­ing 6 cir­cles, with the inner­most 2 cir­cles hav­ing roughly half the radius of the other 4 and the out­er­most 2 cir­cles hav­ing the inversed fill/stroke colour com­bi­na­tion. Expand the below sec­tion to see the ini­tial­ize­Tar­get­Can­vas func­tion which takes a can­vas ele­ment and draws a tar­get board in inside it:

`   1: // Draws the target canvas`

`   2: function initializeTargetCanvas(element) {`

`   3:     var baseX = 1.5, baseY = 1.5;`

`   4:`

`   5:     // get the width and height of the element`

`   6:     var width = (element.width - baseX * 2) / 2,`

`   7:         height = (element.height - baseY * 2) / 2;`

`   8:`

`   9:     // work out the necessary metrics to draw the target`

`  10:     var radius = Math.min(width, height),`

`  11:         centreX = baseX + radius,`

`  12:         centreY = baseY + radius,`

`  13:         ringWidth = radius / 10;`

`  14:`

`  15:     // get the 2D context to start drawing the target!`

`  16:     var context = element.getContext("2d");`

`  17:     context.lineWidth = "2";`

`  18:`

`  19:     // define function to draw a ring`

`  20:     var drawRing = function (strokeStyle, fillStyle, ringRadius) {`

`  21:         context.strokeStyle = strokeStyle;`

`  22:         context.fillStyle = fillStyle;`

`  23:`

`  24:         // draw the circle`

`  25:         context.beginPath();`

`  26:         context.arc(centreX, centreY, ringRadius, 0, Math.PI * 2, true);`

`  27:         context.closePath();`

`  28:`

`  29:         context.stroke();`

`  30:         context.fill();`

`  31:     };`

`  32:`

`  33:     // draw the rings for each score`

`  34:     drawRing("#000", "#FFF", radius);`

`  35:     drawRing("#000", "#FFF", radius -= (ringWidth * 2));`

`  36:     drawRing("#FFF", "#000", radius -= (ringWidth * 2));`

`  37:     drawRing("#FFF", "#000", radius -= (ringWidth * 2));`

`  38:     drawRing("#FFF", "#000", radius -= (ringWidth * 2));`

`  39:     drawRing("#FFF", "#000", radius -= ringWidth);`

`  40: }`

#### Ani­mat­ing the Tar­get Boards

Being able to draw an already cre­ated tar­get board onto the can­vas page is one thing, but how do I ani­mate them using the can­vas? Hav­ing looked at a few other exam­ples it seems the com­mon way to do ani­ma­tion with the can­vas is to sim­ply clear the can­vas and redraw the con­tents on a reg­u­lar inter­val. This seems a lit­tle low level and requires a bit of plumb­ing but I’m sure it won’t be long (if not already) before some good, solid frame­works emerge to make these tasks easier.

For now though, let me illus­trate how you might cre­ate this frame by frame ani­ma­tion yourself.

If you sep­a­rate the respon­si­bil­i­ties of the app, broadly speak­ing you end up with:

• a View – i.e. the can­vas ele­ment, respon­si­ble for dis­play­ing the targets
• a Model – the objects rep­re­sent­ing the tar­gets in the scene, respon­si­ble for keep­ing track of their cur­rent posi­tion, etc.

If you’ve dealt with any sort of MVC/MVP/MVVM pat­tern then this kind of sep­a­ra­tion of duty should be famil­iar to you already. On each frame (or if you pre­fer, every time the set­Inter­val del­e­gate func­tion gets invoked), you update the posi­tion of the tar­gets in the model, then redraw the tar­gets onto the can­vas to reflect their updated positions:

This is all you need to do to cre­ate a basic ani­ma­tion. So in my case, I need a sim­ple Tar­get object to keep track of:

• X, Y coor­di­nates of the top left hand cor­ner of the target
• the radius of the target
• the direc­tion and speed (per frame) it’s mov­ing at

using MooTools here’s the class I arrived at this Tar­get ‘class’:

`   1: /* GLOBAL VARIABLES */`

`   2: var WIDTH,              // width of the canvas area`

`   3:     HEIGHT,             // height of the canvas area`

`   4:     …`

`   5:     targets = new Array(),  // the live targets`

`   6:     targetId = 0,        // the current target ID`

`   7:     …`

`   8:`

`   9: // define the Target 'class' to represent an on-screen target`

`  10: var Target = new Class({`

`  11:     initialize: function (x, y, radius, dx, dy) {`

`  12:         var _id, _x, _y, _radius, _dx, _dy, is;`

`  13:`

`  14:         _id = targetId++;`

`  15:`

`  16:         // the X and Y coordinate of the top left corner`

`  17:         _x = x;`

`  18:         _y = y;`

`  19:`

`  20:         // the radius of the target`

`  21:         _radius = radius;`

`  22:`

`  23:         // the rate of movement in the X and Y direction`

`  24:         if (dx) {`

`  25:             _dx = dx;`

`  26:         } else {`

`  27:             _dx = Math.ceil(Math.random() * 10);`

`  28:         }`

`  29:         if (dy) {`

`  30:             _dy = dy;`

`  31:         } else {`

`  32:             _dy = Math.ceil(Math.random() * 10);`

`  33:         }`

`  34:`

`  35:         // getters`

`  36:         this.getId = function () {`

`  37:             return _id;`

`  38:         }`

`  39:`

`  40:         this.getX = function () {`

`  41:             return _x;`

`  42:         };`

`  43:`

`  44:         this.getY = function () {`

`  45:             return _y;`

`  46:         };`

`  47:`

`  48:         this.getRadius = function () {`

`  49:             return _radius;`

`  50:         };`

`  51:`

`  52:         // move the target to its position for the next frame`

`  53:         this.move = function () {`

`  54:             _x += _dx;`

`  55:             _y += _dy;`

`  56:`

`  57:             // change direction in X if it 'hits' the border`

`  58:             if ((_x + _radius * 2) >= WIDTH || _x <= 0) {`

`  59:                 _dx *= -1;`

`  60:             }`

`  61:`

`  62:             // change direction in Y if it 'hits' the border`

`  63:             if ((_y + _radius * 2) >= HEIGHT || _y <= 0) {`

`  64:                 _dy *= -1;`

`  65:             }`

`  66:         };`

`  67:`

`  68:         // draws the target on the canvas`

`  69:         this.draw = function () {`

`  70:             context.drawImage(targetElement, _x, _y);`

`  71:         };`

`  72:`

`  73:         // hit the target!`

`  74:         this.hit = function () {`

`  75:             for (var i = 0; i < targets.length; i++) {`

`  76:                 var target = targets[i];`

`  77:`

`  78:                 if (target.getId() == _id) {`

`  79:                     targets.splice(i, 1);`

`  80:                     break;`

`  81:                 }`

`  82:             }`

`  83:         };`

`  84:     }`

`  85: });`

The draw func­tion, which is invoked at reg­u­lar inter­vals, first clears the can­vas (by fill­ing it with the back­ground colour) then goes through all the tar­gets in the tar­gets array and updates their loca­tion and then draw them onto the canvas:

`   1: // clear the canvas page`

`   2: function clear() {`

`   3:     context.fillStyle = "#000";`

`   4:     context.fillRect(0, 0, WIDTH, HEIGHT);`

`   5: }`

`   6:`

`   7: // redraw the target boards on the canvas`

`   8: function draw() {`

`   9:     // clear the canvas page first`

`  10:     clear();`

`  11:`

`  12:     for (var i = 0; i < targets.length; i++) {`

`  13:         targets[i].move();`

`  14:         targets[i].draw();`

`  15:     }`

`  16: }`

This will give you the basic ani­ma­tion loop, here’s how it looks with 10 mov­ing tar­gets on screen at the same time:

Now that the ani­ma­tions are in place, let’s add some player inter­ac­tions. The inter­ac­tions I’m after here is sim­ple, click in the can­vas and knock out any (can be one or more) tar­get that’s clicked on.

I’ve already gone over the process of cal­cu­lat­ing the coor­di­nates of the click in respect to the can­vas in a pre­vi­ous blog post here, this is what that code looks like:

`   1: // works out the X, Y position of the click INSIDE the canvas from the X, Y `

`   2: // position on the page`

`   3: function getPosition(mouseEvent, element) {`

`   4:     var x, y;`

`   5:     if (mouseEvent.pageX != undefined && mouseEvent.pageY != undefined) {`

`   6:         x = mouseEvent.pageX;`

`   7:         y = mouseEvent.pageY;`

`   8:     } else {`

`   9:         x = mouseEvent.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;`

`  10:         y = mouseEvent.clientY + document.body.scrollTop + document.documentElement.scrollTop;`

`  11:     }`

`  12:`

`  13:     return { X: x - element.offsetLeft, Y: y - element.offsetTop };`

`  14: }`

If I can work out where you’ve clicked in the can­vas’ coor­di­nate sys­tem, then I can sim­ply run a hit test against each mov­ing tar­get and com­pare the dis­tance between the click and the cen­tre of the tar­get and the target’s radius:

If the dis­tance is smaller or equal to the radius then the click hap­pened INSIDE the tar­get and there­fore it’s a hit, oth­er­wise it’s a miss. Sounds rea­son­able enough? Here’s the code that takes a posi­tion (a sim­ple object with X and Y posi­tion of the click inside the can­vas’ coor­di­nate sys­tem) and return the tar­gets it has hit:

`   1: // check if the player managed to hit any of the live targets`

`   2: function hitTest(position) {`

`   3:     var hitTargets = new Array();`

`   4:`

`   5:     // check if the position is within the bounds of any of the live targets`

`   6:     for (var i = 0; i < targets.length; i++) {`

`   7:         var target = targets[i];`

`   8:`

`   9:         var targetCentreX = target.getX() + target.getRadius(),`

`  10:             targetCentreY = target.getY() + target.getRadius();`

`  11:`

`  12:         // work out the distance between the position and the target's centre`

`  13:         var xdiff = position.X - targetCentreX,`

`  14:             ydiff = position.Y - targetCentreY,`

`  15:             dist = Math.sqrt(Math.pow(xdiff, 2) + Math.pow(ydiff, 2));`

`  16:`

`  17:         // if that distance is less than the radius of the target then the`

`  18:         // position is inside the target`

`  19:         if (dist <= target.getRadius()) {`

`  20:             hitTargets.push(target);`

`  21:         }`

`  22:     }`

`  23:`

`  24:     return hitTargets;`

`  25: }`

To hook this up, I added an event han­dler to the mouse­down event on the can­vas dur­ing initialization:

`   1: \$("#canvas").mousedown(function (mouseEvent) {`

`   2:     // get the coordinates of the click inside the canvas`

`   3:     var position = getPosition(mouseEvent, this);`

`   4:`

`   5:     // find out which targets were hit`

`   6:     var hitTargets = hitTest(position);`

`   7:`

`   8:     // hit the targets`

`   9:     for (var i = 0; i < hitTargets.length; i++) {`

`  10:         hitTargets[i].hit();`

`  11:     }`

`  12: }`

And now, when you ‘hit’ a tar­get it’ll be removed from the array of mov­ing tar­gets and there­fore won’t be drawn again when the can­vas is refreshed in the next frame.

Finally, for this first-pass imple­men­ta­tion of a mini-shooting game, I’d like to add some noti­fi­ca­tions to tell you when you’ve hit some­thing, or when you’ve missed completely!

This is slightly trick­ier than the tar­gets as the mes­sages should not stay around for­ever until some user-triggered action, instead it should be shown for a given amount of time (or frames). To facil­i­tate this require­ment, I cre­ated another ‘class’ called Mes­sage:

`   1: // define the Message 'class' to represent an on-screen message`

`   2: var Message = new Class({`

`   3:     initialize: function (x, y, message, duration) {`

`   4:         var _id, _x, _y, _message, _duration;`

`   5:`

`   6:         _id = messageId++;`

`   7:`

`   8:         // X, Y coordinates of where to display the message`

`   9:         _x = x;`

`  10:         _y = y;`

`  11:`

`  12:         // the message`

`  13:         _message = message;`

`  14:`

`  15:         // how many frames to display the message for`

`  16:         _duration = duration;`

`  17:`

`  18:         this.getId = function () {`

`  19:             return _id;`

`  20:         }`

`  21:`

`  22:         this.draw = function () {`

`  23:             if (_duration >= 0) {`

`  24:                 context.textBaseline = "middle";`

`  25:                 context.textAlign = "center";`

`  26:                 context.fillStyle = "#FFF";`

`  27:                 context.strokeStyle = "#000";`

`  28:                 context.font = "bold 40px arial";`

`  29:`

`  30:                 // draw the message at the specified X, Y coordinates`

`  31:                 context.fillText(_message, _x, _y);`

`  32:`

`  33:                 _duration--;`

`  34:             } else {`

`  35:                 // remove the message`

`  36:                 for (var i = 0; i < messages.length; i++) {`

`  37:                     var message = messages[i];`

`  38:`

`  39:                     if (message.getId() == _id) {`

`  40:                         messages.splice(i, 1);`

`  41:                         break;`

`  42:                     }`

`  43:                 }`

`  44:             }`

`  45:         }`

`  46:     }`

`  47: });`

The Mes­sage objects can only been drawn a num­ber of times, after which it will remove itself from the array of mes­sages cur­rently being displayed.

To make it a bit more inter­est­ing, I defined a num­ber of mes­sages which will be dis­played depend­ing on how many tar­gets you’ve man­aged to hit at once:

`   1: // define the messages to show`

`   2: var hitMessages = new Array();`

`   3: hitMessages[0] = "MISS";`

`   4: hitMessages[1] = "HIT!";`

`   5: hitMessages[2] = "DOUBLE HIT!!";`

`   6: hitMessages[3] = "HAT-TRICK!!!";`

`   7: hitMessages[4] = "UN~BELIEVABLE!!!!";`

`   8: hitMessages[5] = "OH MY GOSH!!";`

On the mouse­down event han­dler (see above) I added these cou­ple of lines to push a new mes­sage to the mes­sages stack to be dis­played for 30 frames, and the mes­sage is deter­mined by how many tar­gets was hit:

`   1: // use one of the defined messages if possible, otherwise use a default`

`   2: var hitMessage = hitMessages[hitTargets.length];`

`   3: if (hitMessage == undefined)`

`   4: {`

`   5:     hitMessage = "TOO GOOOOOOOOD..";`

`   6: }`

`   7:`

`   8: messages.push(new Message(position.X, position.Y, hitMessage, 30));`

For exam­ple, if you man­aged to hit three tar­gets one shot:

Pretty neat, eh? :-)

#### Demo

Here is the full demo, over the next cou­ple of days, I will pol­ish it up and add some more fea­tures to make it feel more gamey and post another update. In the mean time though, feel free to play around with it and let me know of any sug­ges­tions you have on how to improve it!

#### Ref­er­ences:

HTML5 can­vas cheat sheet

Dive into HTML5 – peeks, pokes and pointers

#### Related posts:

Hav­ing fun with HTML5 — Can­vas, part 1

Hav­ing fun with HTML5 — Can­vas, part 2

Hav­ing fun with HTML5 — Can­vas, part 3

Hav­ing fun with HTML5 — Can­vas, part 5