Adobe
Products
Acrobat
Creative Cloud
Creative Suite
Digital Marketing Suite
Digital Publishing Suite
Elements
Photoshop
Touch Apps
Student and Teacher Editions
More products
Solutions
Creative tools for business
Digital marketing
Digital media
Education
Financial services
Government
Web Experience Management
More solutions
Learning Help Downloads Company
Buy
Home use for personal and home office
Education for students, educators, and staff
Business for small and medium businesses
Licensing programs for businesses, schools, and government
Special offers
Search
 
Info Sign in
Welcome,
My cart
My orders My Adobe
My Adobe
My orders
My information
My preferences
My products and services
Sign out
Why sign in? Sign in to manage your account and access trial downloads, product extensions, community areas, and more.
Adobe
Products Sections Buy   Search  
Solutions Company
Help Learning
Sign in Sign out My orders My Adobe
Preorder Estimated Availability Date. Your credit card will not be charged until the product is shipped. Estimated availability date is subject to change. Preorder Estimated Availability Date. Your credit card will not be charged until the product is ready to download. Estimated availability date is subject to change.
Qty:
Purchase requires verification of academic eligibility
Subtotal
Review and Checkout
Adobe Developer Connection / HTML5, CSS3, and JavaScript /

Build a Hangman game with HTML5 Canvas, JavaScript, and CSS – Part 2: Implementing the game logic

by David Powers

David Powers
  • http://foundationphp.com/

Content

  • Checking the selected letter
  • Drawing the Hangman gallows and victim in HTML5 canvas
  • Displaying the correct answer
  • Using local storage to keep score
  • Making the game available offline
  • Enabling canvas and canvas text in IE 6–8
  • Some final tweaks for the iPhone and iPod touch
  • Where to go from here

Created

16 December 2011

Page tools

Share on Facebook
Share on Twitter
Share on LinkedIn
Bookmark
Print
CSS Dreamweaver HTML5 JavaScript mobile

Requirements

Prerequisite knowledge

Sound understanding of HTML and CSS. Also, basic knowledge of JavaScript and jQuery.

User level

Intermediate

Required products

  • Dreamweaver (Download trial)

Sample files

  • hangman_pt2_start.zip
  • hangman_pt2_finish.zip

One of the main features of HTML5 is the new <canvas> element, which provides a blank space where you can draw lines and shapes dynamically, as well as add text and images. In this second part of a two-part tutorial series, you'll use the Canvas 2D Context API (application programming interface) to create the graphics for a Hangman word-guessing game. You'll also use HTML5-related web storage to keep record of the user's score, and add a manifest file to make the game usable offline in modern browsers.

The <canvas> element and web storage are supported by the most recent versions of all browsers, but not by older versions of Internet Explorer (IE). To ensure that the game works in IE 6–8, you'll add helper scripts (JavaScript "polyfills") for HTML5 canvas, canvas text, and local storage.

The instructions assume that you have completed Part 1 of this tutorial series. Continue working with your existing files. If you want to be sure you're starting with working code, use the files in hangman_pt2_start.zip (they're identical to hangman_pt1_finish.zip).

Checking the selected letter

When the user selects a letter in the alphabetic keypad, the checkLetter() function tests whether it's in the word to be guessed. If it is, the letter replaces the appropriate underscore(s). Otherwise, it triggers another function to draw part of the gallows and victim. The checkLetter() function also has to keep track of the number of good and bad guesses.

  1. Start by initializing a couple of local variables. You need one for the row of underscores and correctly guessed letters. The second variable is used to control the subsequent action, and begins by assuming the guess is incorrect. Amend the checkLetter() function like this:
// Check whether selected letter is in the word to be guessed function checkLetter(letter) { var placeholders = word.innerHTML, wrongGuess = true; }
  1. Next, split the placeholders into an array and loop through it to check if the selected letter is in the word to be guessed. If it is, replace the underscore at that point with the letter and increase the number of correct guesses. If it isn't, increase the number of bad guesses and redraw the <canvas> element. Finally, add the placeholders back to the screen. The complete checkLetter() function looks like this:
// Check whether selected letter is in the word to be guessed function checkLetter(letter) { var placeholders = word.innerHTML, wrongGuess = true; // split the placeholders into an array placeholders = placeholders.split(''); // loop through the array for (var i = 0; i < wordLength; i++) { // if the selected letter matches one in the word to guess, // replace the underscore and increase the number of correct guesses if (wordToGuess.charAt(i) == letter.toLowerCase()) { placeholders[i] = letter; wrongGuess = false; correctGuesses++; // redraw the canvas only if all letters have been guessed if (correctGuesses == wordLength) { drawCanvas(); } } } // if the guess was incorrect, increment the number of bad // guesses and redraw the canvas if (wrongGuess) { badGuesses++; drawCanvas(); } // convert the array to a string and display it again word.innerHTML = placeholders.join(''); }

Each time the loop runs, the conditional statement examines the next letter in the word to guess using charAt() and compares it with the selected letter. The comparison is done using letter.toLowerCase() because all the words are in lowercase. If they match, the letter replaces the current placeholder, resets the local variable wrongGuess to false , and increments the global variable correctGuesses .

If correctGuesses equals the global variable wordLength , it means all the letters have been guessed, and the game has been won. When that happens, you need to redraw the <canvas> element to display the result.

If the selected letter doesn't match any of the letters in the word to guess, wrongGuess remains true, so the badGuesses global variable is incremented and the <canvas> element is redrawn.

Finally, the placeholders array is converted back to a string using the join() method and reinserted in the page.

  1. You'll add the programming logic to the drawCanvas() function shortly, but to test the code so far, create a dummy function:
// Draw the canvas function drawCanvas() { }
  1. Save hangman.js and test the app by loading index.html into Dreamweaver Live view or a browser and clicking New Game. Click a letter in the alphabetic keypad. If it's in the word to be guessed, it will replace one or more underscores in the placeholders as shown in Figure 1. You might need to click several letters before guessing a correct one, but all words contain at least one vowel, so try them first.
Figure 1. When you guess a correct letter, it's inserted in the appropriate space.
Figure 1. When you guess a correct letter, it's inserted in the appropriate space.

Drawing the Hangman gallows and victim in HTML5 canvas

The HTML5 <canvas> element creates a blank area within a web page that allows you to draw not only static shapes, text, and other graphic elements, but you can also animate them. In that sense, it's like Flash or Silverlight, except that it works natively in the browser without a plugin. However, using <canvas> is very different. For one thing, there are no dedicated programs—at least not yet—to help build the graphic content. Other important differences are that a <canvas> element doesn't have a timeline, nor can it retain a library of reusable symbols. Each time you update the graphic content, the entire <canvas> needs to be redrawn.

The advantage of using <canvas> is that it's very fast and lightweight. Although you can import images into a <canvas> element, all the shapes in the Hangman game are generated dynamically by JavaScript using the Canvas 2D Context API.

A quick introduction to the Canvas 2D Context API

To draw on a <canvas> element, you need to create a context object using the getContext() method, which takes as its sole argument a string indicating the type of context you want. At the moment, the only type is 2d, but it's expected there'll eventually be 3d as well. This is how you obtain a context object for a <canvas> element with the ID stage :

var canvas = document.getElementById('stage'), c = canvas.getContext('2d');

Once you have a context object, you can draw on the <canvas> element using the object's properties and methods. Table 1 lists the properties used in the Hangman game.

Table 1. Basic Canvas 2D Context properties

Property

Description

fillStyle

The current color, pattern, or gradient used for filling paths. Colors can be specified using any CSS color format. The default is black.

font

Specifies how the text should look using the same syntax as the CSS shorthand font property.

lineWidth

Specifies the width (in pixels) for lines. The default value is 1. Lines are centered over the path.

strokeStyle

Specifies the color, pattern, or gradient used for drawing lines (stroking paths). The default is black.

textAlign

Specifies the horizontal alignment of text. Valid values are left , center , right , start , and end . In left-to-right languages like English, start is the same as left , and end is the same as right .

Lines, shapes, and other objects are drawn on the <canvas> element using a grid system with its origin (0, 0) at the top-left corner. The X axis increases horizontally to the right, and the Y axis increases vertically downwards. By default, the units along each axis are equivalent to pixels, but this changes if the context is scaled. Table 2 lists the methods used in the Hangman game.

Table 2. Basic Canvas 2D Context methods

Method

Description

arc(x, y, radius, startAngle, endAngle, counterclockwise)

Creates an arc centered at x and y with the specified radius between the two angles. The angles must be specified in radians. If the optional final argument is set to true , the arc is drawn in a counterclockwise direction.

beginPath()

Discards any current path and begins a new one.

fillText(string, x, y, maxWidth)

Displays the string specified as the first argument using the current font and fillStyle properties. The text is anchored at point x, y . The position of the coordinates is determined by the textAlign property (for example, x is in the center of the text if textAlign is set to center ). The text is condensed or drawn in a smaller font if it overspills the width set by the optional maxWidth argument.

lineTo(x, y)

Draws a path in a straight line to the specified coordinates.

moveTo(x, y)

Moves the current position to the specified coordinates without drawing a path.

scale(scaleX, scaleY)

Scales the context object horizontally and vertically according to the specified scaling factors.

stroke()

Draws the outline of the current path using the current lineWidth and strokeStyle properties.

Drawing lines and shapes follows a linear process. You set the colors for stroke and fill, line width, and font using the properties in Table 1. Then you use the methods in Table 2 to move to where you want to start drawing, draw a path to outline the shape, and call the stroke() method to render it. You repeat the process to add further lines, shapes, or text. All elements inherit the same properties unless you change their values before adding a new path, shape, or text. One way to think of it is like an artist changing brushes or paint before moving onto the next stroke, except that it's done through a series of written commands rather than visually.

Drawing straight lines

To draw a line on a <canvas> you need to do the following:

  • Clear any existing path
  • Move to the starting point of the new line
  • Draw a path to the end point
  • Add a stroke to the path

The gallows and victim in the Hangman game use a lot of straight lines, so it makes sense to create a custom function to perform these four steps instead of repeatedly typing out similar code.

Add the following function definition to hangman.js:

function drawLine(context, from, to) { context.beginPath(); context.moveTo(from[0], from[1]); context.lineTo(to[0], to[1]); context.stroke(); }

The drawLine() function takes three arguments: the context object, and two arrays containing the coordinates of the start and end points. For example, to draw a straight line from the top-left corner to a point 100 pixels to the right and 50 pixels down, you call the function like this:

drawLine(c, [0,0], [100,50]);

This has the same effect as the following four lines of code:

c.beginPath(); c.moveTo(0, 0); c.lineTo(100, 50); c.stroke();

Scripting the drawCanvas() function

When drawing on a <canvas> element, you can't add to an existing design. You need to draw everything afresh each time you want to update it. To avoid a lot of repetitive code, the drawCanvas() function uses conditional statements to control how much to draw of the gallows and victim (see Figure 2) depending on the number of bad guesses.

Figure 2. The simple drawing reflects the game's hand-drawn origins.
Figure 2. The simple drawing reflects the game's hand-drawn origins.

When the canvas is first drawn, only the ground (the green line) is visible. If badGuesses is more than 0, the upright of the gallows is added. If badGuesses is more than 1, drawCanvas() continues to the cross spar of the gallows. If it's more than 2, it adds the rope and head, and so on.

Because everything needs to be drawn afresh each time, you need to clear the <canvas> element before drawing. Resetting the dimensions of a <canvas> element, even to the same size, automatically deletes its contents. The canvas global variable in hangman.js contains a reference to the <canvas> element, so clearing it is as simple as this:

canvas.width = canvas.width;

The drawCanvas() function is quite long, but the preceding explanation and inline comments should help you understand how it works. Here's what it looks like:

// Draw the canvas function drawCanvas() { var c = canvas.getContext('2d'); // reset the canvas and set basic styles canvas.width = canvas.width; c.lineWidth = 10; c.strokeStyle = 'green'; c.font = 'bold 24px Optimer, Arial, Helvetica, sans-serif'; c.fillStyle = 'red'; // draw the ground drawLine(c, [20,190], [180,190]); // start building the gallows if there's been a bad guess if (badGuesses > 0) { // create the upright c.strokeStyle = '#A52A2A'; drawLine(c, [30,185], [30,10]); if (badGuesses > 1) { // create the arm of the gallows c.lineTo(150,10); c.stroke(); } if (badGuesses > 2) { c.strokeStyle = 'black'; c.lineWidth = 3; // draw rope drawLine(c, [145,15], [145,30]); // draw head c.beginPath(); c.moveTo(160, 45); c.arc(145, 45, 15, 0, (Math.PI/180)*360); c.stroke(); } if (badGuesses > 3) { // draw body drawLine(c, [145,60], [145,130]); } if (badGuesses > 4) { // draw left arm drawLine(c, [145,80], [110,90]); } if (badGuesses > 5) { // draw right arm drawLine(c, [145,80], [180,90]); } if (badGuesses > 6) { // draw left leg drawLine(c, [145,130], [130,170]); } if (badGuesses > 7) { // draw right leg and end game drawLine(c, [145,130], [160,170]); c.fillText('Game over', 45, 110); // remove the alphabet pad letters.innerHTML = ''; // display the correct answer // need to use setTimeout to prevent race condition setTimeout(showResult, 200); // increase score of lost games // display the score after two seconds // code to be added later } } // if the word has been guessed correctly, display message, // update score of games won, and then show score after 2 seconds if (correctGuesses == wordLength) { letters.innerHTML = ''; c.fillText('You won!', 45,110); // increase score of won games // display score // code to be added later } }

Note the following points:

  • The font stack specifies Optimer as the first font. This is for browsers that don't support canvas text. You'll load a JavaScript font for older browsers later.
  • The head is drawn using the arc() method, which requires the start and end angles to be specified in radians. To convert degrees to radians, you divide π (pi) by 180 and multiply the result by the number of degrees. Setting the start to 0 and the end to 360 degrees is how you draw a complete circle. When drawing a partial arc, a start angle of 0 starts drawing from the 3 o'clock position.
  • If the word hasn't been guessed by the end of the game, the showResult() function—which you'll define next—displays the correct result. However, in testing, I found it necessary to delay its execution for 0.2 seconds using setTimeout() . Otherwise, the result fails to appear.
  • Code for displaying the score will be added later.

Displaying the correct answer

Playing Hangman can be frustrating, so you need to take losers out of their misery by revealing the word they failed to guess. The showResult() function does just that. It's simply an adaptation of the loop in the checkLetter() function, so it needs little explanation. Here's what it looks like:

// When the game is over, display missing letters in red function showResult() { var placeholders = word.innerHTML; placeholders = placeholders.split(''); for (i = 0; i < wordLength; i++) { if (placeholders[i] == '_') { placeholders[i] = '<span style="color:red">' + wordToGuess.charAt(i).toUpperCase() + '</span>'; } } word.innerHTML = placeholders.join(''); }

Each time the loop runs, it checks whether the current placeholder is an underscore. If it is, it replaces it with an uppercase version of the correct letter wrapped in <span> tags with an inline style that sets the color to red.

Using local storage to keep score

The Web Storage API offers a simple, yet effective way to store data related to a web page or app on the user's local computer. There are two types of storage:

  • Local storage: data is retained until deleted by the user or the web application.
  • Session storage: data is automatically deleted when the browser is closed.

The browser automatically allocates separate local and session storage areas for each domain, and stores data as key/value pairs. Data stored by one page is available to any other page from the same domain, although local and session values remain separate. You store and retrieve values using the localStorage and sessionStorage objects and the methods listed in Table 3.

Table 3. Web storage methods.

Method

Description

setItem(key, value)

Stores a key/value pair. If the key already exists, the current value is replaced by the new one. Both key and value must be strings.

getItem(key)

Retrieves the value stored by the specified key.

removeItem(key)

Deletes the key and its value.

clear()

Clears all keys and their related values. Use with care because other pages might still need access to the key/value pairs.

key(num)

Retrieves the name of the key at the specified numeric position.

The key() method is of limited value unless you know the order in which the key/value pairs have been listed. However, the localStorage and sessionStorage objects have a length property, so you can retrieve the name of the most recently stored key like this:

var latest = localStorage.key(localStorage.length - 1);

All browsers in widespread use, with the exception of IE 6 & 7, support web storage.

Adding support for local storage in IE 6 & 7

Thanks to Modernizr, you can test whether the browser supports local storage, and load a couple of JavaScript polyfills if it doesn't.

  1. Go to https://gist.github.com/350433 to get a copy of the local storage polyfill by Remy Sharp.
  2. Click the "raw" link (circled in Figure 3) at the top-right of the JavaScript code area to open the code in the browser window.
Figure 3. Click the "raw" link to get just the script.
Figure 3. Click the "raw" link to get just the script.
  1. Use the browser's Save As menu option to save the script as storage_polyfill.js in the hangman/scripts folder.
  2. Remy Sharp's polyfill uses JSON (JavaScript Object Notation), which isn't supported by older versions of IE, so you also need a file by Douglas Crockford, the creator of JSON. Go to https://github.com/douglascrockford/JSON-js, and click the link for json2.js.
  3. When the script appears, click the "raw" link to load the code into the browser window, and save it as json2.js in hangman/scripts.
  4. To ensure these two scripts are loaded only by browsers that don't support local storage, you need to amend the <script> block in the <head> of index.html like this:
<script> Modernizr.load({ test: Modernizr.localstorage, nope: ['scripts/json2.js', 'scripts/storage_polyfill.js'], both: ['scripts/jquery-1.7.min.js', 'scripts/hangman.js'], complete: function() { init(); } }); </script>

This adds two properties to the object literal passed to Modernizr.load() . The first one ( test ) checks whether the browser supports local storage. If it doesn't, the second property ( nope ) loads the two polyfills. Note that json2.js is loaded first because storage_polyfill.js is dependent on it.

The name of the load property in the original script is also changed to both .

The object passed as an argument Modernizr.load() can have the following properties, all of which are optional:

  • test : The browser feature you want to test (see Tables 1 and 2 in Using Modernizr to test HTML5 and CSS3 browser support).
  • yep : Files to be loaded conditionally if the browser passes the test.
  • nope : Files to be loaded conditionally if the browser fails the test.
  • both : Files to be loaded by all browsers.
  • complete : Code to be executed after all files are loaded.

Storing and displaying the score

The score needs to be set and displayed on three occasions: when the game first loads, when a game is lost, and when a game is won. This means adding code to the init() and drawCanvas() functions. A separate function displays the score. Because local storage is shared by all pages from the same domain, it's important to choose unique names for the keys to avoid overwriting each other's values. So, instead of just win and lose , I'll use hangmanWin and hangmanLose as the local storage keys.

  1. The only time the init() function needs to set the score is the first time the game is loaded. It needs to check whether hangmanWin and hangmanLose exist. If they don't, they need to be set to 0. Otherwise, the function that displays the score uses the existing values. Add the following code just before the closing curly brace of init() :
// Initialize the scores and store locally if not already stored if (localStorage.getItem('hangmanWin') == null) { localStorage.setItem('hangmanWin', '0'); } if (localStorage.getItem('hangmanLose') == null) { localStorage.setItem('hangmanLose', '0'); } showScore();

You'll add the code for showScore() shortly.

  1. When a game is lost, you need to retrieve the existing value of hangmanLose and increment it by one, then display the score after a pause of two seconds. In drawCanvas() , insert the following code in the conditional statement that runs when badGuesses is greater than seven (it goes in place of the "code to be added later" comment):
// increase score of lost games // display the score after two seconds localStorage.setItem('hangmanLose', 1 + parseInt(localStorage.getItem('hangmanLose'))); setTimeout(showScore, 2000);

The important point to note is that values are stored as strings. That's why localStorage.getItem('hangmanLose') is passed to parseInt() before it's added to 1. If you fail to convert hangmanLose to a number, the values will be concatenated as strings, resulting in 10, 110, 1110, and so on.

  1. The code to increase the number of games won is very similar. It goes in the conditional statement that runs if correctGuesses is the same as wordLength :
// increase score of won games // display score localStorage.setItem('hangmanWin', 1 + parseInt(localStorage.getItem('hangmanWin'))); setTimeout(showScore, 2000);
  1. You can now add the function that displays the score. The complete code looks like this:
// Display the score in the canvas function showScore() { var won = localStorage.getItem('hangmanWin'), lost = localStorage.getItem('hangmanLose'), c = canvas.getContext('2d'); // clear the canvas canvas.width = canvas.width; c.font = 'bold 24px Optimer, Arial, Helvetica, sans-serif'; c.fillStyle = 'red'; c.textAlign = 'center'; c.fillText('YOUR SCORE', 100, 50); c.font = 'bold 18px Optimer, Arial, Helvetica, sans-serif'; c.fillText('Won: ' + won + ' Lost: ' + lost, 100, 80); }

This is fairly straightforward. The getItem() method retrieves the numbers of wins and losses, which are stored as local variables. A local variable is also created for the <canvas> element's context. The score is then displayed using Canvas 2D Context properties and methods. Note that to change the size of the font for the second line, you need to declare the font weight and font families again. There is no property that sets the font size on its own.

  1. You can now replace the dummy code in the resetScore() function that you created in Part 1 of this tutorial. Amend the function like this:
// Reset stored scores to zero function resetScore() { localStorage.setItem('hangmanWin', '0'); localStorage.setItem('hangmanLose', '0'); showScore(); }
  1. Save hangman.js and load index.html into a browser to test it. The score should be displayed as shown in Figure 4.
Figure 4. The score is displayed in the <canvas> element.
Figure 4. The score is displayed in the <canvas> element.
  1. Click the New Game button. As Figure 5 shows, the score remains onscreen.
Figure 5. The score remains onscreen when you begin a new game.
Figure 5. The score remains onscreen when you begin a new game.

This seems OK, but if you're lucky enough to guess the first letter correctly, the score still remains onscreen, as shown in Figure 6.

Figure 6. The score remains onscreen until you make a bad guess.
Figure 6. The score remains onscreen until you make a bad guess.
  1. Although making a bad guess triggers drawCanvas() , I think it looks better if you remove the score as soon as a new game begins. Amend the newGame() function by adding the following line immediately before the closing curly brace:
drawCanvas();
  1. Save hangman.js and reload index.html in the browser. This time when you click the New Game button, the score is removed, and a horizontal green line appears representing the ground where the gallows will be built (Figure 7).
Figure 7. The <canvas> element is now redrawn when you start a new game.
Figure 7. The <canvas> element is now redrawn when you start a new game.

Making the game available offline

The Hangman game is now complete, but you can make it available offline by utilizing the application cache in all modern browsers (but not IE 6–8). Unlike the normal browser cache, which stores files temporarily, the application cache stores files indefinitely until they're deleted by the user or updated.

To instruct the browser to download the web app's files, you need to list all the files in a special file called a manifest and attach the manifest to the HTML page.

  1. Create a text file and save it as hangman.appcache in the hangman folder.
  2. The first line of the manifest must begin with CACHE MANIFEST on a line of its own. Then you list the path and filename of each file you want the browser to store in the application cache. Each file goes on a separate line. It's also recommended to add a line indicating the version number of the manifest. Because IE 6–8 don't support application caches, don't list the JavaScript polyfills for local storage. Otherwise, they will be downloaded by browsers that don't need them. The contents of hangman.appcache should look like this:
CACHE MANIFEST # version 1 index.html images/icons.png scripts/hangman.js scripts/jquery-1.7.min.js scripts/modernizr.hangman.js styles/hangman.css
  1. Attach the manifest to index.html by amending the opening <html> tag like this:
<html class="no-js" manifest="hangman.appcache">
  1. The manifest file needs to be served by the web browser with the text/cache‑manifest MIME type. Because application caches are a new feature in HTML5, many web servers are unlikely to support this MIME type. If your site is running on an Apache web server and you have permission to configure it using an .htaccess file, add the following line to your existing .htaccess file or create a new file in your site root:
AddType text/cache-manifest .appcache

If your website runs on a different server, or you can't use .htaccess files, contact your hosting company or server administrator and ask for .appcache files to be served with the text/cache-manifest MIME type.

Note: An earlier draft of the HTML5 specification used .manifest as the filename extension for manifest files, but this was later changed to .appcache . Both are acceptable, but .appcache is now the preferred version.

Browsers look only at the manifest when deciding whether a file needs to be stored in the application cache. They ignore the date individual files were created or modified. If you update any of the files listed in a manifest, you need to change the manifest's version number and upload the new manifest with the other files.

Dreamweaver doesn't currently recognize the .appcache or .manifest filename extensions. To edit a manifest, right-click the file in the Files panel and select Open With > Dreamweaver.

Enabling canvas and canvas text in IE 6–8

To make the Hangman game playable in older versions of Internet Explorer, you need a polyfill called explorercanvas, which emulates most aspects of HTML5 canvas. However, it doesn't support canvas text. So, you also need a polyfill called canvas-text and a font that has been encoded as JavaScript. All the necessary files are free and easy to use.

  1. Go to http://code.google.com/p/explorercanvas/downloads/list and download excanvas_r3.zip.
  2. Unzip excanvas_r3.zip and copy excanvas.js to the hangman/scripts folder. This is the only file you need from the downloaded package.
  3. Go to http://code.google.com/p/canvas-text/source/browse/trunk/canvas.text.js and copy the JavaScript code to your clipboard.
  4. Paste the code into a blank JavaScript file and save it as canvas.text.js in the hangman/scripts folder.
  5. Go to http://code.google.com/p/canvas-text/source/browse/trunk#trunk%2Ffaces and click the link for optimer-bold-normal.js. This opens a page consisting mainly of numbers that map the Optimer bold font to JavaScript.
  6. Copy the JavaScript code and paste it into a JavaScript file.
  7. Save the file as optimer-bold-normal.js in the hangman/scripts folder.
  8. The explorer canvas polyfill works only with older versions of Internet Explorer, so attach it to index.html before the Modernizr script, and wrap it conditional comment like this:
<!--[if lte IE 8]> <script src="scripts/excanvas.js"></script> <![endif]--> <script src="scripts/modernizr.hangman.js"></script>
  1. Although the canvas text polyfills are mainly aimed at Internet Explorer, it's possible that someone is using an obscure browser that supports canvas, but not canvas text. Modernizr can test for canvas text and load the files conditionally. Amend the Modernizr.load() script like this:
<script> Modernizr.load([{ test: Modernizr.canvastext, nope: ['scripts/canvas.text.js', 'scripts/optimer-bold-normal.js'] }, { test: Modernizr.localstorage, nope: ['scripts/json2.js', 'scripts/storage_polyfill.js'], both: ['scripts/jquery-1.7.min.js', 'scripts/hangman.js'], complete: function() { init(); } }]); </script>

Instead of passing a single object as the argument to the load() method, this passes an array of two objects. The second object is the same as before. The first one tests for canvas text, and loads the polyfills if the browser doesn't support it.

Some final tweaks for the iPhone and iPod touch

The iPhone and iPod touch don't have as tall a screen as most Android phones, and the Mobile Safari chrome reduces the available screen even further. To compensate for this, you need to make some adjustments to the size and position of the alphabet keypad. You also need to take into account that iOS always regards width as referring to the horizontal size of the screen in portrait orientation. The iPhone and iPod touch always report height as 480px and width as 320px, regardless of orientation. As a result, the layout doesn't adjust properly when you turn the iPhone or iPod touch into landscape orientation.

To fix these problems, add the following media queries at the bottom of hangman.css:

@media only screen and (max-height: 480px) { #letters { margin-left: 6px; } #letters div { font-size: 125%; margin: 0 2px 2px 2px; } } @media only screen and (max-height: 480px) and (orientation: landscape) { #stage { margin-top: 60px; } } @media only screen and (min-width: 320px) and (orientation: landscape) { .js #helptext { width: 400px; } }

Because of the screen size on the iPhone and iPod touch, you need to manually scroll the bottom rows of the alphabetic keypad into view when you click the New Game button. Another minor tweak avoids this problem. Add the following line of code just before the closing curly brace in the newGame() function:

window.scrollBy(0, 200);

This scrolls the page up by 200 pixels on small screens. There’s no movement on larger screens because the bottom of the page is already in view.

Where to go from here

You should now have a fully functioning Hangman game that works both online and offline in a wide range of devices. If you run into any problems, check your code against the version in hangman_pt2_finish.zip.

This simple web app has brought together several HTML5-related technologies: canvas, canvas text, local storage, and application cache. It makes extensive use of CSS3 media queries to serve different styles depending on the width and orientation of the screen. You've also seen how Modernizr and polyfills can fill in the cracks in older browsers that don't support all the latest features.

My book, Adobe Dreamweaver CS5.5 Studio Techniques: Designing and Developing for Mobile with jQuery, HTML5, and CSS, covers local storage and application cache in greater depth. You might also find the following resources helpful:

  • Learn to use HTML5 canvas
  • Let's call it a draw(ing surface)
  • Introducing the HTML5 storage APIs
  • Let's take this offline
  • Using Modernizr to detect HTML5 and CSS3 support
  • Modernizr documentation
  • HTML5 cross-browser polyfills

 

More Like This

  • Using the Geolocation API
  • CSS3 basics
  • Developing HTML5 games with Impact JavaScript game engine and Dreamweaver CS5.5
  • Introducing the HTML5 storage APIs
  • Introducing theexpressiveweb.com beta
  • Adobe, standards, and HTML5
  • Real-time data exchange in HTML5 with WebSockets
  • Pseudo-classical object-oriented programming in JavaScript with Minion
  • Object types in JavaScript
  • Backbone.js Wine Cellar tutorial – Part 2: CRUD

Products

  • Acrobat
  • Creative Cloud
  • Creative Suite
  • Digital Marketing Suite
  • Digital Publishing Suite
  • Elements
  • Mobile Apps
  • Photoshop
  • Touch Apps
  • Student and Teacher Editions

Solutions

  • Digital marketing
  • Digital media
  • Web Experience Management

Industries

  • Education
  • Financial services
  • Government

Help

  • Product help centers
  • Orders and returns
  • Downloading and installing
  • My Adobe

Learning

  • Adobe Developer Connection
  • Adobe TV
  • Training and certification
  • Forums
  • Design Center

Ways to buy

  • For personal and home office
  • For students, educators, and staff
  • For small and medium businesses
  • For businesses, schools, and government
  • Special offers

Downloads

  • Adobe Reader
  • Adobe Flash Player
  • Adobe AIR
  • Adobe Shockwave Player

Company

  • News room
  • Partner programs
  • Corporate social responsibility
  • Career opportunities
  • Investor Relations
  • Events
  • Legal
  • Security
  • Contact Adobe
Choose your region United States (Change)
Choose your region Close

North America

Europe, Middle East and Africa

Asia Pacific

  • Canada - English
  • Canada - Français
  • Latinoamérica
  • México
  • United States

South America

  • Brasil
  • Africa - English
  • Österreich - Deutsch
  • Belgium - English
  • Belgique - Français
  • België - Nederlands
  • България
  • Hrvatska
  • Česká republika
  • Danmark
  • Eastern Europe - English
  • Eesti
  • Suomi
  • France
  • Deutschland
  • Magyarország
  • Ireland
  • Israel - English
  • ישראל - עברית
  • Italia
  • Latvija
  • Lietuva
  • Luxembourg - Deutsch
  • Luxembourg - English
  • Luxembourg - Français
  • الشرق الأوسط وشمال أفريقيا - اللغة العربية
  • Middle East and North Africa - English
  • Moyen-Orient et Afrique du Nord - Français
  • Nederland
  • Norge
  • Polska
  • Portugal
  • România
  • Россия
  • Srbija
  • Slovensko
  • Slovenija
  • España
  • Sverige
  • Schweiz - Deutsch
  • Suisse - Français
  • Svizzera - Italiano
  • Türkiye
  • Україна
  • United Kingdom
  • Australia
  • 中国
  • 中國香港特別行政區
  • Hong Kong S.A.R. of China
  • India - English
  • 日本
  • 한국
  • New Zealand
  • 台灣

Southeast Asia

  • Includes Indonesia, Malaysia, Philippines, Singapore, Thailand, and Vietnam - English

Copyright © 2012 Adobe Systems Incorporated. All rights reserved.

Terms of Use | Privacy Policy and Cookies (Updated)

Ad Choices

Reviewed by TRUSTe: site privacy statement