You are previewing HTML5 Canvas.

HTML5 Canvas

Cover of HTML5 Canvas by Steve Fulton... Published by O'Reilly Media, Inc.
  1. HTML5 Canvas
    1. SPECIAL OFFER: Upgrade this ebook with O’Reilly
    2. A Note Regarding Supplemental Files
    3. Preface
      1. Running the Examples in the Book
      2. What You Need to Know
      3. How This Book Is Organized
      4. Conventions Used in This Book
      5. Using Code Examples
      6. We’d Like to Hear from You
      7. Safari® Books Online
      8. Acknowledgments
    4. 1. Introduction to HTML5 Canvas
      1. The Basic HTML Page
      2. Basic HTML We Will Use in This Book
      3. The Document Object Model (DOM) and Canvas
      4. JavaScript and Canvas
      5. HTML5 Canvas “Hello World!”
      6. Debugging with Console.log
      7. The 2D Context and the Current State
      8. The HTML5 Canvas Object
      9. Another Example: Guess The Letter
      10. What’s Next
    5. 2. Drawing on the Canvas
      1. The Basic File Setup for This Chapter
      2. The Basic Rectangle Shape
      3. The Canvas State
      4. Using Paths to Create Lines
      5. Advanced Path Methods
      6. Compositing on the Canvas
      7. Simple Canvas Transformations
      8. Filling Objects with Colors and Gradients
      9. Filling Shapes with Patterns
      10. Creating Shadows on Canvas Shapes
      11. What’s Next
    6. 3. The HTML5 Canvas Text API
      1. Displaying Basic Text
      2. Setting the Text Font
      3. Text and the Canvas Context
      4. Text with Gradients and Patterns
      5. Width, Height, Scale, and toDataURL() Revisited
      6. Final Version of Text Arranger
      7. What’s Next
    7. 4. Images on the Canvas
      1. The Basic File Setup for This Chapter
      2. Image Basics
      3. Simple Cell-Based Sprite Animation
      4. Advanced Cell-Based Animation
      5. Applying Rotation Transformations to an Image
      6. Creating a Grid of Tiles
      7. Zooming and Panning an Image
      8. Pixel Manipulation
      9. Copying from One Canvas to Another
      10. What’s Next
    8. 5. Math, Physics, and Animation
      1. Moving in a Straight Line
      2. Bouncing Off Walls
      3. Curve and Circular Movement
      4. Simple Gravity, Elasticity, and Friction
      5. Easing
      6. What’s Next?
    9. 6. Mixing HTML5 Video and Canvas
      1. HTML5 Video Support
      2. Converting Video Formats
      3. Basic HTML5 Video Implementation
      4. Preloading Video in JavaScript
      5. Video and the Canvas
      6. Video on the Canvas Examples
      7. Animation Revisited: Moving Videos
      8. What’s Next?
    10. 7. Working with Audio
      1. The Basic <audio> Tag
      2. Audio Formats
      3. Audio Tag Properties, Functions, and Events
      4. Playing a Sound with No Audio Tag
      5. Creating a Canvas Audio Player
      6. Case Study in Audio: Space Raiders Game
      7. What’s Next
    11. 8. Canvas Game Essentials
      1. Why Games in HTML5?
      2. Our Basic Game HTML5 File
      3. Our Game’s Design
      4. Game Graphics: Drawing with Paths
      5. Animating on the Canvas
      6. Applying Transformations to Game Graphics
      7. Game Graphic Transformations
      8. Game Object Physics and Animation
      9. A Basic Game Framework
      10. Putting It All Together
      11. The player Object
      12. Geo Blaster Game Algorithms
      13. The Geo Blaster Basic Full Source
      14. Rock Object Prototype
      15. What’s Next
    12. 9. Combining Bitmaps and Sound
      1. Geo Blaster Extended
      2. Creating a Dynamic Tile Sheet at Runtime
      3. A Simple Tile-Based Game
      4. What’s Next
    13. 10. Mobilizing Games with PhoneGap
      1. Going Mobile!
      2. Creating the iOS Application with PhoneGap
      3. Beyond the Canvas
      4. What’s Next
    14. 11. Further Explorations
      1. 3D with WebGL
      2. Multiplayer Applications with ElectroServer 5
      3. Conclusion
    15. Index
    16. About the Authors
    17. Colophon
    18. SPECIAL OFFER: Upgrade this ebook with O’Reilly
O'Reilly logo

Chapter 10. Mobilizing Games with PhoneGap

Going Mobile!

Nowadays it seems that everyone is making, planning to make, or thinking of making applications for mobile devices. Mobile is the next great (or maybe actually the current) place to make money by selling applications. The Apple iPhone is currently one of the most popular personal communication devices, and the iTunes Store gives budding application developers a place to show and sell the fruits of their labor. Apple separates their application-development platforms into three categories: Desktop (OS X), Browser (Safari), and iPhone/iPad/iPod Touch (iOS).

Most iOS applications, especially games, are written in Objective-C and compiled directly to the platform using the Xcode IDE. This is a large barrier to entry to develop native applications, as Objective-C is not widely used on platforms other than Apple devices. Up until early 2010, Objective-C/Xcode was the only viable development system for targeting iOS development.

In this chapter, we will “port” our HTML5 Canvas application to the iPhone using a technology called PhoneGap. PhoneGap allows an HTML application to run natively on an iPhone by packaging the application as a Safari Mobile “app.” This app can be run from the iPhone interface, and it will look and act like an app compiled in Objective-C. Applications packaged with PhoneGap can even be sold in the iTunes Store.

Note

Other third-party tools can be used to create iOS applications and games. For example, Unity (http://unity3d.com/) is a powerful game-development platform that can target iOS. In addition, there are tools such as Ansca’s Corona SDK (http://www.anscamobile.com/) that use scripting languages to harness the power of the platform.

Introducing PhoneGap

PhoneGap is an open source development tool created by Nitobi (http://www.nitobi.com) that acts as a software bridge between standards-based HTML development and several mobile platforms. Using PhoneGap, the HTML5 Canvas developer has access to the various hardware APIs for supported devices through an abstraction layer. This software interface allows the same code to be used to target features common among various devices—such as geolocation, touch screens, microphones, and other hardware capabilities—so that separate code does not need to be written for each device.

Warning

You will need an Intel-based Macintosh running Xcode to be able to compile a PhoneGap project. There currently is no development platform for Windows that will allow compiling Safari Mobile applications to the iOS platform with an Objective-C wrapper.

We won’t target too many specific iPhone features in this chapter. In the allotted space, we will cover the basics needed to take a simple application and get it up and running in the iPhone simulator, and then onto a physical device. We will then implement an accelerometer feature into our application.

Note

For further reading, Jonathan Stark’s Building iPhone Apps with HTML, CSS, and JavaScript (O’Reilly) covers PhoneGap and hardware feature API integration in detail. If you’d rather try this with Android, explore the similar Building Android Apps with HTML, CSS, and JavaScript (O’Reilly), also by Jonathan Stark, which applies PhoneGap to create applications for Android.

The Application

The application we are going to create is a simple BS Bingo game. BS Bingo was designed on paper well before mobile devices were available. This cynical game concept is based on the feeling (by some) that the typical business workplace has been overtaken with Dilbert- or Office Space-esque annoying corporate jargon and doublespeak. This doublespeak seems to have deeply rooted itself in the workplace over the last 20 years, mostly to the annoyance of software developers (such as ourselves).

In the pen-and-paper version of the game, each player brings a “bingo card” to a meeting where he expects to hear a lot of this corporate doublespeak. The bingo card is a 5×5 grid, and each of the 25 squares is filled with one of the annoying words or jargon phrases. During the meeting, each player marks off squares as the words or phrases are said aloud by the unsuspecting (and not playing) members of the meeting. When a player has a full column of his card marked off, he is supposed to jump up from the meeting table and yell “BS!”

Whether this game was ever widely played (or even played at all) is a debatable urban legend, but the simple concept of clicking squares to highlight them makes for a useful piece of code that we can build easily and then port to the iPhone. We are not even going to build the entire game here; we will leave extending it into a full application (possibly adding multiplayer, which is discussed in Chapter 11) for you, the reader.

The Code

Example 10-1 gives the code for our game. We’ll discuss the various functions in the next section before we move on to installing, modifying, and testing it in Xcode using PhoneGap. This version of the game will work fine on a Safari desktop browser. We will highlight the modifications necessary to port it to the iPhone in the next section.

Example 10-1. BSBingo.html full source listing

<!doctype html>
 <html lang="en">
 <head>
    <meta charset="UTF-8">

 <title>BS Bingo</title>
 <script src="modernizr-1.6.min.js"></script>
 <script src="TextButton.js"></script>
 <script src="ConsoleLog.js"></script>
 <script type="text/javascript">

 window.addEventListener('load', eventWindowLoaded, false);
 function eventWindowLoaded() {

    canvasApp();

 }

 function canvasSupport () {
     return Modernizr.canvas;
 }

 function canvasApp(){

    if (!canvasSupport()) {
           return;
      }else{
       theCanvas = document.getElementById("canvas");
        context = theCanvas.getContext("2d");
    }

    var bingoCard = [];
    var buttons = [];

    var standardJargonList = [];
    var tempButton = {};
    var clickSound;

    function initLists(){

       standardJargonList=[
        "Actionable", "Assessment" ,"Bandwidth", "Benchmark", 
        "Best\nPractices", "Bottle neck" , "Change\nManagement",  "Coach",
        "Competitive\nAdvantage", "Constraints", "Core\nCompetencies", 
        "Core values", "Critical\nthinking", "Cutting\nedge",
        "Dashboard", "Deliverables", "Enterprise","Gatekeeper", 
        "Individual\nContributor", "Leadership", "Matrix\norganisation",
        "Metrics", "Milestones", "Momentum", "Moving target", 
        "Initiative","Partnership", "Process", "Process\nmanagement",
        "Re-engineer", "Requirements", "Rightsize", "Seat at\nthe table",
        "Tentpole", " Silo", "Standards", "State of the art",
        "Supply chain", "Synergy","Teamwork", "Thought\nleader", 
        "Touchpoints", "Value\nadded", "Drink the\nKool Aid",
        "Baked In", "Champion", "Circle Back", "Dialogue", "Emerge", 
        "Enhance", "Evolve", "Execute", "Facilitate" ,"Incentivise", 
        "Leverage", "Partner", "Spearhead", "Strategize","Synergise", 
        "Throw\na\nCurve", "Touch Base", "Outside\nthe\nBox", 
        "Opportunity", "Open Door\nPolicy","Win-Win\n(Anything)",
        "Risk\n(Anything)","Proactive","Reactive","Buy-In",
        "Paradigm\nShift","Task-Oriented","Empower","Team\nPlayer",
        "Enterprise\nWide","Globalization","Localization",
        "Mission-critical", "Magic\nQuadrant","Agile\n(Anything)",
        "Waterfall","Outsourcing","Off-Shoring","Blue Sky",
        "20/20 \nindsight","Low\nHanging\nFruit","10,000\nFoot View",
        "Take\nOwnership","Ramp up", "Out of\nthe Box", "24x7", 
        "Fast Track", "Out of\nthe Loop", "In the\nLoop","Touch Base", 
        "Mindset", "Game Plan", "Bring to \nthe Table", "Drill Down", 
        "Elevator\nSpeech", "Level the\nPlaying field", 
        "Ping\n(Someone)","Pushback","Retool", "Take Away", 
        "Life-Time\nValue", "Thought\nLeadership", "Up Sell"
         ];

    }

    function initButtons(){
       buttons = [
          [

          new TextButton(0,0,"Button 
              0,0",85,50,gr,"#000000","#ffff00","#000000"),

          new TextButton(92,0,"Button 
              0,1",85,50,gr,"#000000","#ffff00","#000000"),

          new TextButton(184,0,"Button 
              0,2",85,50,gr,"#000000","#ffff00","#000000"),

          new TextButton(276,0,"Button 
              0,3",85,50,gr,"#000000","#ffff00","#000000"),

          new TextButton(368,0,"Button 
              0,4",85,50,gr,"#000000","#ffff00","#000000")

         ],

         [

          new TextButton(0,57,"Button 
              1,0",85,50,gr,"#000000","#ffff00","#000000"),

          new TextButton(92,57,"Button 
              1,1",85,50,gr,"#000000","#ffff00","#000000"),

          new TextButton(184,57,"Button 
              1,2",85,50,gr,"#000000","#ffff00","#000000"),

          new TextButton(276,57,"Button
              1,3",85,50,gr,"#000000","#ffff00","#000000"),

          new TextButton(368,57,"Button 
              1,4",85,50,gr,"#000000","#ffff00","#000000")

         ],

         [

          new TextButton(0,114,"Button 
              2,0",85,50,gr,"#000000","#ffff00","#000000"),

          new TextButton(92,114,"Button 
              2,1",85,50,gr,"#000000","#ffff00","#000000"),

          new TextButton(184,114,"Button 
              2,2",85,50,gr,"#000000","#ffff00","#000000"),

          new TextButton(276,114,"Button 
              2,3",85,50,gr,"#000000","#ffff00","#000000"),

          new TextButton(368,114,"Button 
              2,4",85,50,gr,"#000000","#ffff00","#000000")

         ],

         [

          new TextButton(0,171,"Button 
              3,0",85,50,gr,"#000000","#ffff00","#000000"),

          new TextButton(92,171,"Button 
              3,1",85,50,gr,"#000000","#ffff00","#000000"),

          new TextButton(184,171,"Button 
              3,2",85,50,gr,"#000000","#ffff00","#000000"),

          new TextButton(276,171,"Button 
              3,3",85,50,gr,"#000000","#ffff00","#000000"),

          new TextButton(368,171,"Button 
              3,4",85,50,gr,"#000000","#ffff00","#000000")

         ],

         [

          new TextButton(0,228,"Button 
              4,0",85,50,gr,"#000000","#ffff00","#000000"),

          new TextButton(92,228,"Button 
              4,1",85,50,gr,"#000000","#ffff00","#000000"),

          new TextButton(184,228,"Button 
              4,2",85,50,gr,"#000000","#ffff00","#000000"),

          new TextButton(276,228,"Button 
              4,3",85,50,gr,"#000000","#ffff00","#000000"),

          new TextButton(368,228,"Button 
              4,4",85,50,gr,"#000000","#ffff00","#000000")

         ]
       ];
    }

    function initSounds(){
       clickSound = document.getElementById('clicksound');
    }

    function chooseButtonsForCard(){
       //copy jargon into temp array
       var tempArray = [];
       for (var arrayctr=0;arrayctr<standardJargonList.length;arrayctr++){
          tempArray.push(standardJargonList[arrayctr]);
       }

       for (var ctr1=0;ctr1<buttons.length;ctr1++){

          for (var ctr2=0; ctr2<buttons[ctr1].length;ctr2++){
             var randInt = Math.floor(Math.random()*tempArray.length)
             buttons[ctr1][ctr2].text = tempArray[randInt];
             tempArray.splice(randInt,1)
          }
       }

    }



    function drawScreen() {
       //ConsoleLog.log("standardAcronymList="+standardAcronymList.length);
       //ConsoleLog.log("standardJargonList="+standardJargonList.length);
       for (var ctr1=0;ctr1<buttons.length;ctr1++){
          ConsoleLog.log("ctr1="+ctr1)
          for (var ctr2=0; ctr2<buttons[ctr1].length;ctr2++){
             ConsoleLog.log("ctr2="+ctr2)
             buttons[ctr1][ctr2].draw(context);
          }
       }

    }

    function onMouseClick(e) {

       //select case through states and then the locations of
       //buttons in those states
       mouseX = e.clientX-theCanvas.offsetLeft;
       mouseY = e.clientY-theCanvas.offsetTop;
       ConsoleLog.log("click " + mouseX + "," + mouseY);
       //find the button clicked

       var col = Math.floor(mouseX/92);
       var row = Math.floor(mouseY/57);

       console.log("row",row,"col", col);
       tempButton = buttons[row][col];
       clickSound.play();
       tempButton.pressDown();
       tempButton.draw(context);

    }

    function onMouseMove(e) {
       mouseX = e.clientX-theCanvas.offsetLeft;
       mouseY = e.clientY-theCanvas.offsetTop;

       //ConsoleLog.log("move: " + mouseX + "," + mouseY);
    }

       //**** start application
   var gr = context.createLinearGradient(0, 0, 85, 50);

    // Add the color stops.
    gr.addColorStop(0,'#ffffff');
         gr.addColorStop(.5,'#bbbbbb');
    gr.addColorStop(1,'#777777');

    theCanvas.addEventListener("mousemove", onMouseMove, false);
    theCanvas.addEventListener("click", onMouseClick, false);

    initSounds();
    initButtons();
    initLists();
    chooseButtonsForCard();
    drawScreen();

 }

 </script>
 </head>
 <body>
 <div style="position: absolute; top: 0px; left: 0px;">
 <canvas id="canvas" width="570" height="418">
  Your browser does not support HTML5 Canvas.
 </canvas>
 <audio id ="clicksound"  preload="auto">
    <source src="click.mp3" type="audio/mpeg" />

 Your browser does not support the audio element.
 </audio>
 </div>
 </body>
 </html>

Name this file BSBingo.html and save it in a folder. If you are going to follow along and create the example project, you will also want to create a folder to hold the project files.

Examining the Code for BSBingo.html

Note

When designing an application for the iPhone using PhoneGap, we are actually targeting the Safari Mobile browser. This means we can make concessions rather than having to target all available HTML5-compatible devices. You will notice this especially when we discuss <audio> tag usage.

The TextButton.js file

Our BS Bingo game will be played on a grid of 25 squares. We created a class (an object prototype, actually) called TextButton.js to help us create buttons with the text, as well as a “press” state we can use to show that the button has been clicked. You will want to save this file in the project folder along with the BSBingo.html file. Here is the code for this file:

function TextButton(x,y,text, width, height, backColor, strokeColor, 
 overColor, textColor){
    this.x = x;
    this.y = y;
    this.text = text;
    this.width = width;
    this.height = height;
    this.backColor = backColor;
    this.strokeColor = strokeColor;
    this.overColor = overColor;
    this.textColor = textColor;
    this.press = false;
}

TextButton.prototype.pressDown=function() {
    if (this.press==true){
        this.press = false;
    }else{
        this.press = true;
    }
}

TextButton.prototype.draw = function(context){

    context.save();
    context.setTransform(1,0,0,1,0,0); // reset to identity
    context.translate(this.x, this.y);

    context.shadowOffsetX = 3;
    context.shadowOffsetY = 3;
    context.shadowBlur = 3;
    context.shadowColor = "#222222";

    context.lineWidth = 4;
    context.lineJoin = 'round';
    context.strokeStyle = this.strokeColor;

    if (this.press==true){
        context.fillStyle = this.overColor;
    }else{
       context.fillStyle = this.backColor;
    }

    context.strokeRect(0, 0, this.width,this.height);
    context.fillRect(0, 0, this.width,this.height);

    //text
    context.shadowOffsetX = 1;
    context.shadowOffsetY = 1;
    context.shadowBlur = 1;
    context.shadowColor = "#ffffff";
    context.font = "14px serif"
    context.fillStyle = this.textColor;
    context.textAlign = "center";
    context.textBaseline = "middle";
    var metrics = context.measureText(this.text)
    var textWidth = metrics.width;
    var xPosition = this.width/2;
    var yPosition = (this.height/2);

    var splitText = this.text.split('\n');
    var verticalSpacing = 14;
    console.log("text=" + this.text)
    console.log("text split length=" + splitText.length)

    for (var ctr1=0; ctr1<splitText.length;ctr1++) {
        context.fillText  ( splitText[ctr1],  xPosition,
        yPosition+ (ctr1*verticalSpacing));
    }

    context.restore();
}

This object prototype contains functions for creating, drawing, and clicking a gray square button with black text on it. When clicked, the button will be drawn with a yellow background. We have covered all these drawing functions earlier in this book, so they will look familiar to you if you have read those chapters. If you have not, it’s especially a good idea to read Chapter 2, which covers drawing and shading objects drawn with paths.

Let’s now take a quick look at the functions we have created in BSBingo.html.

The initLists() function

The first game-related function you will encounter is initLists(). For our simple game implementation, we have created a single list of words based on some common business jargon. The standardJargonList application scope variable will contain a single-dimension array of words that will be placed randomly on the player’s bingo card. We can add more types of lists if we would like to target other types of jargon-speak, such as pure IT process-speak, marketing-speak, or even sports- or geek-speak.

The initButtons() function

This function creates a grid of 25 TextButton instances, 85 pixels in width and 25 in height. These are stored in the application scope buttons two-dimensional array so they can be accessed via the [row][column] syntax.

The initSounds() function

The initSounds() function needs to initialize only a single sound referenced in an HTML5 <audio> tag. Since we are targeting the iOS platform, we need to provide only a single .mp3-formatted sound. We do not need .ogg or .wav because we are not targeting any other browsers. Here is the HTML5 <audio> tag:

<audio id="clicksound"  preload="auto">
    <source src="click.mp3" type="audio/mpeg" />
 Your browser does not support the audio element.
 </audio>

The chooseButtonsForCard() function

This function creates a local array called tempArray and fills it with the contents of the standardJargonList. Next, it randomly chooses an element from the tempArray for each of the 25 row/column combinations on the bingo card. As it selects a word, it splices it from the tempArray so it cannot be selected again, leaving the card with no duplicates.

The drawScreen() function

This function loops through the buttons two-dimensional array and draws the initial 25 buttons with text onto the canvas.

The onMouseClick() function

When the user clicks the mouse on the game screen, this event listener function determines which of the 25 squares was clicked. It calls the appropriate TextButton instance’s pressDown() function and then its draw() function, passing in the context.

The onMouseMove() function

When the mouse is moved, this event listener function will set the mouseX and mouseY values to the current mouse position on the canvas.

The Application Code

Once all the functions and the TextButton object prototype are created, the actual application code is very simple. Because this is a completely event-based application, we don’t need a main loop. We also have not put in any other states or buttons, such as a title screen or a reset button. This makes the app less user-friendly, but it is fine for this simple example. It also makes the application code very simple:

 //**** start application
    var gr = context.createLinearGradient(0, 0, 100, 100);

    // Add the color stops.
    gr.addColorStop(0,'#ffffff');
    gr.addColorStop(.5,'#bbbbbb');
    gr.addColorStop(1,'#777777');

    theCanvas.addEventListener("mousemove", onMouseMove, false);
    theCanvas.addEventListener("click", onMouseClick, false);
    initSounds();
    initButtons();
    initLists();
    chooseButtonsForCard();
    drawScreen();

First, we create a shared linear gradient that can be used by all the TextButton instances. Next, we add the mouse event listeners for click and move. Finally, we run through our functions to set up the card, and then we simply wait for the user to click a button. That’s all there is to it. We haven’t even added a way to announce that the player has won. Extending this into a full-fledged application would be very simple, so we leave this task up to the reader if you have the desire to do so.

Figure 10-1 shows the screen for the finished application.

BS Bingo in Safari Desktop Edition

Figure 10-1. BS Bingo in Safari Desktop Edition

Next, we will look at how to use PhoneGap to turn this simple game into a native iOS application.

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