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

Video and the Canvas

The HTML video object already has a poster property for displaying an image before the video starts to play, as well as functions to autoplay and loop. So why is it necessary to preload the video? Well, as we alluded to in the previous section, simply playing a video is one thing—manipulating it with HTML5 Canvas is quite another. If you want to start manipulating video while it is displayed on the canvas, you first need to make sure it is loaded.

In this section, we will load video and then manipulate it in various ways so you can see how powerful Canvas can be when it is mixed with other HTML5 elements.

Displaying a Video on HTML5 Canvas

First, we must learn the basics of displaying video on HTML5 Canvas. There are a few important things to note that are not immediately obvious when you start working with video and the canvas. We worked through them so you don’t have to do it yourself (you’re welcome).

Video must still be embedded in HTML

Even though the video is only displayed on HTML5 Canvas, you still need a <video> tag in HTML. The key is to put the video in a <div> (or a similar construct), and set the display CSS style property of that <div> to none in HTML. This will ensure that while the video is loaded in the page, it is not displayed. If we wrote the code in HTML, it might look like this:

<div style="position: absolute; top: 50px; left: 600px; display:none">
<video loop controls id="thevideo" width="320" height="240" preload="auto">
 <source src="muirbeach.mp4" type='video/mp4; codecs="avc1.42E01E, mp4a.40.2"' >
 <source src="muirbeach.webm"type='video/webm; codecs="vp8, vorbis"' >
 <source src="muirbeach.ogg" type='video/ogg; codecs="theora, vorbis"'>
</video>

However, we already know that we don’t want to use an HTML embed. As we stated at the end of the last section, video events do not appear to fire reliably when video elements are embedded in the HTML page. For this reason, we need a new strategy to load video dynamically—we’ll create the <div> and <video> elements in JavaScript.

The first thing we do in our JavaScript is add a couple variables to hold references to the dynamic HTML elements we will create. The videoElement variable will hold the dynamically created <video> tag, while videoDiv will hold the dynamically created <div>:

var videoElement;
var videoDiv;

Note

We use this method to create global variables throughout this chapter. There are many reasons not to use global variables, but for these simple applications, it’s the quickest way to get something on the canvas. If you want to learn a better way to handle loading assets, the last section of Chapter 7 employs a strategy to preload assets without the use of global variables.

Next, we create our dynamic form elements in the eventWindowLoaded() function. First, we use the createElement() method of the document DOM object to create a <video> element and a <div> element, placing references to them in the variables we just created:

function eventWindowLoaded() {

   videoElement = document.createElement("video");
   videoDiv = document.createElement('div');
   document.body.appendChild(videoDiv);

Next, we add videoElement as a child of videoDiv, essentially putting it inside of that <div> on the HTML page. We then set the style attribute of <div> to display:none;, which will make it invisible on the HTML page. We do this because although we want the video to display on the canvas, we don’t want to show it on the HTML page:

   videoDiv.appendChild(videoElement);
   videoDiv.setAttribute("style", "display:none;");

We then create another new variable named videoType that holds the results of a new function we will create, supportedVideoFormat(). This function returns the file extension of the supported video format for the browser; otherwise, it returns “” (an empty string), which means we alert the user that there is no video support in the app for his browser:

   var videoType = supportedVideoFormat(videoElement);
   if (videoType == "") {
      alert("no video support");
      return;
   }

Finally, we set the src property of the video element using the file extension we just received from supportedVideoFormat(), and create the event handler for the canplaythrough event:

   videoElement.setAttribute("src", "muirbeach." + videoType);
   videoElement.addEventListener("canplaythrough",videoLoaded,false);

}

When the video has finished loading, the videoLoaded event handler is called, which in turn calls the canvasApp() function:

function videoLoaded(event) {

   canvasApp();

}

Before the code in the last section will work, we need to define the supportedVideoFormat() function. The reason for this function is simple: since we are adding video objects dynamically to the HTML page, we do not have a way to define multiple <source> tags. Instead, we are going to use the canPlayType() method of the video object to tell us which type of audio file to load.

The canPlayType() method takes a single parameter, a MIME type. It returns a text string of maybe, probably, or nothing (an empty string).

“” (nothing)

This is returned if the browser knows the type cannot be rendered.

maybe

This is returned if the browser does not confidently know that the type can be displayed.

probably

This is returned if the browser knows the type can be displayed using an audio or video element.

We are going to use these values to determine which media type to load and play. For the sake of this exercise, we will assume that both maybe and probably equate to yes. If we encounter either result with any of our three MIME types (video/webm, video/mp4, video/ogg), we will return the extension associated with that MIME type so the sound file can be loaded.

In the function below, video represents the instance of HTMLVideoElement that we are going to test. The returnExtension variable represents that valid extension for the first MIME type found that has the value of maybe or probably returned from the call to canPlayType():

function supportedVideoFormat(video) {
   var returnExtension = "";
   if (video.canPlayType("video/webm") =="probably" || 
       video.canPlayType("video/webm") == "maybe") {
         returnExtension = "webm";
   } else if(video.canPlayType("video/mp4") == "probably" || 
       video.canPlayType("video/mp4") == "maybe") {
         returnExtension = "mp4";
   } else if(video.canPlayType("video/ogg") =="probably" || 
       video.canPlayType("video/ogg") == "maybe") {
         returnExtension = "ogg";
   }

   return returnExtension;

}

We do not check for a condition when no valid video format is found and the return value is “”. If that is the case, the code that has called this function might need to be written in a way to catch that condition and alter the program execution. We did that with the test of the return value and alert(), which we described previously.

Video is displayed like an image

When you write code to display a video on the canvas, you use the context.drawImage() function, as though you are displaying a static image. Don’t go looking for a drawVideo() function in the HTML5 Canvas spec because you won’t find it. The following code will display a video stored in a variable named videoElement, displayed at the x,y position of 85,30:

context.drawImage(videoElement , 85, 30);

However, when you draw a video for the first time, you will notice that it will not move—it stays on the first frame. At first you might think you have done something wrong, but you have not. You just need to add one more thing to make it work.

Set an interval to update the display

Just like when we discussed animation in the previous chapters, a video placed on HTML5 Canvas using drawImage() will not update itself. You need to call drawImage() in some sort of loop to continually update the image with new data from the playing video in the HTML page (hidden or not). To do this, we call the video’s play() method, and then use setInterval() to call the drawScreen() function every 33 milliseconds. This will give you about 30 frames per second (FPS). We put this code in our canvasApp() function, which is called after we know the video has loaded:

videoElement.play();
setInterval(drawScreen, 33);

In drawScreen(), we will call drawImage() to display the video, but since it will be called every 33 milliseconds, the video will be updated and play on the canvas:

function  drawScreen () {

   context.drawImage(videoElement , 85, 30);

   }

Example 6-6 gives the full code for displaying a video on the canvas and updating it using setInterval(). Figure 6-6 shows this code in the browser.

Example 6-6. Basic HTML5 loading video onto the canvas

<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>CH6EX6: Basic HTML5 Load Video Onto The Canvas</title>
<script src="modernizr-1.6.min.js"></script>
<script type="text/javascript">
window.addEventListener('load', eventWindowLoaded, false);
var videoElement;
var videoDiv;
function eventWindowLoaded() {

   videoElement = document.createElement("video");
   videoDiv = document.createElement('div');
   document.body.appendChild(videoDiv);
   videoDiv.appendChild(videoElement);
   videoDiv.setAttribute("style", "display:none;");
   var videoType = supportedVideoFormat(videoElement);
   if (videoType == "") {
      alert("no video support");
      return;
   }
   videoElement.setAttribute("src", "muirbeach." + videoType);
   videoElement.addEventListener("canplaythrough",videoLoaded,false);

}

function supportedVideoFormat(video) {
   var returnExtension = "";
   if (video.canPlayType("video/webm") =="probably" || 
       video.canPlayType("video/webm") == "maybe") {
         returnExtension = "webm";
   } else if(video.canPlayType("video/mp4") == "probably" || 
       video.canPlayType("video/mp4") == "maybe") {
         returnExtension = "mp4";
   } else if(video.canPlayType("video/ogg") =="probably" || 
       video.canPlayType("video/ogg") == "maybe") {
         returnExtension = "ogg";
   }

   return returnExtension;

}

function canvasSupport () {
     return Modernizr.canvas;
}

function videoLoaded(event) {

   canvasApp();

}

function canvasApp() {

   if (!canvasSupport()) {
          return;
        }

function  drawScreen () {

      //Background
      context.fillStyle = '#ffffaa';
      context.fillRect(0, 0, theCanvas.width, theCanvas.height);
      //Box
      context.strokeStyle = '#000000';
      context.strokeRect(5,  5, theCanvas.width−10, theCanvas.height−10);
      //video
      context.drawImage(videoElement , 85, 30);

   }

   var theCanvas = document.getElementById("canvasOne");
   var context = theCanvas.getContext("2d");
   videoElement.play();

   setInterval(drawScreen, 33);

}

</script>

</head>
<body>
<div style="position: absolute; top: 50px; left: 50px;">

<canvas id="canvasOne" width="500" height="300">
 Your browser does not support HTML5 Canvas.
</canvas>
</div>
</body>
</html>
Displaying a video on HTML5 Canvas

Figure 6-6. Displaying a video on HTML5 Canvas

HTML5 Video Properties

We have already talked about some properties of HTMLVideoElement (inherited from HTMLMediaElement), but now that we have a video loaded onto the canvas, it would be interesting to see them in action.

In this example, we are going to display seven properties of a playing video, taken from the HTMLVideoElement object: duration, currentTime, loop, autoplay, muted, controls, and volume. Of these, duration, loop, and autoplay will not update because they are set when the video is embedded. Also, since we call the play() function of the video after it is preloaded in JavaScript, autoplay may be set to false, but the video will play anyway. The other properties will update as the video is played.

To display these values on the canvas, we will draw them as text in the drawScreen() function called by setInterval(). Below is the drawScreen() that we have created to display these values:

function  drawScreen () {

      //Background
      context.fillStyle = '#ffffaa';
      context.fillRect(0, 0, theCanvas.width, theCanvas.height);
      //Box
      context.strokeStyle = '#000000';
      context.strokeRect(5,  5, theCanvas.width−10, theCanvas.height−10);
      //video
      context.drawImage(videoElement , 85, 30);
      // Text
      context.fillStyle = "#000000";
      context.fillText  ("Duration:" + videoElement.duration,  10 ,280);
      context.fillText  ("Current time:" + videoElement.currentTime,  260 ,280);
      context.fillText  ("Loop: " + videoElement.loop,  10 ,290);
      context.fillText  ("Autoplay: " + videoElement.autoplay,  100 ,290);
      context.fillText  ("Muted: " + videoElement.muted,  180 ,290);
      context.fillText  ("Controls: " + videoElement.controls,  260 ,290);
      context.fillText  ("Volume: " + videoElement.volume,  340 ,290);

   }

Figure 6-7 shows what the attributes look like when displayed on the canvas. Notice that we have placed the <video> embed next to the canvas, and we have not set the CSS display style to none. We did this to demonstrate the relationship between the video embedded in the HTML page and the one playing on the canvas. If you roll over the video in the HTML page, you can see the control panel. If you set the volume, you will notice that the volume attribute displayed on the canvas will change. If you pause the embedded video, the video on the canvas will stop playing, and the currentTime value will stop.

This demo should give you a very good idea of the relationship between the video on the canvas and the one embedded with the <video> tag. Even though they are displayed using completely different methods, they are in fact one and the same.

Video on the canvas with properties displayed and <video> embed

Figure 6-7. Video on the canvas with properties displayed and <video> embed

You can see Example 6-7 in action by executing CH6EX7.html from the code distribution.

Example 6-7. Video properties

<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>CH6EX7: Video Properties</title>
<script src="modernizr-1.6.min.js"></script>
<script type="text/javascript">
window.addEventListener('load', eventWindowLoaded, false);
var videoElement;
var videoDiv;
function eventWindowLoaded() {

   videoElement = document.createElement("video");
   var videoDiv = document.createElement('div');
   document.body.appendChild(videoDiv);
   videoDiv.appendChild(videoElement);
   videoDiv.setAttribute("style", "position: absolute; top: 50px; left: 600px; ");
   var videoType = supportedVideoFormat(videoElement);
   if (videoType == "") {
      alert("no video support");
      return;
   }
   videoElement.setAttribute("src", "muirbeach." + videoType);
   videoElement.addEventListener("canplaythrough",videoLoaded,false);

}


function supportedVideoFormat(video) {
   var returnExtension = "";
   if (video.canPlayType("video/webm") =="probably" || 
       video.canPlayType("video/webm") == "maybe") {
         returnExtension = "webm";
   } else if(video.canPlayType("video/mp4") == "probably" || 
       video.canPlayType("video/mp4") == "maybe") {
         returnExtension = "mp4";
   } else if(video.canPlayType("video/ogg") =="probably" || 
       video.canPlayType("video/ogg") == "maybe") {
         returnExtension = "ogg";
   }

   return returnExtension;

}


function canvasSupport () {
     return Modernizr.canvas;
}

function videoLoaded() {
   canvasApp();

}

function canvasApp() {

  if (!canvasSupport()) {
          return;
        }

  function  drawScreen () {

      //Background
      context.fillStyle = '#ffffaa';
      context.fillRect(0, 0, theCanvas.width, theCanvas.height);
      //Box
      context.strokeStyle = '#000000';
      context.strokeRect(5,  5, theCanvas.width−10, theCanvas.height−10);
      //video
      context.drawImage(videoElement , 85, 30);
      // Text
      context.fillStyle = "#000000";
      context.fillText  ("Duration:" + videoElement.duration,  10 ,280);
      context.fillText  ("Current time:" + videoElement.currentTime,  260 ,280);
      context.fillText  ("Loop: " + videoElement.loop,  10 ,290);
      context.fillText  ("Autoplay: " + videoElement.autoplay,  100 ,290);
      context.fillText  ("Muted: " + videoElement.muted,  180 ,290);
      context.fillText  ("Controls: " + videoElement.controls,  260 ,290);
      context.fillText  ("Volume: " + videoElement.volume,  340 ,290);

   }

   var theCanvas = document.getElementById("canvasOne");
   var context = theCanvas.getContext("2d");
   videoElement.play();

   setInterval(drawScreen, 33);

}

</script>

</head>
<body>
<div style="position: absolute; top: 50px; left: 50px;">

<canvas id="canvasOne" width="500" height="300">
 Your browser does not support HTML5 Canvas.
</canvas>
</div>
</body>
</html>

Note

You can see all the events and properties for the HTMLVideoElement at http://www.w3.org/2010/05/video/mediaevents.html.

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