Test your maths skills at the MathDOJO!

Another productive weekend bares fruit for another mini game! This time I’ve put together a dojo-themed mini game whose sole purpose is to test your ability to do simple (well, mostly) arithmetic calculations in your head and your tolerance for my sense of humour (which is not so bad I hope ;-) ).

Screenshots

Here’s some screenshots taken from the game:

image

image

image

image

image

image

image

As you can see, the main game loop is simple: see some dialogue from your opponent, he asks a question, you answer, if you answer correctly within time then your opponent 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 opponents in the game, each with his own set of dialogues and questions become progressively harder, your score is tracked and you can get a big bonus in score if you answer the questions quickly (and correctly of course!) and manage to string together a succession of correct answers! As the opponents become tougher, their questions will yield greater scores too, and not to mention they’ll be harder to beat as they have a higher HP value which means you have more chance to better your all-time top score!

Game Flow

Without going through all the steps of making the game (maybe I’ll do that at a later date), the basic flow of the game goes something like this:

image

The tricky thing is then how do you control this flow easily so that depending on the state you’re in the right action is triggered when the play presses the ENTER key.

In the end I settled for a mechanism where the DOM‘s onkeydown event is handled 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 nextAction variable 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 deviation from this pattern is when I try to get the answer from a player, because there’s a time restriction on answering the questions I needed to set up a timer so that when it times out it’ll trigger the state transition as well.

Play

You can play the game at:

http://mathdojo.theburningmonk.com

or via the shortened link:

http://LNK.by/fgzcu

Enjoy! Do let me know if you have any suggestions, difficulty level, etc. etc.

Creating a count down meter with CSS3 and JQuery

Whilst working on the MathDOJO mini game one of the neat little things I tried to do was implement a time meter which steadily counts down and changes colour (from green to yellow to glowing red) as it approaches 0:

image

image

image

First, you need something to represent the meter, a simple <span> element will do:

   1: <body>

   2:     <span id="count-down-meter"></span>

   3: </body>

Then you need CSS to define its colour (and glow effect using CSS3 animation which for now, is only supported by webkit browser unfortunately) at different stages

   1: /* define the animation (webkit-only) */

   2: @-webkit-keyframes redPulse

   3: {

   4:     from { -webkit-box-shadow: 0 0 9px #f00; }

   5:     50% { -webkit-box-shadow: 0 0 18px #f00; }

   6:     to { -webkit-box-shadow: 0 0 9px #f00; }

   7: }

   8:

   9: /* initial state of the meter */

  10: #count-down-meter

  11: {

  12:     display: block;

  13:     width: 100%;

  14:     height: 15px;

  15:     margin-top: 10px;

  16:     background-color: #0f0;

  17: }

  18:

  19: /* change color at midway */

  20: #count-down-meter.middle

  21: {

  22:     background-color: #ff0;

  23: }

  24:

  25: /* change colour and play animation when it's on its last leg */

  26: #count-down-meter.lastleg

  27: {

  28:     background-color: #f00;

  29:     -webkit-animation-name: redPulse;

  30:     -webkit-animation-duration: 2s;

  31:     -webkit-animation-iteration-count: infinite;

  32: }

The animation itself is handled by Javascript, using JQuery’s animate method:

   1: function start() {

   2:         // change to yellow when 40% way through

   3:     var midWidth = meter.width() * 0.6,

   4:         // change to glowing red when 70% way through

   5:         lastlegWidth = meter.width() * 0.3;

   6:

   7:     meter.animate({

   8:         width: 0 + "px",

   9:     }, {

  10:         duration: 5000,

  11:         easing: "linear",

  12:         step: function(now, fx) {

  13:             if (now <= midWidth) {

  14:                 meter.addClass("middle");

  15:

  16:                 if (now <= lastlegWidth) {

  17:                     meter.addClass("lastleg");

  18:                 }

  19:             }

  20:         }

  21:     });

  22: };

To reset the state changes to the meter afterwards, it’s easiest to remove the “style” attribute as JQuery applies the changes through the “style” attribute, which can be a problem if you had manually inserted style in your HTML:

   1: function reset() {

   2:     meter.stop()

   3:          .removeAttr("style")

   4:          .removeClass("middle")

   5:          .removeClass("lastleg");

   6: };

Here’s a live demo:

So that’s it, nice and easy! Enjoy! Hope you find some good use for it.

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 CSS3 – Box Shadow property

CSS3 introduces a useful little property called box-shadow, which as the name suggests adds a shadow to an element.

Simple box shadow

You can create a basic box shadow like this:

div {
    -webkit-box-shadow: 10px 20px #000;  /* for Chrome */
    -moz-box-shadow: 10px 20px #000;  /* for Firefox */
}

This casts a shadow with an X-offset of 10 pixels and a Y-offset of 20 pixels:

image image

You can equally use negative values for the offsets, which will simply cast the shadow onto the other side of the element:

div {
    -webkit-box-shadow: –10px -20px #000;  /* for Chrome */
    -moz-box-shadow: -10px -20px #000;  /* for Firefox */
}

image

You might have noticed the use of vendor prefixes (-webkit for Chrome and –moz for Firefox), the reason for this is described in my post about the border radius here.

Adding a blur distance

To make the shadow look more realistic, you can blur the shadow by supplying an additional parameter:

div {
    -webkit-box-shadow: 10px 20px 10px #000;  /* for Chrome */
    -moz-box-shadow: 10px 20px 10px #000;  /* for Firefox */
}

image

The higher the number of pixels the more pronounced the effect of the blur is.

Adding opacity

What’s more? You can add opacity to the shadow to make the shadow semi-transparent, to do this in CSS3 is simple as it introduces a new rgba function which you can use to specify the colour of the shadow. Most of you should be familiar with how RGBa works (a stands for alpha which is a value between 0 and 1 with 0 being fully transparent and 1 being fully opaque) already and to use it on a shadow here’s what you can do:

div {
    -webkit-box-shadow: 10px 20px 10px rgba(0, 0, 0, 0.4);  /* for Chrome */
    -moz-box-shadow: 10px 20px 10px rgba(0, 0, 0, 0.4);  /* for Chrome */
}

Compare the two images below, using solid colour on the left and using RGBa function with alpha value of 0.4 on the right:

image image

As you can see, the shadow on the right look a lot more believable!

Multiple shadows

With the box-shadow property in CSS3, you’re not limited to only one shadow, to specify additional shadows simply turn the box-shadow property into a comma separated list of shadow specifications:

div {
    -webkit-box-shadow: 10px 20px 10px rgba(0, 0, 0, 0.4),
                        -10px 20px 10px rgba(100, 0, 0, 0.4);
    -moz-box-shadow: 10px 20px 10px rgba(0, 0, 0, 0.4),
                     -10px 20px 10px rgba(100, 0, 0, 0.4);
}

image

Creating an inner shadow

You can create an inner shadow by adding the inset keyword:

div {
    -webkit-box-shadow: inset 10px 20px 10px rgba(0, 0, 0, 0.4);  /* for Chrome */
    -moz-box-shadow: inset 10px 20px 10px rgba(0, 0, 0, 0.4);  /* for Firefox */
}

image

You could create multiple inner shadows, though the effect tend to be a little strange if you ask me..:

div {
    -webkit-box-shadow: inset 10px 20px 10px rgba(0, 0, 0, 0.4),
                        inset -10px -20px 10px rgba(0, 0, 150, 0.6);
    -moz-box-shadow: inset 10px 20px 10px rgba(0, 0, 0, 0.4),
                     inset -10px -20px 10px rgba(0, 0, 150, 0.6);
}

image

Or combine inner shadow with normal shadows:

div {
    -webkit-box-shadow: inset 10px 20px 10px rgba(0, 0, 0, 0.4),
                        10px 20px 10px rgba(0, 0, 150, 0.6);
    -moz-box-shadow: inset 10px 20px 10px rgba(0, 0, 0, 0.4),
                     10px 20px 10px rgba(0, 0, 150, 0.6);
}

image

again, doesn’t feel quite right…

Adding a spread distance

Finally, you can add a ‘spread’ to the shadow, which basically adds some padding to the edges of the shadow:

div {
    -webkit-box-shadow: 10px 20px 0px 10px #000;  /* for Chrome */
    -moz-box-shadow: 10px 20px 0px 10px #000;  /* for Firefox */
}

Compare these two images, one with spread of 10 pixels (left) and one without:

image image

Demo

Finally, here’s a quick demo page I’ve put together to let you play around with the box-shadow property and see its effects as you change the relevant parameters, note it only works with webkit browsers (Safari and Chrome) for now as I’m using the new range type input in HTML5 which is not yet supported in Firefox and iE.

References:

CSS3info.com on box shadows

Having fun with CSS3 – Border Radius

As you may know already, CSS3 has introduced a new set of properties which allows you to easily add curved corners to a box.

Simple round corners

You can create a div with round corners with this simple css:

div {
    border-radius: 50px;
}

image

However, given the stage of development CSS3 is at as of now this is only supported by Google Chrome… Instead, you are encouraged to add vendor prefixes :

div {
    -webkit-border-radius: 50px; /* for Chrome */
    -moz-border-radius: 50px; /* for Firefox */
}

How much of a curve the corners get is worked out by placing an imaginary circle of a radius of the given number of pixels at each of the corners (see image below):

image

Naturally this introduces an interesting edge condition – what if the radius of the imaginary circles is set to be equal to or greater than half of the width of a square-shaped element? Do we just get a circle back?

Yes.

image

Specify different radius for each corner

You can specify a different radius for each of the top-left, top-right, bottom-right and bottom-left corners either like this:

div {
    /* for Chrome */
    -webkit-border-top-left-radius: 40px;
    -webkit-border-top-right-radius: 10px;
    -webkit-border-bottom-right-radius: 80px;
    -webkit-border-bottom-left-radius: 20px;

    /* for Firefox */
    -moz-border-radius-topleft: 40px;
    -moz-border-radius-topright: 10px;
    -moz-border-radius-bottomright: 80px;
    -moz-border-radius-bottomleft: 20px;
}

Note: the property names are different!

Or using the short-hand syntax by listing 4 consecutive values for each of the four corners in the above mentioned order:

div {
    -webkit-border-radius: 40px 10px 80px 20px;  /* for Chrome */
    -moz-border-radius: 40px 10px 80px 20px;  /* for Firefox */
}

both will produce the same result:

image

Oval-shaped corners

You don’t have to be content with circular-shaped corners either, to specify an oval-shaped corner, just use this syntax:

div {
    -webkit-border-radius: 80px / 40px;  /* for Chrome */
    -moz-border-radius: 80px / 40px;  /* for Firefox */
}

image

The only thing we’re doing differently now is that we’re putting an oval as opposed to a perfect circle into the corners. The oval is defined by a width and height:

image

And once more, you can specify these for each corner by setting separate properties:

div {
    /* for Chrome */
    -webkit-border-top-left-radius: 80px 40px;
    -webkit-border-top-right-radius: 10px 50px;
    -webkit-border-bottom-right-radius: 40px 80px;
    -webkit-border-bottom-left-radius: 60px 20px;

    /* for Firefox */
    -moz-border-radius-topleft: 80px 40px;
    -moz-border-radius-topright: 10px 50px;
    -moz-border-radius-bottomright: 40px 80px;
    -moz-border-radius-bottomleft: 60px 20px;
}

image

though unfortunately there’s no short-hand syntax to save you from all these typing this time around.

And finally, you don’t HAVE to use pixels as unit either, most other standard CSS units work too. It’s also worth noting that using the vendor prefix (e.g. -webkit, -moz) allows for a more graceful degrading behavior IF the final spec for CSS3 uses a different naming scheme for the border radius properties.

Demo

Check out this quick demo page I’ve put together for you to see the effect of changing the border radius, note this only works if your browser supports the new ‘range’ input type defined in HTML5 (Chrome works fine, but Firefox doesn’t support it yet).