Creating a sticky note app with HTML5, CSS3 and Javascript

I saw this tutorial the other day, it’s cool but I fancied taking it a step further and make it useful as an app rather than just a fancy way to show some structured data on the screen. Here’s a list of the features which I wanted to add:

  • being able to edit the title and content 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
  • randomize the colour of the notes more

HTML and CSS changes

I initially considered using the new contenteditable attribute in HTML5 to make the title and content editable, but settled on using textarea instead as it offers better support for multiple line text and you can easily add placeholder text as well as set a limit on the length of the title.

I added two images as buttons to add a new note and save the state of all the notes and put a a border around the notes to make them stand out a little 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 couple of things to note here:

  • the textarea element has a transparent background
  • the textarea element is not resizable
  • the textarea element has a placeholder text

Here’s what the page looks like now:

image

Adding interactions with Javascript

Adding a new note

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 create a new note, all we need to do to randomize its colour is to add one of these classes to the element randomly.

Here’s the event handler 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 handler calls the addNewNote function which is responsible for adding a new note to the notes unordered list and sets its class, title or content depending on the arguments. In this particular usage it’ll pick one of the three colour classes randomly and add the class to the new note.

Once a note has been added, I then get a reference to it and create an event handler for its close button so that when its clicked it’ll remove this note from the DOM.

The addNoteEvent function is then charged with adding the focus and hover in/out event handlers to the note to show and hide the close button so that it’s only shown when the note is focused/hovered on.

Saving the notes

I have previously cover the use of HTML5’s local and session storage in this post already, what I’m doing here is very similar but a little more involved.

When the ‘save’ button is clicked, I need to inspect each of the notes and remember the class, title text (remember, you can’t get the text that’s been entered into the textarea by getting the HTML of the element) and content text in a bespoke object, the resulting array of objects is then JSON serialized using Douglas Crockford‘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 storage, if it is, then deserialize 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

or using the short link:

http://LNK.by/fhbba

Enjoy!

UPDATE 27/02/2011:

Thanks to Joey Beechey for pointing out to me in the comment that the demo doesn’t work in Safari and after a bit of digging I found that the problem is with the addNewNote function which has a parameter named ‘class‘ which is a reserved word. I found another reference of this problem here too, which mentions the same problem but with the variable name ‘abstract‘ instead!

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

References:

Douglas Crockford’s JSON-js project on github

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

Original tutorial which inspired this blog post

Having fun with HTML5 – Canvas, part 5

In part 4, I put together the foundations for a mini shooting game, with targets moving on the screen and you can ‘hit’ them by clicking inside the targets, etc. etc. I promised an update to make it feel more like a game, so here it is!

Adding a background

The black background was more than a little dull so I had a look around for some freely available images, and to apply them I had two simple choices:

  • draw the image onto the main canvas on each frame
  • use the image as background to the canvas

I opted for the latter as it seems more efficient, it does mean that I need to change the way I’m clearing the canvas for each frame though. In part 1 of this series I mentioned that by setting the width or height of the canvas element you can erase the contents and reset all the properties of its drawing context, which seems to fit the bill of what I’m trying to do here.

The clear function is therefore 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 number of Shots

To make the game more challenging (having an unlimited number of shots is just too easy), I decided to add a limit on the number of shots you can have and after you’ve used up all of them it’s effectively game over.

To show how many shots you have left, I want to show a number of bullets in the top left corner and depending on the number of shots you have left the bullets are shown differently:

  • Five shots or less and you will see a bullet for each
  • More than five shots and you will see a bullet followed by the words x N where N is the number of shots left
  • If no restriction on the number of shots (for time attack mode for instance), you see an infinity sign next to a bullet

To facilitate these requirements, I need to be able to track the number of shots left and decrement its value every time you click inside the canvas element. Thankfully, the structure 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 business of actually showing them on the screen, luckily it wasn’t too hard to create a basic bullet looking image and an infinity symbol. To make it easier to use them over and over in my code, I added two <img> elements 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 function to add the bullets to the scene. To encapsulate 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:

image image image

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

Using a Custom Cursor

Though a good approximate, the crosshair cursor just isn’t quite up to the task here, a scope-like cursor is needed here, like the one I created here:

image

(if you’re interested in how I made the cursor, I used Axialis CursorWorkshop which makes the task simple)

All that’s left is to replace the crosshair cursor in CSS, but still keeping it as fallback:

   1: #canvas

   2: {

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

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

   5: }

A similar 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 seeing the new cursor when you run the game.

Adding a Bullet Target

Thanks to my wife Yinan for giving me the idea of having bonus targets which are harder to hit but give you extra bullets when they’re hit. You have already seen the HTML changes earlier, and probably noticed that I have switched to using an image rather than drawing the regular target boards by hand..

Much of the changes to support a different type of target happened in the Javascript code, namely the Target class, which is now extended by a new BulletTarget 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 removing a target from the targets array is now moved out of the Target class and the code which generates targets during initialization has also changed to generate a bullet target by a configurable 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 features in the near future so come back later if you’re interested in seeing how far this little game can go!

Related posts:

Having fun with HTML5 – Canvas, part 1

Having fun with HTML5 – Canvas, part 2

Having fun with HTML5 – Canvas, part 3

Having fun with HTML5 – Canvas, part 4

Having fun with HTML5 – Canvas, part 4

Following on from part 3 where we basically made a little app that lets you scribble with HTML5’s canvas element, let us push on and see what else we can achieve with the canvas element.

The logical next step would be to made animations, and give you a way to interact with the animation. So how about a little game that lets you shoot moving targets like those classic Point Blank games? Hands up who’s up for that?

image

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

To Start Off..

First, we’ll need a simple HTML page, something like my previous 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>

Nothing fancy here, the CSS for this page is also pretty basic too, the only thing worth noting is that to get the crosshair cursor inside the canvas I included this in my CSS:

   1: #canvas

   2: {

   3:     cursor: crosshair;

   4: }

The onSelectStart line in the canvas HTML is to ensure that the crosshair cursor is used when you click and hold inside the canvas element.

You might have also noticed that I had specified two canvas elements in the HTML, this is to simplify the task of drawing moving targets onto the main canvas. With the 2D context object’s drawImage function, you can draw an img, canvas, or video element so I can draw the target onto the target canvas during initialization and then reuse it in the main canvas.

Drawing the Target board

Next, I need to draw the target boards, and make them look like this, but without the score on the rings:

image

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

As you can imagine, we can create a target board looking like this by drawing 6 circles, with the innermost 2 circles having roughly half the radius of the other 4 and the outermost 2 circles having the inversed fill/stroke colour combination. Expand the below section to see the initializeTargetCanvas function which takes a canvas element and draws a target 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: }

Animating the Target Boards

Being able to draw an already created target board onto the canvas page is one thing, but how do I animate them using the canvas? Having looked at a few other examples it seems the common way to do animation with the canvas is to simply clear the canvas and redraw the contents on a regular interval. This seems a little low level and requires a bit of plumbing but I’m sure it won’t be long (if not already) before some good, solid frameworks emerge to make these tasks easier.

For now though, let me illustrate how you might create this frame by frame animation yourself.

If you separate the responsibilities of the app, broadly speaking you end up with:

  • a View – i.e. the canvas element, responsible for displaying the targets
  • a Model – the objects representing the targets in the scene, responsible for keeping track of their current position, etc.

If you’ve dealt with any sort of MVC/MVP/MVVM pattern then this kind of separation of duty should be familiar to you already. On each frame (or if you prefer, every time the setInterval delegate function gets invoked), you update the position of the targets in the model, then redraw the targets onto the canvas to reflect their updated positions:

image

This is all you need to do to create a basic animation. So in my case, I need a simple Target object to keep track of:

  • X, Y coordinates of the top left hand corner of the target
  • the radius of the target
  • the direction and speed (per frame) it’s moving at

using MooTools here’s the class I arrived at this Target ‘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 function, which is invoked at regular intervals, first clears the canvas (by filling it with the background colour) then goes through all the targets in the targets array and updates their location 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 animation loop, here’s how it looks with 10 moving targets on screen at the same time:

image

Adding Interactions

Now that the animations are in place, let’s add some player interactions. The interactions I’m after here is simple, click in the canvas and knock out any (can be one or more) target that’s clicked on.

I’ve already gone over the process of calculating the coordinates of the click in respect to the canvas in a previous 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 canvas’ coordinate system, then I can simply run a hit test against each moving target and compare the distance between the click and the centre of the target and the target’s radius:

image

If the distance is smaller or equal to the radius then the click happened INSIDE the target and therefore it’s a hit, otherwise it’s a miss. Sounds reasonable enough? Here’s the code that takes a position (a simple object with X and Y position of the click inside the canvas’ coordinate system) and return the targets 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 handler to the mousedown event on the canvas during 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 target it’ll be removed from the array of moving targets and therefore won’t be drawn again when the canvas is refreshed in the next frame.

Adding Notifications

Finally, for this first-pass implementation of a mini-shooting game, I’d like to add some notifications to tell you when you’ve hit something, or when you’ve missed completely!

This is slightly trickier than the targets as the messages should not stay around forever until some user-triggered action, instead it should be shown for a given amount of time (or frames). To facilitate this requirement, I created another ‘class’ called Message:

   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 Message objects can only been drawn a number of times, after which it will remove itself from the array of messages currently being displayed.

To make it a bit more interesting, I defined a number of messages which will be displayed depending on how many targets you’ve managed 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 mousedown event handler (see above) I added these couple of lines to push a new message to the messages stack to be displayed for 30 frames, and the message is determined by how many targets 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 example, if you managed to hit three targets one shot:

image

Pretty neat, eh? :-)

Demo

Here is the full demo, over the next couple of days, I will polish it up and add some more features 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 suggestions you have on how to improve it!

References:

HTML5 canvas cheat sheet

Dive into HTML5 – peeks, pokes and pointers

Related posts:

Having fun with HTML5 – Canvas, part 1

Having fun with HTML5 – Canvas, part 2

Having fun with HTML5 – Canvas, part 3

Having fun with HTML5 – Canvas, part 5

Closure in Javascript vs C#

Java vs C#

As Jon Skeet pointed out in this excellent article on closures, the strategies of capturing the execution context which the behaviour is bound to different between Java and C#. C# captures the variable itself, whereas Java captures the value of the variable. To illustrate the difference, here’s Jon’s original example in C#:

// First build a list of actions
List<Action> actions = new List<Action>();

for (int counter = 0; counter < 10; counter++)
{
    actions.Add(() => Console.WriteLine(counter));
}

// Then execute them
foreach (Action action in actions)
{
    action();
}

This code actually writes 10 ten times instead of 0 to 9 because the context the actions are bound to is the variable counter, and at the time of execution the value of counter is 10 hence why each Action delegate writes 10 instead of the value of counter at the time the Action delegate was created.

The equivalent code in Java would print 0 to 9 because Java’s closure implementation captures the value of the variable instead.

However, as Jon pointed out, whilst Java’s implementation is more intuitive and easier to understand (less WTF bugs), C#’s implementation is more flexible as you can just easily mimic the effect of capturing the variable’s value by using a local variable:

// First build a list of actions
List<Action> actions = new List<Action>();

for (int counter = 0; counter < 10; counter++)
{
    int copy = counter;
    actions.Add(() => Console.WriteLine(copy));
}

// Then execute them
foreach (Action action in actions)
{
    action();
}

Javascipt vs C# (Updated 16/01/2011)

Thanks for Jens for pointing out my initial incorrect assessment (see comment), so I revised my test and used a more straight forward test using just one private variable:

function test () {
    var i = 10;
    
    // create the closure
    var func = function () { return i; };

    // 10
    alert(func());

    i = 11;
    // 11? or 10?
    alert(func());
}

The test here is simple, we create a function func which returns the value of the private variable i, whose value is then changed later on. If Javascript captures the value of the variable then the second time func is invoke the alert message would still be 10, otherwise it’ll be 11 which is the current value of i.

And the answer is?

.

.

.

Javascript captures the reference of the variable, and the second alert message is 11.

References:

Jon Skeet’s article on closures

Article on Javascript closures

Currying and Partial Applications in F#, Javascript and C#

In my last post I explained the difference between the techniques of Currying and Partial Application, following on where there let me show you how you might apply these two techniques in F#, Javascript and C#.

F#

Starting with F#, being a functional language it makes currying and partial application dead easy (considering that they are primarily function programming concepts). Take for example, a simple function f which takes 3 parameters:

// base function f
let f a1 a2 a3 = a1 + a2 + a3

// partial function fixes two of the parameters
let partial a1 = f a1 5 10

// currying creates a china of functions each taking one parameter
let curry a1 =
    // create the next function in the chain which takes another parameter
    // and returns another function
    let curry' a2 =
        // the inner most function gives you the full application
        // of the original function
        let curry'' a3 = f a1 a2 a3
        curry''
    curry'

From the method signatures you can see how the result of the two approaches differs:

image

To use the partially applied function partial:

partial 1 // returns 16

clearly, this is equivalent to calling

f 1 5 10 // returns 16

seeing as the partial function has fixed a2 and a3 to 5 and 10 respectively.

With the curry function, however, it’s slightly more interesting:

((curry 1) 5) 10 // returns 16

this way of invoking the chained function in turn is in fact the same as:

curry 1 5 10 // returns 16

Javascript

Despite being considered an object-oriented language, Javascript shares more than a handful of similarities with function languages, take its dynamic, loose type system, or the ability to treat function as objects, for instance. It’s therefore of little surprise to see that you can implement currying in Javascript with ease.

Here’s the example I used in the last post:

// original function that takes three parameters a1, a2 and a3
function f(a1, a2, a3) {
    return a1 + a2 + a3;
}

// curried function which takes one parameter at a time
function curry(a1) {
    // each stage takes another parameter and get you closer to the
    // full application of f
    return function (a2) {
        // but only with the inner most function do you actually get
        // the return value you wanted
        return function (a3) {
            return f(a1, a2, a3);
        };
    };
}

// partial applied function which takes one parameter and fixes
// the other 2
function partial(a1) {
    // a partially applied function of the original function f can
    // get you the full application straight away
    return f(a1, 5, 10);
}

curry(1)(5)(10); // returns 16
partial(1); // returns 16

C#

Yes, it’s possible! Here’s a simple example of how using the Func delegate:

// base function f
Func<int, int, int, int> f = ( a1, a2, a3 ) => a1 + a2 + a3;

// note that partial is a reserved keyword in C#
Func<int, int> partialf = a1 => f(a1, 5, 10);

Func<int, Func<int, Func<int, int>>> curryf = a1 => a2 => a3 => f(a1, a2, a3);

f(1, 5, 10);  // returns 16
partialf(1);  // returns 16
curryf(1)(5)(10);  // returns 16

Parting thoughts…

There is one important language feature which all three languages support, which is what enables us to apply both currying and partial application so effortlessly – closure. If you’re not familiar with the concept of closure, it’s definitely worth spending a bit of time familiarise yourself with it as it’s a very powerful (albeit often overlooked) language feature.

References:

Wikipedia page on Currying

John Resig’s post on currying in JavaScript

Angus Croll – Curry: cooking up tastier functions

Angus Croll – partial: curry’s flashy cousin

Olivier Steel – Functional Javascript

SO question – what is the difference between currying and partial application

Lambda the Ultimate – Currying != Generalized Partial Application?!

Good blog posts which explains the differences between Currying and Partial Application

Did it with .Net – The Art of Currying