You are previewing HTML5: Up and Running.

HTML5: Up and Running

Cover of HTML5: Up and Running by Mark Pilgrim Published by O'Reilly Media, Inc.
  1. HTML5: Up and Running
    1. SPECIAL OFFER: Upgrade this ebook with O’Reilly
    2. Preface
      1. Diving In
      2. Conventions Used in This Book
      3. Using Code Examples
      4. A Note on the Editions of This Book
      5. Safari® Books Online
      6. How to Contact Us
    3. 1. How Did We Get Here?
      1. Diving In
      2. MIME Types
      3. A Long Digression into How Standards Are Made
      4. An Unbroken Line
      5. A Timeline of HTML Development from 1997 to 2004
      6. Everything You Know About XHTML Is Wrong
      7. A Competing Vision
      8. What Working Group?
      9. Back to the W3C
      10. Postscript
      11. Further Reading
    4. 2. Detecting HTML5 Features
      1. Diving In
      2. Detection Techniques
      3. Modernizr: An HTML5 Detection Library
      4. Canvas
      5. Canvas Text
      6. Video
      7. Video Formats
      8. Local Storage
      9. Web Workers
      10. Offline Web Applications
      11. Geolocation
      12. Input Types
      13. Placeholder Text
      14. Form Autofocus
      15. Microdata
      16. Further Reading
    5. 3. What Does It All Mean?
      1. Diving In
      2. The Doctype
      3. The Root Element
      4. The <head> Element
      5. New Semantic Elements in HTML5
      6. A Long Digression into How Browsers Handle Unknown Elements
      7. Headers
      8. Articles
      9. Dates and Times
      10. Navigation
      11. Footers
      12. Further Reading
    6. 4. Let’s Call It a Draw(ing Surface)
      1. Diving In
      2. Simple Shapes
      3. Canvas Coordinates
      4. Paths
      5. Text
      6. Gradients
      7. Images
      8. What About IE?
      9. A Complete Example
      10. Further Reading
    7. 5. Video on the Web
      1. Diving In
      2. Video Containers
      3. Video Codecs
      4. Audio Codecs
      5. What Works on the Web
      6. Licensing Issues with H.264 Video
      7. Encoding Ogg Video with Firefogg
      8. Batch Encoding Ogg Video with ffmpeg2theora
      9. Encoding H.264 Video with HandBrake
      10. Batch Encoding H.264 Video with HandBrake
      11. Encoding WebM Video with ffmpeg
      12. At Last, the Markup
      13. What About IE?
      14. A Complete Example
      15. Further Reading
    8. 6. You Are Here (And So Is Everybody Else)
      1. Diving In
      2. The Geolocation API
      3. Show Me the Code
      4. Handling Errors
      5. Choices! I Demand Choices!
      6. What About IE?
      7. geo.js to the Rescue
      8. A Complete Example
      9. Further Reading
    9. 7. The Past, Present, and Future of Local Storage for Web Applications
      1. Diving In
      2. A Brief History of Local Storage Hacks Before HTML5
      3. Introducing HTML5 Storage
      4. Using HTML5 Storage
      5. HTML5 Storage in Action
      6. Beyond Named Key/Value Pairs: Competing Visions
      7. Further Reading
    10. 8. Let’s Take This Offline
      1. Diving In
      2. The Cache Manifest
      3. The Flow of Events
      4. The Fine Art of Debugging, a.k.a. “Kill Me! Kill Me Now!”
      5. Let’s Build One!
      6. Further Reading
    11. 9. A Form of Madness
      1. Diving In
      2. Placeholder Text
      3. Autofocus Fields
      4. Email Addresses
      5. Web Addresses
      6. Numbers As Spinboxes
      7. Numbers As Sliders
      8. Date Pickers
      9. Search Boxes
      10. Color Pickers
      11. And One More Thing...
      12. Further Reading
    12. 10. “Distributed,” “Extensibility,” and Other Fancy Words
      1. Diving In
      2. What Is Microdata?
      3. The Microdata Data Model
      4. Marking Up People
      5. Marking Up Organizations
      6. Marking Up Events
      7. Marking Up Reviews
      8. Further Reading
    13. A. The All-in-One Almost-Alphabetical Guide to Detecting Everything
      1. List of Elements
      2. Further Reading
    14. Index
    15. About the Author
    16. Colophon
    17. SPECIAL OFFER: Upgrade this ebook with O’Reilly
O'Reilly logo

A Complete Example

Halma is a centuries-old board game. Many variations exist. In this example, I’ve created a solitaire version of Halma with nine pieces on a 9 × 9 board. In the beginning of the game, the pieces form a 3 × 3 square in the bottom-left corner of the board. The object of the game is to move all the pieces so they form a 3 × 3 square in the upper-right corner of the board, in the least possible number of moves.

There are two types of legal moves in Halma:

  • Take a piece and move it to any adjacent empty square. An “empty” square is one that does not currently have a piece in it. An “adjacent” square is immediately north, south, east, west, northwest, northeast, southwest, or southeast of the piece’s current position. (The board does not wrap around from one side to the other. If a piece is in the leftmost column, it cannot move west, northwest, or southwest. If a piece is in the bottommost row, it cannot move south, southeast, or southwest.)

  • Take a piece and hop over an adjacent piece, and possibly repeat. That is, if you hop over an adjacent piece, then hop over another piece adjacent to your new position, that counts as a single move. In fact, any number of hops still counts as a single move. (Since the goal is to minimize the total number of moves, doing well in Halma involves constructing, and then using, long chains of staggered pieces so that other pieces can hop over them in long sequences.)

Figure 4-16 is a screenshot of the game itself; you can also play it online if you want to poke at it with your browser’s developer tools.

So how does it work? I’m so glad you asked. I won’t show all the code here (you can see it at http://diveintohtml5.org/examples/halma.js). I’ll skip over most of the gameplay code itself, but I want to highlight a few parts of the code that deal with actually drawing on the canvas and responding to mouse clicks on the <canvas> element.

Starting position of a Halma game

Figure 4-16. Starting position of a Halma game

During page load, we initialize the game by setting the dimensions of the <canvas> itself and storing a reference to its drawing context:

gCanvasElement.width = kPixelWidth;
gCanvasElement.height = kPixelHeight;
gDrawingContext = gCanvasElement.getContext("2d");

Then we do something you haven’t seen yet—we add an event listener to the <canvas> element to listen for click events:

gCanvasElement.addEventListener("click", halmaOnClick, false);

The halmaOnClick() function gets called when the user clicks anywhere within the canvas. Its argument is a MouseEvent object that contains information about where the user clicked:

function halmaOnClick(e) {
    var cell = getCursorPosition(e);

    // the rest of this is just gameplay logic
    for (var i = 0; i < gNumPieces; i++) {
       if ((gPieces[i].row == cell.row) &&
           (gPieces[i].column == cell.column)) {
           clickOnPiece(i);
           return;
       }
    }
    clickOnEmptyCell(cell);
}

The next step is to take the MouseEvent object and calculate which square on the Halma board just got clicked. The Halma board takes up the entire canvas, so every click is somewhere on the board. We just need to figure out where. This is tricky, because mouse events are implemented differently in just about every browser:

function getCursorPosition(e) {
    var x;
    var y;
    if (e.pageX || e.pageY) {
      x = e.pageX;
      y = e.pageY;
    }
    else {
      x = e.clientX + document.body.scrollLeft +
           document.documentElement.scrollLeft;
      y = e.clientY + document.body.scrollTop +
           document.documentElement.scrollTop;
    }

At this point, we have x and y coordinates that are relative to the document (that is, the entire HTML page). But we want coordinates relative to the canvas. We can get them as follows:

x -= gCanvasElement.offsetLeft;
y -= gCanvasElement.offsetTop;

Now we have x and y coordinates that are relative to the canvas (see Canvas Coordinates). That is, if x is 0 and y is 0 at this point, we know that the user just clicked the top-left pixel of the canvas.

From here, we can calculate which Halma square the user clicked, and then act accordingly:

    var cell = new Cell(Math.floor(y/kPieceWidth),
                        Math.floor(x/kPieceHeight));
    return cell;
}

Whew! Mouse events are tough. But you can use the same logic (in fact, this exact code) in all of your own canvas-based applications. Remember: mouse clickdocument-relative coordinatescanvas-relative coordinatesapplication-specific code.

OK, let’s look at the main drawing routine. Because the graphics are so simple, I’ve chosen to clear and redraw the board in its entirety every time anything changes within the game. This is not strictly necessary. The canvas drawing context will retain whatever you have previously drawn on it, even if the user scrolls the canvas out of view or changes to another tab and then comes back later. If you’re developing a canvas-based application with more complicated graphics (such as an arcade game), you can optimize performance by tracking which regions of the canvas are “dirty” and redrawing just the dirty regions. But that is outside the scope of this book. Here’s the board-clearing code:

gDrawingContext.clearRect(0, 0, kPixelWidth, kPixelHeight);

The board-drawing routine should look familiar. It’s very similar to how we drew the canvas coordinates diagram (see Canvas Coordinates):

gDrawingContext.beginPath();

/* vertical lines */
for (var x = 0; x <= kPixelWidth; x += kPieceWidth) {
    gDrawingContext.moveTo(0.5 + x, 0);
    gDrawingContext.lineTo(0.5 + x, kPixelHeight);
}

/* horizontal lines */
for (var y = 0; y <= kPixelHeight; y += kPieceHeight) {
    gDrawingContext.moveTo(0, 0.5 + y);
    gDrawingContext.lineTo(kPixelWidth, 0.5 +  y);
}

/* draw it! */
gDrawingContext.strokeStyle = "#ccc";
gDrawingContext.stroke();

The real fun begins when we go to draw each of the individual pieces. A piece is a circle, something we haven’t drawn before. Furthermore, if the user selects a piece in anticipation of moving it, we want to draw that piece as a filled-in circle. Here, the argument p represents a piece, which has row and column properties that denote the piece’s current location on the board. We use some in-game constants to translate (column, row) into canvas-relative (x, y) coordinates, then draw a circle, then (if the piece is selected) fill in the circle with a solid color:

function drawPiece(p, selected) {
    var column = p.column;
    var row = p.row;
    var x = (column * kPieceWidth) + (kPieceWidth/2);
    var y = (row * kPieceHeight) + (kPieceHeight/2);
    var radius = (kPieceWidth/2) - (kPieceWidth/10);

That’s the end of the game-specific logic. Now we have (x, y) coordinates, relative to the canvas, for the center of the circle we want to draw. There is no circle() method in the canvas API, but there is an arc() method. And really, what is a circle but an arc that goes all the way around? Do you remember your basic geometry? The arc() method takes a center point (x, y), a radius, a start and end angle (in radians), and a direction flag (false for clockwise, true for counterclockwise). We can use the Math module that’s built into JavaScript to calculate radians:

gDrawingContext.beginPath();
gDrawingContext.arc(x, y, radius, 0, Math.PI * 2, false);
gDrawingContext.closePath();

But wait! Nothing has been drawn yet. Like moveTo() and lineTo(), the arc() method is a “pencil” method (see Paths). To actually draw the circle, we need to set the strokeStyle and call stroke() to trace it in “ink”:

gDrawingContext.strokeStyle = "#000";
gDrawingContext.stroke();

What if the piece is selected? We can reuse the same path we created to draw the outline of the piece to fill in the circle with a solid color:

if (selected) {
    gDrawingContext.fillStyle = "#000";
    gDrawingContext.fill();
}

And that’s...well, that’s pretty much it. The rest of the program is game-specific logic—distinguishing between valid and invalid moves, keeping track of the number of moves, detecting whether the game is over. With nine circles, a few straight lines, and an onclick handler, we’ve created an entire game in <canvas>. Huzzah!

The best content for your career. Discover unlimited learning on demand for around $1/day.