P. 1
HTML Canvas Guide

HTML Canvas Guide

|Views: 494|Likes:
Published by Paul McKellar

More info:

Published by: Paul McKellar on Apr 26, 2012
Copyright:Attribution Non-commercial

Availability:

Read on Scribd mobile: iPhone, iPad and Android.
download as PDF, TXT or read online from Scribd
See more
See less

04/26/2012

pdf

text

original

Sections

  • About Canvas
  • At a Glance
  • You Can Add a Canvas Element in a Few Lines of Code
  • It’s Easy to Include JPEGs, GIFs, PNGs, and SVGs
  • You Can Also Render Text On Canvas
  • Canvas is Great for Infographics
  • Canvas Can Create Fast, Lightweight Animations
  • You Can Manipulate Pixels Directly for Image Processing
  • Make Games That Play on the Desktop and iOS-Based Devices
  • The Web Inspector Provides Built-In JavaScript Debugging
  • Export to Canvas is Possible from Illustrator or Flash
  • Prerequisites
  • See Also
  • Start by Adding a <canvas> Tag
  • Specify the Fallback Behavior
  • Create a Drawing Context
  • Setting Up the Canvas
  • Save and Restore the Context
  • Set the Stroke and Fill Styles
  • Drawing Lines and Shapes
  • Drawing Rectangles
  • Paths and Subpaths
  • Drawing Lines
  • Setting Caps and Joins
  • Drawing Bezier Curves
  • Drawing Arcs and Circles
  • Drawing Complex Shapes
  • Creating Masks
  • Gradients
  • Linear Gradients
  • Gradients and Patterns
  • Listing 3-1 Creating a linear gradient
  • Radial Gradients
  • Animating Gradients
  • Patterns
  • Image Sources
  • Using Predrawn Images
  • Preparing to Draw an Image
  • Drawing an Image Normally
  • Drawing an Image with a Given Height and Width
  • Drawing an Image with Region Mapping
  • Text Settings
  • Adding Text
  • Font Settings
  • Text Direction
  • Text Alignment
  • Text Baseline
  • Bounding Box Width
  • Drawing Text
  • Listing 5-1 Drawing “Hello, world.”
  • Animating Text
  • Shadow Basics
  • Adding Shadows
  • Transparency and Blur
  • Figure 6-2 Shadow hardness
  • Shadows and Animation
  • Translation, Rotation, and Scaling
  • Translation
  • Listing 7-1 Drawing a path at a new location
  • Figure 7-1 Translating a path
  • Scaling
  • Rotation
  • Listing 7-2 Rotating an image and text
  • Rotation Around the Center
  • Combining Asymmetric Scaling and Rotation
  • Setting the Transformation Matrix
  • Matrix Transforms
  • Example: Setting the Matrix for Reflection
  • Figure 8-3 Reflection with scale and gradient
  • Listing 8-2 Adding a scalar and gradient
  • The Identity Matrix
  • Transforming the Transformation Matrix
  • Global Alpha
  • Advanced Compositing
  • Compositing Modes
  • Scaling Your Data
  • Creating Charts and Graphs
  • Scaling Using the Transformation Matrix
  • Scaling Using a Function
  • Data Plots
  • Listing 10-3 Data plot template
  • Bar Graphs
  • Figure 10-2 Sample bar graph
  • Pie Charts
  • Listing 10-6 Pie chart generator
  • Interactive Data Visualization and Animation
  • Listing 10-7 Performing interactive frequency addition
  • The Animation Sequence
  • Animating the Canvas
  • Animation Timing
  • Figure 11-1 Animation profile
  • A Simple Animation
  • Listing 11-1 Creating a simple animation
  • Adding a Gradient
  • Intermittent Animation
  • Listing 11-2 Creating an intermittent animation
  • Panning Background
  • Simple Panning Background
  • Layered Panning Background
  • Listing 11-4 Adding a layered panning background
  • <title>Layered Panning Background</title>
  • Assigning a Border and Background
  • Listing 12-1 Adding a CSS background and border
  • Modifying the Canvas with CSS
  • A Pop-Up Canvas—Animating Position and Opacity
  • Free Range Canvas
  • Using Standard Inputs with Canvas
  • Adding Mouse and Touch Controls to Canvas
  • Using Standard Inputs on Canvas
  • Responding to Mouse and Touch Events on Canvas
  • Tracking a Single Touch
  • Tracking Multiple Touches and Testing for Hits
  • Listing 13-4 Track multiple touches
  • Creating Custom Canvas Controls
  • Adding a Custom Button
  • Figure 13-5 Clicking a custom button
  • Listing 13-5 Creating a custom button on canvas
  • Adding a Slider
  • Listing 13-6 Adding a slider using CSS
  • Adding Sound to Canvas Animations
  • Adding a Soundtrack
  • Looping Audio
  • Listing 14-2 Adding looping audio
  • Adding a Voice-Over
  • Listing 14-3 Displaying slides with voice-overs
  • Adding Sound Effects
  • Listing 14-4 Creating animation with sound effects
  • Getting Pixel Data From the Canvas
  • Creating Buffer Objects
  • Pixel Manipulation
  • Modifying Pixel Data
  • Example: Reading and Modifying Pixel Data
  • Figure 15-1 Green clear and restore
  • Collision Detection
  • Space Arcade Game
  • Creating Games
  • Loony Lander Game
  • Putting Video on Canvas
  • Canvas Over Video
  • Listing 17-1 Displaying video behind the canvas
  • Tiled Video
  • Listing 17-2 Breaking video into tiles
  • Image Processing
  • Document Revision History

Safari HTML5 Canvas Guide

Contents

About Canvas 10
At a Glance 11 You Can Add a Canvas Element in a Few Lines of Code 11 There Are Methods for Drawing Rectangles, Lines, Curves, Arcs, and Complex Shapes 11 It’s Easy to Include JPEGs, GIFs, PNGs, and SVGs 12 You Can Also Render Text On Canvas 12 Canvas is Great for Infographics 13 Canvas Can Create Fast, Lightweight Animations 13 You Can Manipulate Pixels Directly for Image Processing 13 Make Games That Play on the Desktop and iOS-Based Devices 13 The Web Inspector Provides Built-In JavaScript Debugging 14 Export to Canvas is Possible from Illustrator or Flash 14 Prerequisites 14 See Also 14

Setting Up the Canvas 16
Start by Adding a <canvas> Tag 16 Specify the Fallback Behavior 16 Create a Drawing Context 16 Save and Restore the Context 17

Drawing Lines and Shapes 19
Set the Stroke and Fill Styles 19 Drawing Rectangles 20 Paths and Subpaths 20 Drawing Lines 21 Setting Caps and Joins 22 Drawing Bezier Curves 23 Drawing Arcs and Circles 23 Drawing Complex Shapes 24 Creating Masks 27

Gradients and Patterns 29
Gradients 29

2011-03-14 | © 2011 Apple Inc. All Rights Reserved.

2

Contents

Linear Gradients 29 Radial Gradients 33 Animating Gradients 35 Patterns 37

Using Predrawn Images 41
Image Sources 41 Preparing to Draw an Image 42 Drawing an Image Normally 43 Drawing an Image with a Given Height and Width 43 Drawing an Image with Region Mapping 44

Adding Text 48
Text Settings 48 Font Settings 49 Text Direction 49 Text Alignment 49 Text Baseline 50 Bounding Box Width 51 Drawing Text 52 Animating Text 53

Adding Shadows 57
Shadow Basics 57 Transparency and Blur 58 Shadows and Animation 58

Translation, Rotation, and Scaling 60
Translation 61 Scaling 62 Rotation 63 Rotation Around the Center 65 Combining Asymmetric Scaling and Rotation 67

Matrix Transforms 69
Setting the Transformation Matrix 69 Example: Setting the Matrix for Reflection 70 The Identity Matrix 72 Transforming the Transformation Matrix 72

2011-03-14 | © 2011 Apple Inc. All Rights Reserved.

3

Contents

Advanced Compositing 73
Global Alpha 73 Compositing Modes 74

Creating Charts and Graphs 76
Scaling Your Data 76 Scaling Using the Transformation Matrix 77 Scaling Using a Function 78 Data Plots 79 Bar Graphs 82 Pie Charts 86 Interactive Data Visualization and Animation 91

Animating the Canvas 99
The Animation Sequence 99 Animation Timing 100 A Simple Animation 102 Adding a Gradient 106 Intermittent Animation 107 Panning Background 110 Simple Panning Background 111 Layered Panning Background 113

Modifying the Canvas with CSS 116
Assigning a Border and Background 116 A Pop-Up Canvas—Animating Position and Opacity 117 Free Range Canvas 121

Adding Mouse and Touch Controls to Canvas 126
Using Standard Inputs with Canvas 126 Using Standard Inputs on Canvas 129 Responding to Mouse and Touch Events on Canvas 132 Tracking a Single Touch 132 Tracking Multiple Touches and Testing for Hits 135 Creating Custom Canvas Controls 139 Adding a Custom Button 139 Adding a Slider 143

Adding Sound to Canvas Animations 149
Adding a Soundtrack 150

2011-03-14 | © 2011 Apple Inc. All Rights Reserved.

4

All Rights Reserved.Contents Looping Audio 153 Adding a Voice-Over 158 Adding Sound Effects 161 Pixel Manipulation 166 Getting Pixel Data From the Canvas 166 Creating Buffer Objects 166 Modifying Pixel Data 167 Example: Reading and Modifying Pixel Data 168 Creating Games 171 Collision Detection 171 Space Arcade Game 171 Loony Lander Game 177 Putting Video on Canvas 188 Canvas Over Video 189 Tiled Video 192 Image Processing 197 Document Revision History 203 2011-03-14 | © 2011 Apple Inc. 5 .

” 52 Animating text 54 2011-03-14 | © 2011 Apple Inc.Figures and Listings Drawing Lines and Shapes 19 Figure 2-1 Figure 2-2 Figure 2-3 Listing 2-1 Listing 2-2 Default cap and join 22 The winding rule in action 25 Clipping mask 27 Exercising the winding rule 26 Making a mask 27 Gradients and Patterns 29 Figure 3-1 Figure 3-2 Figure 3-3 Figure 3-4 Figure 3-5 Figure 3-6 Figure 3-7 Listing 3-1 Listing 3-2 Listing 3-3 Listing 3-4 Linear gradient 30 Linear gradients 31 Rainbow gradient 32 Radial gradient 33 Ball with gradient overlay 34 Animated gradient 35 Patterns 38 Creating a linear gradient 30 Creating a radial gradient 33 Animating a gradient 35 Creating patterns 38 Using Predrawn Images 41 Figure 4-1 Figure 4-2 Listing 4-1 Listing 4-2 Region mapping 44 Exploding soccer ball 45 Drawing an image 42 Exploding an image 45 Adding Text 48 Figure 5-1 Figure 5-2 Figure 5-3 Figure 5-4 Listing 5-1 Listing 5-2 Text alignment 50 Text baselines 51 Hello world 52 Animated text 54 Drawing “Hello. world. All Rights Reserved. 6 .

Rotation. 7 .Figures and Listings Adding Shadows 57 Figure 6-1 Figure 6-2 Figure 6-3 Shadows 57 Shadow hardness 58 Shadow rotation 59 Translation. and Scaling 60 Figure 7-1 Figure 7-2 Listing 7-1 Listing 7-2 Listing 7-3 Listing 7-4 Translating a path 62 Nonintuitive interaction 68 Drawing a path at a new location 61 Rotating an image and text 64 Defining a shape literally 66 Defining a shape relative to its center point 66 Matrix Transforms 69 Figure 8-1 Figure 8-2 Figure 8-3 Figure 8-4 Listing 8-1 Listing 8-2 Matrix parameters and positions 69 Reflected text 70 Reflection with scale and gradient 71 The identity matrix 72 Reflecting text 70 Adding a scalar and gradient 71 Creating Charts and Graphs 76 Figure 10-1 Figure 10-2 Figure 10-3 Figure 10-4 Listing 10-1 Listing 10-2 Listing 10-3 Listing 10-4 Listing 10-5 Listing 10-6 Listing 10-7 Sample data plot 79 Sample bar graph 83 Sample pie chart 87 Frequency addition 91 Scaling by matrix 77 Creating a scaling function 78 Data plot template 80 Bar graph template 83 Drawing a pie chart 86 Pie chart generator 87 Performing interactive frequency addition 91 Animating the Canvas 99 Figure 11-1 Figure 11-2 Figure 11-3 Figure 11-4 Figure 11-5 Animation profile 101 Animated soccer ball 102 Animation with gradient fill 106 Flapping butterfly 107 Panning background 111 2011-03-14 | © 2011 Apple Inc. All Rights Reserved.

All Rights Reserved.Figures and Listings Figure 11-6 Listing 11-1 Listing 11-2 Listing 11-3 Listing 11-4 Layered background 113 Creating a simple animation 103 Creating an intermittent animation 108 Adding a panning background 111 Adding a layered panning background 113 Modifying the Canvas with CSS 116 Figure 12-1 Figure 12-2 Figure 12-3 Listing 12-1 Listing 12-2 Listing 12-3 CSS bulletin board 116 Animating a pop-up canvas 118 Butterfly 122 Adding a CSS background and border 116 Making the canvas a pop-up 118 Making a free-roaming canvas 122 Adding Mouse and Touch Controls to Canvas 126 Figure 13-1 Figure 13-2 Figure 13-3 Figure 13-4 Figure 13-5 Figure 13-6 Listing 13-1 Listing 13-2 Listing 13-3 Listing 13-4 Listing 13-5 Listing 13-6 Big buttons 127 Controls on canvas 129 Tracking events on canvas 132 Bubbles 136 Clicking a custom button 140 Slider controlling image size 144 Make big buttons 127 Putting controls on canvas 130 Track mouse and touch events 133 Track multiple touches 136 Creating a custom button on canvas 140 Adding a slider using CSS 144 Adding Sound to Canvas Animations 149 Figure 14-1 Figure 14-2 Figure 14-3 Figure 14-4 Listing 14-1 Listing 14-2 Listing 14-3 Listing 14-4 Soundtrack 150 Looping audio 154 Voice-over 158 Animation with sound effects 162 Adding a simple soundtrack 150 Adding looping audio 154 Displaying slides with voice-overs 159 Creating animation with sound effects 162 Pixel Manipulation 166 Figure 15-1 Green clear and restore 168 2011-03-14 | © 2011 Apple Inc. 8 .

9 . All Rights Reserved.Figures and Listings Listing 15-1 Manipulating pixels 168 Creating Games 171 Figure 16-1 Figure 16-2 Listing 16-1 Listing 16-2 Space arcade game 172 Lander game 177 Space arcade game 172 Lander game 177 Putting Video on Canvas 188 Figure 17-1 Figure 17-2 Figure 17-3 Listing 17-1 Listing 17-2 Listing 17-3 Video under canvas 189 Rotating video tiles 192 Image processing 197 Displaying video behind the canvas 189 Breaking video into tiles 193 Realtime image processing 197 2011-03-14 | © 2011 Apple Inc.

Because the graphics are created at runtime on the user’s system.About Canvas If your website includes infographics. Since no plug-in is needed. image processing. they’re always up-to-date and generate no server load or round-trip delay. The canvas element offers a quick and elegant solution for creating interactive animations and 2D games. There are also JavaScript methods for manipulating canvas pixels directly. you can modify them quickly using a text editor or text output from a script. All Rights Reserved. The canvas specification provides for simple fallback. including animations. animation. 10 . curved paths. games and animations written using canvas run on iOS-based devices as well as on desktop computers. and JavaScript. HTML5 adds a set of powerful graphics methods to the JavaScript API. all without using a plug-in. or games. and shapes. games. and even video. Canvas is supported on the desktop (Mac OS X and Windows) in Safari 2. as well as for rendering images such as JPEGs. and video.0 and later and in all versions of Safari on iOS. lines. CSS. you should learn about the HTML5 canvas element. so you can start using canvas today while keeping your website compatible with older browsers. Because canvas animations are written in HTML. interactive graphics. There are JavaScript methods for drawing text. PNGs. SVGs. 2011-03-14 | © 2011 Apple Inc. supporting real-time image processing. as well as in most other current browsers. You interact with canvas using JavaScript. an immediate drawing surface in your webpage where you can create runtime-generated graphics. GIFs.

Arcs. include a line that defines the canvas element. The fillRect(x.y. width. It’s a good idea to assign it an ID as well. That’s all the setup required. All Rights Reserved. Now you’re ready to start drawing. You can download a . Put any fallback behavior for older browsers between the opening and closing <canvas> tags. var can = document.About Canvas At a Glance Important This document includes over 40 complete HTML examples. hide it offscreen. Lines. At a Glance Here. round the corners. move it around on the screen. specify the x and y coordinates and the height and width of the rectangle. 2011-03-14 | © 2011 Apple Inc.zip file. The strokeRect(x. Curves. in a nutshell. Be sure to include a closing tag. width. giving it a height and width. var ctx = can. Because the canvas element is HTML. You Can Add a Canvas Element in a Few Lines of Code In your HTML. Click the Companion File link at the top of the page in the HTML version of this document to download the .height) method draws a filled rectangle.getContext("2d"). you can use CSS to modify it—give it a border or a background. and Complex Shapes To draw a rectangle.height) method draws the outline of a rectangle. get the canvas element into an object and get a "2d" drawing context. 11 .zip file with all of the HTML examples. Relevant Chapters “Setting Up the Canvas” (page 16) and “Modifying the Canvas with CSS” (page 116).getElementById("can"). including any required media assets. along with the methods and properties you use to implement them. <canvas id="can" height="300" width="400"> <img src="fallback.jpg"> </canvas> Using JavaScript. are the main features of canvas.y. There Are Methods for Drawing Rectangles. and so on. Follow the links at the bottom of each subsection for details and examples on particular features.

“Adding Shadows” (page 57). video. Canvas supports matrix transforms—anything you draw can be translated. by describing the geometric shapes that make it up. creating a closed shape. Relevant Chapters “Using Predrawn Images” (page 41) and “Adding Shadows” (page 57). All Rights Reserved. or ctx.lineWidth=2. you can include predrawn images on the canvas using an image source.128.y) method. Set the starting point. Include the image source in your HTML and get it into a JavaScript object using a method such as getElementById. rotated. For example.y) method. GIFs. The stroke or fill style specifies the color the element is drawn in. 12 .fillStyle="rgba(128. or make any shape into a mask by designating it as the clipping region for drawing operations.5)".128. such as an img. and more.y) or strokeText("text". scaled. for example. “Translation. by calling the moveTo(x. The closePath() method draws a line from the current endpoint to the starting point of the path. such as the font family. Begin a path using beginPath().0. Just enter a line of text and x and y coordinates for the text box using the fillText("text". and “Matrix Transforms” (page 69).About Canvas At a Glance You draw shapes other than rectangles by creating a path. 2011-03-14 | © 2011 Apple Inc. and the text alignment and baseline. You can add a shadow to any shape. Rotation. adding line segments. and SVGs The drawing operations given so far are perfect for drawing graphs and charts. and closing the path. You can also set the line width for strokes. curves. or arcs. It’s Easy to Include JPEGs. and weight. but you probably wouldn’t want to draw fine art. or even a smiley face. The path is not actually drawn until you call stroke() or fill(). and Scaling” (page 60). ctx. “Gradients and Patterns” (page 29).x. “Drawing Lines and Shapes” (page 19).strokeStyle="black". or start a discontinuous subpath. Fortunately. PNGs. You can specify a number of text settings. Colors are specified using the same naming conventions as CSS—ctx.x. or another canvas element. size. You Can Also Render Text On Canvas The canvas element supports basic text rendering on a line-by-line basis. Relevant Chapters “Drawing Rectangles” (page 20).

and scale of the elements in the model. detect collisions. width. ms) method for endless animations.height). iPhone.y. high scores. or setTimeout("function()". apply digital filters. For the smoothest animation. Relevant chapter “Pixel Manipulation” (page 166). such as iPad. To plot data. Set the animation to repeat using the setInterval("function()". 13 . Lightweight Animations Animation involves repeatedly clearing the canvas and drawing the visual elements. and do image processing in real time. Because canvas games work natively in the browser. rotation. Because your game logic is in JavaScript. just choose your colors and select an appropriate scale to fit your data. Make Games That Play on the Desktop and iOS-Based Devices You can add sound to canvas-based media using HTML5 audio. from backmost to frontmost. Bar charts are also straightforward—pick your colors. ms) for animation that repeats conditionally. and make calls to fillRect(x. create bar graphs. pie charts and infographics using canvas. and so on.About Canvas At a Glance Relevant chapter “Adding Text” (page 48) Canvas is Great for Infographics It’s easy to plot data. Relevant chapter “Creating Charts and Graphs” (page 76) Canvas Can Create Fast. updating position. You can also add controls that respond to mouse or touch input. You can use this information to analyze the canvas’s content. Pie charts are only slightly more complicated. which simplifies tracking multiple players. it has ready access to client-side data storage. 2011-03-14 | © 2011 Apple Inc. choose an appropriate scale. and iPod touch. break your animation into a model and a view. web-based games based on canvas work equally well on the desktop and on mobile devices. Relevant chapter “Animating the Canvas” (page 99) You Can Manipulate Pixels Directly for Image Processing You have direct access to the canvas bitmap as an array of RGBa pixels. You can detect collisions using isPointInPath(x. then drawing everything at the precalculated values. dungeon maps.y). All Rights Reserved.

TicTacToe with HTML5 Offline Storage —Example of a game that uses HTML5 client-side storage. To enable the developer tools. All Rights Reserved. These tools are invaluable for creating interactive canvas displays and games. HTML. Current versions of Flash support export to HTML for complete Flash documents. Prerequisites You should already be familiar with HTML and JavaScript. Safari CSS Visual Effects Guide —Guide to using CSS for animated transformations and effects. ActionScript and all. For more information. “Adding Sound to Canvas Animations” (page 149). CanvasRenderingContext2D Class Reference —Summary of the methods and properties used for drawing on canvas. load your webpage and choose Show Web Inspector from the Develop menu. Safari CSS Reference —Supported CSS properties in Safari. which is ideal for creating resolution-independent images for canvas. “Adding Mouse and Touch Controls to Canvas” (page 126). Familiarity with CSS is helpful. The Web Inspector Provides Built-In JavaScript Debugging Safari has built-in tools for debugging HTML and JavaScript. Safari HTML5 Audio and Video Guide —Guide to use of audio and video elements in Safari. using a provided JavaScript library. 14 . Illustrator can export vector graphics in SVG format. see Safari Developer Tools Guide . and for optimizing JavaScript timing. you can continue to author in them while creating canvas content. click “Show develop menu in menu bar” in Safari preferences > Advanced. and “Creating Games” (page 171). as the two languages are quite similar. Porting from ActionScript to JavaScript is relatively straightforward. Export to Canvas is Possible from Illustrator or Flash If your expertise has been honed using authoring tools such as Flash or Illustrator. ● ● ● ● ● ● 2011-03-14 | © 2011 Apple Inc. See Also ● HTMLCanvasElement Class Reference —Summary of the methods and properties of the canvas element. and CSS using Safari’s built-in Web Inspector. You can draw images in Flash and export them in PNG or SVG format. Safari Developer Tools Guide —Complete guide to debugging JavaScript. To open the Web Inspector and begin debugging.About Canvas Prerequisites Relevant chapters “Animating the Canvas” (page 99).

2011-03-14 | © 2011 Apple Inc.About Canvas See Also ● iOS Human Interface Guidelines —Includes guidance for creating web-based apps for iOS-based devices. All Rights Reserved. 15 .

Browsers that don’t support the canvas element ignore the <canvas> tags and display the fallback content.jpg"> </canvas> HTML5-savvy browsers ignore everything between the opening and closing <canvas> tags. Currently. you need to add a <canvas> tag to your HTML and assign a 2D drawing context to it. <canvas height="300" width="400"> <img src="fallback. All Rights Reserved. in the future. Specify the Fallback Behavior Put any fallback behavior for older browsers between the opening and closing <canvas> tags. Start by Adding a <canvas> Tag In your HTML. the default width of 300 pixels and the default height of 150 pixels are used. giving it a width and height. 16 . get the canvas element into an object and get a "2d" drawing context. Be sure to include a closing tag. 2011-03-14 | © 2011 Apple Inc. <canvas width="400" height="300"> </canvas> If you don’t specify a width or height. The canvas specification is designed to support 3D drawing contexts. Create a Drawing Context Using JavaScript. only a "2d" context is supported. You perform all drawing operations on the context. such as WebGL.Setting Up the Canvas To set up a canvas for drawing. The canvas is initially empty and transparent. All your drawing operations are performed on the context. include a line that defines the canvas element.

rotation. among other things. and is nestable. 2011-03-14 | © 2011 Apple Inc. calls a drawing operation. 17 . You save the current context settings by calling the save() method on the context.getElementById("can"). When you save a context. line thickness. you need to change the context settings. then restores the context. Restore the previous settings by calling restore(). for example. where you store settings such as line color. var ctx. All Rights Reserved. When you restore a context. the settings are popped off the stack. function init() { can = document. To draw different elements in different colors or at different rotations. The following snippet saves the context. changes the rotation. and scaling. then restore it as needed. it’s easier to save the current context with all its settings. ctx = can.Setting Up the Canvas Save and Restore the Context <html> <head> <title>Simple Canvas</title> <script type="text/javascript"> var can. Saving and restoring contexts is therefore a very fast operation. the settings are pushed onto a stack. fill color. Instead of setting and resetting large numbers of drawing parameters.getContext("2d"). } </script> </head> <body onload="init()"> <canvas id="can" height="300" width="400"> </canvas> </body> </html> Save and Restore the Context The context is.

Setting Up the Canvas Save and Restore the Context

ctx.save(); ctx.rotate(3.14); drawSomething(); ctx.restore();

2011-03-14 | © 2011 Apple Inc. All Rights Reserved.

18

Drawing Lines and Shapes

The 2D drawing context has methods for drawing rectangles, lines, curves, arcs, and circles.

Lines, curves, and arcs can be connected at their endpoints to form paths. Paths can be closed to form complex shapes. Shapes can be stroked (drawn in outline) or filled. You can set the thickness of lines and strokes. Strokes and fills can be set to a color, pattern, or gradient.

Set the Stroke and Fill Styles
To actually draw a line or shape, you need to specify a fill style or a stroke style. The stroke or fill style specifies the color the element is drawn in. The following snippet specifies that drawing should be stroked in black or filled in gray at 50% opacity.
ctx.strokeStyle="black"; ctx.fillStyle="rgba(128,128,128,0.5)";

The thickness of the stroke is determined by the context’s lineWidth property. The default line width is 1 pixel. To set the line width to 2 pixels, for example, set ctx.lineWidth = 2. Colors can be specified in any of the usual CSS ways, such as "white", "rgb(255,255,255)", or "#ffffff". Colors can also be specified using RGBa, which specifies the 8-bit RGB values and an alpha value between 0 (transparent) and 1 (completely opaque), such as "rgba(255,255,255,1.0)", for example. A gradient or pattern can be used instead of a color. For more information, see “Gradients and Patterns” (page 29).

2011-03-14 | © 2011 Apple Inc. All Rights Reserved.

19

Drawing Lines and Shapes Drawing Rectangles

Drawing Rectangles
Once you’ve set a context and a stroke or fill style, you can begin drawing shapes on the canvas. To draw a rectangle, specify the x and y coordinates (with 0,0 in the upper-left corner of the canvas) and the height and width of the rectangle. There are three rectangle methods: Use clearRect() to clear a rectangle (erase that area to transparent black). Use strokeRect() to stroke a rectangle (draw the outline). Use fillRect() to fill a rectangle in the current color, gradient, or pattern. The following snippet draws a 50 x 50 blue rectangle at the upper-left corner of the canvas.
function drawBlueRect() { ctx.fillStyle="blue"; ctx.fillRect(0,0,50,50); }

Note If you’re unfamiliar with RGBa colors, the notion of “transparent black” may seem a little odd. It means that the RGB portion of the color is set to black, with the alpha channel set to transparent. When the alpha channel is set to zero (transparent), the RGB color value is immaterial, but it has to be set to something.

Paths and Subpaths
You draw shapes other than rectangles by creating a path, adding elements to it, then calling fill() or stroke(). You begin a path by calling beginPath(). Set the starting point by calling moveTo(x,y). Then add elements such as lines, bezier curves, arcs, and so on. Each element adds a new endpoint to the current path. A path ends when you call beginPath() again. You do not necessarily need to end a path—once you start a path, it becomes the current path, and can be stroked or filled at any time. A subpath is a continuous part of the path. Initially, the current path and the current subpath are the same. You can use moveTo(x,y) within a path to create a new, discontinuous subpath.

2011-03-14 | © 2011 Apple Inc. All Rights Reserved.

20

Drawing Lines and Shapes Drawing Lines

A subpath ends when you call moveTo(x,y) or closePath(). Note that closePath() does not necessarily close the current path, only the current subpath (though they may be the same). The closePath() method draws a line from the current endpoint to the starting point of the current subpath. Calling fill() or stroke() renders the current path, including all subpaths. You cannot create multiple paths and fill or stroke them all with a single call to fill() or stroke()—each path you want drawn must be followed by a call to stroke(), fill(), or both, before your next call to beginPath().

Drawing Lines
You draw lines by specifying the endpoint of a line segment. Add an endpoint by using the lineTo() method. The line is drawn from the current endpoint on the path to the new endpoint. You can string endpoints together in a connect-the-dots fashion. Alternatively, set a new starting point for a line segment, without drawing a line to it, by using the moveTo() method. The lines are not actually drawn until you call the stroke() or fill() method. If you call the fill() method, the line segments are treated as the outline of a shape, which is filled in the current fill style. You can adjust the line thickness by setting the context’s lineWidth property. For example, the following snippet sets the line width to 3 pixels.
ctx.lineWidth=3;

The following snippet draws a horizontal and a vertical line the height and width of the canvas, crossing in the middle of the canvas.
function init() { can = document.getElementsByTagName("canvas")[0]; ctx = can.getContext("2d"); } function drawLines() { ctx.strokeStyle="black"; ctx.beginPath(); ctx.moveTo(0,can.height / 2); ctx.lineTo(can.width,can.height / 2); ctx.moveTo(can.width / 2, 0);

2011-03-14 | © 2011 Apple Inc. All Rights Reserved.

21

● "bevel"—Default. "round"—Line ends with a round cap. ● ● Example: ctx. Line ends with no cap. All Rights Reserved. ctx.height). "square"—Line ends with a square cap half a line-width long. can. "miter"—Lines join with a smoothly mitered corner.lineJoin = "round" The cap and join styles are illustrated in Figure 2-1. Figure 2-1 Default cap and join 2011-03-14 | © 2011 Apple Inc. Lines join with a beveled corner. 22 .Drawing Lines and Shapes Setting Caps and Joins ctx. Set the join style by setting the context’s lineJoin property to one of the three values in the following list. ● ● Example: ctx.width / 2.lineTo(can. Set the end cap by setting the context’s lineCap property to one of the three values in the following list.lineCap = "round". } Setting Caps and Joins You can choose how lines are capped at the ends and how they are joined where they meet. ● "butt"—Default. "round"—Lines join with a rounded corner.stroke().

you use either the bezierCurveTo() method. which connects the endpoints with a cubic bezier curve using a specified pair of control points.beginPath(). function init() { can = document. ctrlY. var ctrlYa=300.moveTo(0. } Drawing Arcs and Circles An arc is a section of a circle. ctx. ctx. var wide = can. var ctrlXa=50. var high = can. ctx. 23 . } function drawCurves() { ctx. or the quadraticCurveTo() method.stroke(). ctx. ctx. var ctrlY=150. from the upper left corner of the canvas to the lower right corner of the canvas. ctx = can. draw an arc with a starting angle of 0 and ending angle of 2 x Pi. var ctrlX=25. ctx.getContext("2d").height. ctrlYa. wide. 2011-03-14 | © 2011 Apple Inc.0).Drawing Lines and Shapes Drawing Bezier Curves Drawing Bezier Curves You draw bezier curves in the same connect-the-dots manner as you draw lines. All Rights Reserved. one cubic and one quadratic. high). ctx.moveTo(0. wide. ctrlXa.0). high).bezierCurveTo(ctrlX.quadraticCurveTo(ctrlX.stroke().beginPath().strokeStyle="black".width. ctx.getElementById("can"). To draw a circle. which connects the endpoints with a quadratic bezier curve using a single specified control point. but instead of using the lineTo() method. ctrlY. The following snippet draws two bezier curves.

When filling a path. 2011-03-14 | © 2011 Apple Inc. with 0 corresponding to the 3:00 o’clock position on the right of the circle.radius. the shallower the arc.endAngle).0. Safari does not require this parameter.startAngle.height / 2. adding line segments.startAngle. The arcTo() method connects the current endpoint with the specified endpoint using an arc. } function drawCircle() { ctx.Math.getElementById("can"). each of these subpaths is filled independently.PI * 2). clockwise. You can either stroke or fill a path.width / 2. ctx = can.strokeStyle="black". the stroke() method draws only the specified path segments.radius).getContext("2d"). and optionally closing the path. To ensure cross-browser compatibility. curves.endAngle. If you create a path but don’t close it. The x and y parameters specify the center point of the circle the arc is a section of. function init() { can = document.y.arc(can. The radius parameter is the radius of the circle the arc is part of. The startAngle and endAngle parameters are in radians.can. ctx.radius. Note The arc() method draws an arc clockwise.y. false) Add an arc to the current endpoint of the path by calling arcTo(x. The following snippet draws a circle of radius 50 at the center of the canvas. The height of the arc is determined by the radius—the larger the radius. 24 . but some browsers do.Drawing Lines and Shapes Drawing Complex Shapes Draw a freestanding arc by calling arc(x.50. A path can contain discontinuous subpaths—lines or shapes that aren’t connected.stroke().y. but the fill() method fills a solid shape as if you had closed the path. add a false parameter at the end of the arc() method parameter list: arc(x. All Rights Reserved. or arcs. ctx. unless you supply the optional anticlockwise parameter as a sixth argument. } Drawing Complex Shapes To quickly review—you draw shapes other than rectangles by creating a path.

25 . 5. If the winding number is 0. If the winding number is nonzero. The example snippet in Listing 2-1 draws two filled paths. discontinuous polygons. the non-zero winding number rule is used to determine if a point is within the path and should be filled. some areas may not be filled. the area where the polygons overlap is not filled. perform the following test. For example. 2. Figure 2-2 The winding rule in action 2011-03-14 | © 2011 Apple Inc. the squares are both drawn counterclockwise. 4. if a path winds around a region once going clockwise and once going counterclockwise. the point is filled. For complex shapes. The result is illustrated in Figure 2-2. Each path consists of a pair of overlapping squares. If both polygons are composed entirely of line segments going clockwise. In one path. consider a path consisting of two overlapping. If the two polygons are composed of line segments going in opposite directions. For instance. but the area inside the smaller circle is left unfilled. both rectangles are filled completely. Add 1 to the winding number for every path segment the line passes through where the path is going clockwise. where a point is surrounded by more than one set of path edges. you can draw a doughnut by drawing an outer circle clockwise and an inner circle counterclockwise. in the other path. As another example. Initialize a winding number to 0. All Rights Reserved. even though you might expect them to be inside the shape. 1. the point is not filled. Subtract 1 from the winding number for every path segment the line passes through where the path is going counterclockwise. Draw a line from point P to the outside of the path (without passing through any vertexes). The area between the circles is filled. the region is not filled. one square is drawn clockwise and the other counterclockwise. The non-zero winding number rule To determine whether the fill() method fills a given point P.Drawing Lines and Shapes Drawing Complex Shapes You can create complex shapes that contain non-filled areas. 3. If a path overlaps itself going in different directions. or all going counterclockwise.

fill(). ctx.beginPath(). ctx.10). ctx.20).lineTo(120.10).moveTo(210.lineTo(100.lineTo(300.10). ctx. ctx. All Rights Reserved.120).20).20). ctx. ctx.20).120). ctx.Drawing Lines and Shapes Drawing Complex Shapes Listing 2-1 Exercising the winding rule function drawPath() { // two counterclockwise squares ctx.lineTo(320.20).lineTo(100. ctx.120). ctx.lineTo(210.lineTo(210.lineTo(220. ctx. ctx. } 2011-03-14 | © 2011 Apple Inc.moveTo(20.beginPath().lineTo(220.lineTo(10.10).20). // two squares.lineTo(120. one counterclockwise ctx. ctx.lineTo(20. one clockwise.100).100). ctx.moveTo(10.lineTo(10.10).100).lineTo(20. ctx. ctx.lineTo(320. ctx.lineTo(300.100). 26 . ctx. ctx. ctx.fill().moveTo(320.120). ctx. ctx.10).

can. and so is unaffected. All Rights Reserved. } function drawMask() { 2011-03-14 | © 2011 Apple Inc. The example then fills the canvas with a blue rectangle and draws some text. function init() { can = document. or images drawn subsequently are clipped if they fall outside the borders of the path.Drawing Lines and Shapes Creating Masks Creating Masks You can make any shape into a mask by calling the context’s clip() method after you define the path. The rectangle and text are clipped when they fall outside the boundaries of the triangle.strokeStyle = "black".getContext("2d").can. 27 . var ctx.height-2). The example in Listing 2-2 creates a triangle shape.strokeRect(1. Any shapes. Note that the black outline around the canvas is drawn prior to the mask. ctx.width-2. Figure 2-3 Clipping mask Listing 2-2 <html> <head> Making a mask <title>Triangular Mask</title> <script type="text/javascript"> var can. as shown in Figure 2-3. Calling clip() makes the current path the clipping region for the canvas. lines. drawMask().1.getElementById("can"). ctx. then calls clip() to make the shape a clipping mask. ctx = can.

Drawing Lines and Shapes Creating Masks // make a triangle with top at mid canvas ctx.lineTo(0.width / 2.height).fillRect(0.can.fillText("Triangular Clipping Mask".height).beginPath().can. 28 . ctx. ctx. ctx.width. ctx. 20.clip().lineTo(can. ctx. 180). // rectangle and text are clipped to the triangle ctx.fillStyle = "blue".fillStyle = "white".can.0).moveTo(can. ctx.width.closePath().height).can. All Rights Reserved. } </script> </head> <body onload="init()" > <h2>Clipping To A Path</h2> <canvas id="can" height="200" width="400"> </canvas> </body> </html> 2011-03-14 | © 2011 Apple Inc. ctx. // make the current path the clipping region ctx. ctx.font = "24pt Helvetica".0.

A color stop consists of a color and a position between 0 and 1. You create a linear gradient by calling createLinearGradient() and passing in two points. To let the gradient “show through. All Rights Reserved. Defining a linear gradient is like creating a virtual rectangle on the canvas. A single gradient can encompass more than one color change. or a gradient.” you need to set the fill style or stroke style to the gradient and draw something on top of the gradient.addColorStop(0. 'white'). the fill or stroke color is the nearest color stop. an ending color.y2).addColorStop(1. but it doesn’t display anything.y1. it’s drawn using the current stroke or fill style. The stroke or fill style can be set to a color. var grad = ctx. inclusive. 29 . The color changes from one color stop to the next. x2. The next step in defining a gradient is to add at least two color stops. Gradients A gradient specifies a starting color. 2011-03-14 | © 2011 Apple Inc. Linear Gradients A linear gradient defines color change along a line between two points. and an area over which color changes. 'black').createLinearGradient(x1. The 2D canvas drawing context supports two kinds of gradients: linear and radial. a pattern.Gradients and Patterns When you stroke or fill something on the canvas. where 0 is the beginning of the gradient and 1 is the end. grad. grad. Note that if the thing you are drawing extends beyond the endpoints of the gradient. at the defined intervals along the gradient.

ctx = can.getContext("2d"). ctx. } function drawGradient() { var grad = ctx.0).0. var ctx. The result is shown in Figure 3-1. 'black').createLinearGradient(10.100). 100.fillStyle=grad. drawGradient(). 80.addColorStop(0. grad. } </script> </head> <body onload="init()" style="background-color:#e0e0e0"> 2011-03-14 | © 2011 Apple Inc.getElementById("can"). The rectangle extends 25 pixels beyond the gradient’s endpoints. Figure 3-1 Linear gradient Listing 3-1 <html> <head> Creating a linear gradient <title>Simple gradient example</title> <script type="text/javascript"> var can.0. ctx. 30 .Gradients and Patterns Gradients The example in Listing 3-1 (page 30) defines a gradient that goes from black to white from the point 0. grad. 'white'). All Rights Reserved.fillRect(10.10.0 to the point 50. function init() { can = document.addColorStop(1. then fills a rectangle with the gradient.

Gradients and Patterns Gradients

<canvas id="can" height="200" width="400"> </canvas> </body> </html>

The gradient in the example above blends horizontally—the y-coordinate of the second point is the same as that of the first point. By creating a gradient with the second point above or below the first point, you can create a gradient that blends up or down. If the second point is above or below the first point, and to the left or right as well, you create a gradient that blends diagonally to the left or right. By specifying a color stop with an RGBa color value, you can create a gradient that includes degrees of transparency. The following snippet redefines drawGradients() to draw four rectangles, each with a gradient going in a different direction, all going from black to 50% transparent white. Since the webpage has a light grey background, it shows through where the gradient is partly transparent. The output of the snippet is shown in Figure 3-2.
Figure 3-2 Linear gradients

function drawGradients() { var grad = ctx.createLinearGradient(0,0, 50,0); grad.addColorStop(0, 'black'); grad.addColorStop(1, 'rgba(255,255,255, 0.5)'); ctx.fillStyle=grad; ctx.fillRect(0,0, 75,75); var g2 = ctx.createLinearGradient(0,0, 0,50); g2.addColorStop(0, 'black'); g2.addColorStop(1, 'rgba(255,255,255, 0.5)'); ctx.fillStyle=g2; ctx.fillRect(75,0, 75,75);

2011-03-14 | © 2011 Apple Inc. All Rights Reserved.

31

Gradients and Patterns Gradients

var g3 = ctx.createLinearGradient(150,0, 200,50); g3.addColorStop(0, 'black'); g3.addColorStop(1, 'rgba(255,255,255, 0.5)'); ctx.fillStyle=g3; ctx.fillRect(150,0, 75,75); var g4 = ctx.createLinearGradient(275,50, 225,0); g4.addColorStop(0, 'black'); g4.addColorStop(1, 'rgba(255,255,255, 0.5)'); ctx.fillStyle=g4; ctx.fillRect(225,0, 75,75); }

Of course, you can use colors other than black and white, and you can have more than one color stop. The following snippet redefines drawGradients() to create a rainbow gradient with seven color stops, each 1/6th of the distance along the gradient, as shown in Figure 3-3.
Figure 3-3 Rainbow gradient

function drawGradients() { var grad = ctx.createLinearGradient(10,0, 390,0); grad.addColorStop(0, 'red'); grad.addColorStop(1 / 6, 'orange'); grad.addColorStop(2 / 6, 'yellow'); grad.addColorStop(3 / 6, 'green') grad.addColorStop(4 / 6, 'aqua'); grad.addColorStop(5 / 6, 'blue'); grad.addColorStop(1, 'purple'); ctx.fillStyle=grad; ctx.fillRect(0,0, 400,75); }

2011-03-14 | © 2011 Apple Inc. All Rights Reserved.

32

Gradients and Patterns Gradients

Note that the rectangle extends a little beyond the ends of the gradient to display bands of pure red and purple. Important You can add color stops, but you cannot change a color stop, replace it, or delete it. If you need to change color stops, you must create a new gradient with the color stops you want.

Radial Gradients
A radial gradient defines a color change along a cone between two circles. You create a radial gradient by calling createRadialGradient() and passing in two circles, each defined by a center point and a radius. It’s common to use the same center point and different radii.
var grad = ctx.createRadialGradient(x,y,rad, x1,y1,rad1);

The next step is to add at least two color stops. A color stop consists of a color and a position between zero and one, inclusive, where zero is the beginning of the gradient and one is the end.
grad.addColorStop(0, 'black'); grad.addColorStop(1, 'white');

The color change goes from the first color stop to the last, at the defined intervals along the gradient. The example in Listing 3-2 defines a radial gradient then uses it to fill a circle. The result is shown in Figure 3-4.
Figure 3-4 Radial gradient

Listing 3-2
<html> <head>

Creating a radial gradient

<title>Radial Gradient</title> <script type="text/javascript">

var can; var ctx;

2011-03-14 | © 2011 Apple Inc. All Rights Reserved.

33

Gradients and Patterns Gradients

function init() { can = document.getElementById("can"); ctx = can.getContext("2d"); drawGradients(); }

function drawGradients() { var grad = ctx.createRadialGradient(50,50,5, 50,50,50); grad.addColorStop(0, 'white'); grad.addColorStop(1, 'black'); ctx.fillStyle=grad; ctx.arc(50,50, 50, 0, 2 * Math.PI); ctx.fill(); } </script>

</head> <body onload="init()" <body onload="init()" style="backgroundcolor:#e0e0e0"> <h2>Radial Gradient</h2> <canvas id="can" height="200" width="400"> </canvas> </body> </html>

A radial gradient is commonly used to create a 3D lighting effect. The following snippet draws a blue-filled circle, then superimposes a gradient that goes from white at 90% opacity to black at 60% opacity. The center of the gradient is offset 10 pixels upward, to simulate a light source from above. The result is shown in Figure 3-5 (page 34).
Figure 3-5 Ball with gradient overlay

2011-03-14 | © 2011 Apple Inc. All Rights Reserved.

34

9)'). 50.createRadialGradient(50. ctx.5.PI).50.50).beginPath.0. 2 * Math.fill().Gradients and Patterns Gradients function drawGradients() { ctx.PI). grad. All Rights Reserved. 50.0.0. ctx. ctx.addColorStop(0.arc(50.50.255. grad. illustrated in Figure 3-6. The example in Listing 3-3 creates an endlessly repeating animated sequence of gradients.arc(50.fill(). ctx. 35 . } Animating Gradients You can’t change the existing color stops in a gradient after it’s created. 2 * Math. ctx.255. 0.addColorStop(1.6)'). 0.fillStyle=grad. 'rgba(255.fillStyle="blue". ctx. or generate new gradients on the fly. ctx. var grad = ctx. Figure 3-6 Animated gradient Listing 3-3 <html> Animating a gradient 2011-03-14 | © 2011 Apple Inc.40.beginPath.40. 50.0. you need to either create multiple gradients in advance and replace one with another. 'rgba(0. To animate a gradient.

var g = parseInt(128 + 128 * scalar)." + g + ". function init() { can = document. var ctx. ctx = can. var delay = 50. 2011-03-14 | © 2011 Apple Inc. var angle = 0." + b + ")"). var b = parseInt(128 + 128 * scalar). 'blue').0).createLinearGradient(0. 36 .Gradients and Patterns Gradients <head> <title>Animated Gradient</title> <script type="text/javascript"> var can. All Rights Reserved.can. // start gradient at blue grad. // create gradient that goes from bottom to top of canvas var grad = ctx. // create rgb color value that goes from black to white // as the variable goes from -1 to 1 var r = parseInt(128 + 128 * scalar). var t = setInterval("animate()".addColorStop(0. "rgb(" + r + ". } // animate() is called every 50 msec function animate() { // create a variable that cycles from -1 to 1 angle = angle + 0.height.03. delay).addColorStop(1. 0.getContext("2d").getElementById("can"). // add color stop with new rgb color grad. var scalar = Math.sin(angle).

ctx. 37 . You create a pattern using the context’s createPattern() method. ctx. you first need to create an image source. GIF. naturally repetitive image of sand. A pattern consists of an image source—such as a JPG. All Rights Reserved.getElementById("dot"). var dot = document.0.height). 2011-03-14 | © 2011 Apple Inc. fill canvas with gradient ctx.clearRect(0.height). PNG. SVG.width.can. ctx. to create a pattern from tiles of the image. A pattern can be made from a small.width.save(). or wood grain. can. such as an img or video element. } </script> </head> <body onload="init()" style="background-color:#e0e0e0"> <h2>Animated Gradient</h2> <canvas id="can" height=250 width=500> </canvas> </html> Patterns Patterns let you use small images in place of much larger graphics. passing in the image object and a "repeat" parameter. ctx. and used like a 2D texture map to generate photo-realistic landscapes or interiors. can. can. then get the element into a JavaScript object.Gradients and Patterns Patterns // clear canvas. To create a pattern.fillStyle=grad. You can choose a pattern to use as a stroke or fill style.fillRect(0. or the currently playing frame of a video—and a repetition setting.restore(). for example.0. grass.

"repeat"). var ctx = can.png. but you can specify "repeat-x". The "repeat" string repeats the image both horizontally and vertically.getElementById("cork").png—and makes them into patterns that are each used to stroke a line and fill a rectangle. 2011-03-14 | © 2011 Apple Inc. The example in Listing 3-4 loads three images—dot. Figure 3-7 Patterns Listing 3-4 <html> <head> Creating patterns <title>Patterns</title> <script type="text/javascript"> function init() { var can = document. The result is shown in Figure 3-7. All Rights Reserved.getElementById("block").Gradients and Patterns Patterns var pat = ctx.createPattern(dot. or not at all."repeat").createPattern(block.createPattern(dot. var pat2 = ctx. and cork. // get images into objects var dot = document. 38 ."repeat").getContext("2d"). block. var block = document.png. or "repeat-none" to cause the pattern to be repeat horizontally only.getElementById("dot"). vertically only. var cork = document.getElementById("can"). "repeat-y". // create patterns from images var pat = ctx.

ctx.20.lineTo(250.fillRect(150.lineWidth=3.beginPath(). // stroke line and fill rect with block pattern ctx.lineTo(100.lineTo(400. ctx. ctx.20.150).moveTo(10.10). ctx. ctx. 100. All Rights Reserved.fillStyle=pat3.stroke().150).moveTo(300.20. 39 . ctx. ctx. ctx.moveTo(150. ctx.fillStyle=pat. ctx. // stroke line and fill rect with cork pattern ctx."repeat").strokeStyle=pat.10). ctx.stroke().150).10). ctx.fillRect(10. // stroke line and fill rect with dot pattern ctx.10). 100.10).fillStyle=pat2. ctx. 100. ctx.beginPath(). ctx.10).stroke(). ctx.strokeStyle=pat3. // make line thicker so pattern shows ctx.createPattern(cork.strokeStyle=pat2.Gradients and Patterns Patterns var pat3 = ctx. ctx. } </script> </head> 2011-03-14 | © 2011 Apple Inc.fillRect(300. ctx.beginPath().

40 .png"><br> Cork: <img id="cork" src="block.Gradients and Patterns Patterns <body onload="init()"> Dot: <img id="dot" src="dot.png"><br> <canvas id="can" height="200" width="300"> </canvas> </body> </html> 2011-03-14 | © 2011 Apple Inc. All Rights Reserved.png"><br> Block: <img id="block" src="block.

For a description of how to use video as an image source. Unless your source is an SVG. Image Sources You can use an img element. SVGs. You can specify a height and width at which to display the image. and are clipped if they extend beyond the canvas. get it into a JavaScript object using a method such as getElementById. You can also create an image source entirely in JavaScript. You can use another canvas as an image source. so they appear smooth and sharp at any size or rotation. All Rights Reserved. using such methods as new Image. it is recommended that it have a transparent background. and display the results in another canvas. JPEGs. If you use an animated GIF as an image source. or specify a region of the image to draw and a region of the canvas to draw it on. such images are drawn at their native size. so that it composites smoothly with other elements. the best results will come from using a source whose native size is the largest version displayed and scaling the image down when it needs to appear smaller.Using Predrawn Images You can add predrawn images to the canvas. You can load an image into a hidden canvas. but you cannot use a canvas as an image source inside itself. If your image is an HTML element. such as GIFs. manipulate the image at the pixel level. When you use a PNG as an image source. This chapter describes using images specified using the <img> tag. PNGs. the ideal image source is an SVG file. The main reason to use a canvas as an image source is as an offscreen buffer for image processing. Scaling images other than SVGs to a size greater than 1 can result in pixelation. By default. if the image is displayed at multiple sizes. Transparency is supported. SVGs are inherently resolution-independent. or another canvas element as an image source. If your animation uses a lot of scaling and rotation. 2011-03-14 | © 2011 Apple Inc. 41 . or the current frame of a video. a video element. only the first frame of the animation is shown. see “Putting Video on Canvas” (page 188). however. PNG image sources are preferred to GIFs or JPGs.

It’s a good idea to give the image an id. } </script> </head> <body onload="init()"> <canvas id="can" height="300" width="300"> </canvas> <img id="sprite" src="lem. sprite = document. ctx = can. Listing 4-1 shows how to add an image for use in your canvas. All Rights Reserved. If you don’t want the image displayed outside of the canvas. var ctx. var sprite. 42 . set the display property to none. Listing 4-1 <html> <head> <title>Drawing An Image</title> <script type="text/javascript"> Drawing an image var can. function init() { can = document.Using Predrawn Images Preparing to Draw an Image Preparing to Draw an Image Include an <img> tag in your HTML.getElementById("can").getElementById("sprite").png" style="display:none"> </body> </html> 2011-03-14 | © 2011 Apple Inc. Get the image into a JavaScript object so you can use it with the canvas. to make it easier to manipulate using JavaScript.getContext("2d").

getContext("2d").Using Predrawn Images Drawing an Image Normally Drawing an Image Normally To draw an image at its native height and width. ctx.y coordinate at which to draw the image.0).getElementById("sprite"). x.y) method. call the context’s drawImage(image. clipping any areas that fall outside the canvas. 2011-03-14 | © 2011 Apple Inc. 43 .y) The following snippet draws an image at 0.getContext("2d").getElementById("can"). ctx = can. All Rights Reserved. width.height) The image is scaled to the specified height and width.y. x.x. the image is clipped at the canvas boundary. drawSprite(). function init() { can = document. passing in the x.drawImage(image. ctx.0 with a width of 200 and a height of 100.0—by default. the upper-left corner of the canvas.drawImage(image.drawImage(sprite. function init() { can = document. The following snippet draws an image at 0. } function drawSprite() { ctx.getElementById("can"). } Drawing an Image with a Given Height and Width You can specify a height and width for an image by passing two additional parameters into drawImage(). If part of the image falls outside the canvas. ctx = can. sprite = document.0.

dHigh) Figure 4-1 Region mapping sy sx sw dx dw sh dy dh Destination canvas Source image The following snippet clips an image to a 100 x 100 area and draws it at 0. and height of the destination region in the canvas.0. scaled to fill the specified region of the canvas. ctx. } Drawing an Image with Region Mapping You can specify a region of the image and a region of the canvas as part of the drawImage() method. 2011-03-14 | © 2011 Apple Inc. } function drawSprite() { ctx.dy. All Rights Reserved.200.getElementById("sprite"). To draw an image with region mapping.drawImage(image. as illustrated in Figure 4-1.0. y. passing in the image. function init() { can = document.drawImage(sprite. 44 .Using Predrawn Images Drawing an Image with Region Mapping sprite = document.sHi. drawSprite().dWide. and the x. width. dx. and height of the source region within the image. ctx = can.100). sx.sWide. the x. width. call drawImage().0 scaled up by a factor of 2.getElementById("sprite"). Only the specified region of the image is displayed.sy. sprite = document.getElementById("can"). y.getContext("2d").

0. var ctx. } The example in Listing 4-2 uses region mapping to divide an image of a soccer ball into 25 tiles. All Rights Reserved.200).100.y 2011-03-14 | © 2011 Apple Inc. so the ball appears to be drawn normally.drawImage(sprite. } function drawSprite() { ctx. var ballHi = 150. 0.Using Predrawn Images Drawing an Image with Region Mapping drawSprite(). mapped further and further from each other each time. var ball. var x. var ballWide = 150. as illustrated in Figure 4-2.0. the tiles are redrawn several times. When the BOOM button is clicked. The canvas is rotated slightly between drawings so the tiles fly away from each other with a circular motion. var scalar. The tiles are initially mapped to the regions where they would be drawn as part of the whole image. 0. // offset so ball center is drawn at x.200.100. Figure 4-2 Exploding soccer ball Listing 4-2 <html> <head> Exploding an image <title>Exploding Ball</title> <script type="text/javascript"> var can. var y. 45 .

25. ctx. tWide = ballWide / grid.sqrt(tiles).height / 2.width / 2.translate(x. can = document. var tiles = 25. 9. y = can.rotate(scalar .y). scalar * (xOff + tileX).i<grid.tileX. var yOff = -1 * ballHi / 2.can. ctx. 2011-03-14 | © 2011 Apple Inc.getElementById("can"). } function drawTiles(){ ctx. // when scalar is 1. ctx. x = can.height). 16.scale = scalar.can. ctx.Using Predrawn Images Drawing an Image with Region Mapping var xOff = -1 * ballWide / 2. everything is drawn normally scalar = 1. ctx. var grid. drawTiles().j<grid. var tWide. etc. 46 .0. var tHi.getElementById("ball").getContext("2d").drawImage(ball. tHi = ballHi / grid.i++) { for (j=0.1). All Rights Reserved. function init() { ball = document.save().clearRect(0. ctx = can. var tileY = j * tHi. // tiles must be a perfect square--4. for (i=0. grid = Math.tileY.j++) { var tileX = i * tWide.tHi.tWide.width.

can. drawTiles().0. if (scalar < 3) setTimeout("boom()".tHi).width.Using Predrawn Images Drawing an Image with Region Mapping scalar * (yOff + tileY). } function boom() { scalar = scalar + 0.05.can.height). else ctx. All Rights Reserved. } } ctx."> </body> </html> 2011-03-14 | © 2011 Apple Inc.restore().tWide."> <input type="button" value="Reset" onclick="init().top:-50"> </canvas> </p> <input type="button" value="BOOM" onclick="boom().clearRect(0.50).png" style="display:none"> <canvas id="can" height="300" width="400" style="position:relative. } </script> </head> <body onload="init()"> <h2>Exploding Ball</h2> <p> <img id="ball" src="soccerball1. 47 .

You probably also want to specify text settings. ctx. All Rights Reserved. unless you want to use the browser’s default settings. You can specify a number of text settings. 2011-03-14 | © 2011 Apple Inc.fillStyle = "blue". 250 in 24-point Helvetica. Text Settings Before you add text to the canvas. 48 . For details.font="24pt Helvetica".y) method. or pattern. scaled. Rotation. or transformed like any other element of the canvas. For example. You need to create a separate text box for each line of text. x.y) or strokeText("text".Adding Text The canvas element supports basic text rendering on a line-by-line basis. and Scaling” (page 60) and “Matrix Transforms” (page 69). The text box extends automatically to hold the specified text on a single line. Text is not word-wrapped at the boundaries of the canvas. gradient. 250). size. ctx. function drawText() { ctx. and the text alignment and baseline. the following snippet prints “Figure 1” on the canvas at coordinates 10. such as the font family. x. Just enter a line of text and an x.y coordinate for the text box using the fillText("text". and weight. see “Translation. The text is rendered using the current stroke or fill style. which can be a color. 10.fillText("Figure 1". The default alignment and baseline are used. } Note Text can be rotated. you need to set either the fill or stroke style (see “Set the Stroke and Fill Styles” (page 19) for details).

font="24pt Helvetica bold. All Rights Reserved. right. and center. For example: ctx. You can change this by setting the canvas dir property to rtl. For example.Adding Text Text Settings Note The default canvas text settings for Safari are 10-pixel sans-serif. using a space-delimited or comma-delimited list. start is the same as left. Text Direction The default text direction is left-to-right. 49 . bold Helvetica. as appropriate. If the text direction is left-to-right. Set all desired font properties at once.getElementsByTagName('canvas')[0]. such as font-size and font-family. The canvas has access to any fonts loaded in your webpage using the CSS @font-face property. left. Font Settings You can specify font settings by setting the context’s font property. start is the same as right. You are not limited to the fonts the user has installed on the host device. Text Alignment Set the text alignment (sometimes called justification) by setting the context’s textAlign property. sans-serif" specifies a preference for 24-point. document. 2011-03-14 | © 2011 Apple Inc. You can specify any or all of the font settings. and will cause errors. once the fonts have loaded. end. Possible values for dir are rtl and ltr. The start and end values are the same as the left and right values. The font property is set using the same syntax as the all-in-one CSS font property.dir = 'rtl'. and can be inherited from parent elements such as the document body. with the alphabetic bottom of the text baseline at the y-coordinate. falling back to a generic sans-serif font if Helvetica is unavailable. left justified. space or comma delimited. and if the text direction is right-to-left. Note that text direction is a standard HTML property. would change the text direction of the first canvas element to right-to-left. are not supported. but are dependent on the text direction. Possible values are start. not specific to the canvas. Important Individual font settings.

Legal values are: ● top hanging middle alphabetic ideographic bottom ● ● ● ● ● 2011-03-14 | © 2011 Apple Inc.y).y). right. x. x. You can change the text baseline by setting the context’s textBaseline property. p.y) or fillText("text". shown by the red line. depending on the textAlign setting. or q drops below it). 50 . x. Figure 5-1 shows the different text alignment settings applied to text drawn at the same x-coordinate. The x-coordinate specifies the left. The default text baseline is the alphabetic baseline—the bottom of most letters in the roman alphabet (the descender of a lowercase y. Figure 5-1 Text alignment Text Baseline The text baseline is specified by the y-coordinate in fillText(text. or center of text box.Adding Text Text Settings The textAlign property changes the meaning of the x-coordinate specified in strokeText("text".y) or strokeText(text. All Rights Reserved. x.

prints the name.i++) { var name = playerName[i]. or you might need to position the text relative to a graphic element. To do any of these things.Adding Text Text Settings The position of each baseline value. 51 .textBaseline = "middle". var metric = measureText(name). The measureText() method takes a string as an argument and returns a metric. or scale the text. ctx.drawImage(playerIcon. and positions an image ten pixels to the right of the bounding box. ctx. } 2011-03-14 | © 2011 Apple Inc. var edge = metric. All Rights Reserved.i<players. 50 * i). Bounding Box Width Suppose that your canvas needs to include text that’s supplied at runtime. The following snippet iterates through an array of player names. You might want to break the text into multiple lines. measures the width of the bounding box needed to hold each name. relative to the bounding box. for (i=0. is shown in Figure 5-2. edge + 10. 50 * i).fillText(name. depending on its length. The width property of the metric is the width in pixels of the needed bounding box with the current font settings. 0. you need to know how large the bounding box for the text would be. Figure 5-2 Text baselines Top of bounding box Top of em square Hanging baseline Middle Alphabetic baseline Ideographic baseline Bottom of em square Bottom of bounding box The following snippet positions the text baseline so the text is vertically centered in the bounding box. ctx.width.

as set in the context’s textBaseline property.maxWidth]) method outlines the text in the current stroke style.getElementById("can"). The default is the left edge of the text box.Adding Text Drawing Text Drawing Text The context has two methods for drawing a line of text.maxWidth]) method fills the text in the current fill style.” in 24-point Helvetica. The strokeText(text. if needed. 2011-03-14 | © 2011 Apple Inc. world. world. to fit in a text box no wider than maxWidth. The x parameter is the alignment point for the text box. The default setting is alphabetic—in line with the bottom of roman letters without descenders. x. The result is shown in Figure 5-3. Both methods accept the same arguments. Listing 5-1 displays the text “Hello. and can contain Unicode characters. drawText(). filled in blue on a white background. The y parameter is the text baseline within the bounding box.y [. ctx = can. All Rights Reserved. The fillText(text. Both methods define a text box. centered horizontally and vertically. x. which scales the text.y [.” <title>Hello world</title> <script type="text/javascript"> var can. You can pass in an optional maxWidth value. as set in the context’s textAlign property. 52 . function init() { can = document. var ctx. The text parameter must be a string. Figure 5-3 Hello world Listing 5-1 <html> <head> Drawing “Hello.getContext("2d").

Draw the next frame. can. 2. 3.fillText("Hello.Adding Text Animating Text } function drawText() { ctx. Change the context settings. scaled. All Rights Reserved. 2011-03-14 | © 2011 Apple Inc. ctx. because bitmapped fonts can appear jagged when scaled up or rotated. can.width / 2 .textBaseline = "middle". Save the context. ctx.".fillStyle = "blue". Clear the canvas. 4. transformed.height / 2). text can be rotated. Note It’s best to use vector fonts when scaling or rotating text. world. and animated.textAlign = "center".font = "24pt Helvetica". The usual sequence for animation applies: 1. ctx. ctx. 53 . } </script> </head> <body onload="init()" style="background-color:black"> <canvas id="can" height="200" width="400" style="backgroundcolor:white"> </canvas> </body> </html> Animating Text Like any other visual element.

Translate the context to the x and y coordinates of the element. and the function is called periodically after a suitable delay—using setInterval() for endless animations. var steps = 50. 2011-03-14 | © 2011 Apple Inc. var step. starting small and growing to its full size as it spins. Draw the element. To repeat the animation. Restore the context. var delay = 20. Listing 5-2 <html> <head> <title>Hello world!</title> <script type="text/javascript"> Animating text var can. the simplest approach is to follow these steps: 1. Rotate the context. so the scale will grow to 1 in steps. 3. displays the string “Hello. the steps are grouped into a function. the text is scaled and rotated repeatedly. filled in blue. so a rotation increment of 2 * Pi / steps is calculated. centered at 0. When rotating an element. The text spins in. see “Rotation” (page 63). var ctx. The rotation goes from 0 to 2 x Pi radians (0-360°). as illustrated in Figure 5-4. All Rights Reserved.Adding Text Animating Text 5. (For a detailed explanation of why you should follow these steps. world!” in 48 point Helvetica. var addAngle.) Listing 5-2. 2. 54 . The animation takes place over a fixed number of steps. var addScale. or setTimeout() for animations that repeat conditionally.0. Figure 5-4 Animated text To achieve this effect. A scale increment of 1 / steps is calculated.

0. addScale * step). ctx.height / 2).getContext("2d").save().0). ctx. spinText(). addAngle = Math.scale(addScale * step. 55 .translate(can.textBaseline = "middle".clearRect(0.width. ctx.fillStyle = "blue". can.Adding Text Animating Text function init() { can = document. ctx.font = "48pt Helvetica". ctx = can.fillText("Hello.width / 2. ctx. ctx. ctx. ctx.restore(). All Rights Reserved.height).getElementById("can"). ctx. if (step < steps) var t = setTimeout('spinText()'. world!". ctx. ctx. can. addScale = 1 / steps. } function spinText() { step++. } </script> </head> <body onload="init()"> <canvas id="can" height="200" width="400"> </canvas> 2011-03-14 | © 2011 Apple Inc.0. delay). step = 0.rotate(addAngle * step).PI * 2 / steps.textAlign = "center". can.

56 . All Rights Reserved.Adding Text Animating Text </body> </html> 2011-03-14 | © 2011 Apple Inc.

however.80. The default x and y offsets of a shadow are zero. JPG images cast rectangular shadows. a line. casting shadows the shape of the nontransparent parts of the images. The shadow color must be a CSS color. with elements seeming to lift off the page. text. the x and y offsets from the source. ctx. even if the image background matches the canvas background. or GIF images on transparent backgrounds. 57 . a JPG. Even though the canvas drawing surface is 2D. You can set the shadow color. Disable shadows by setting the shadowColor to any RGBa value with an alpha component of 0—for example. To make the shadow fall away from the element. PNG. Figure 6-1 Shadows Shadow Basics Enable shadows by setting the context’s shadowColor property to a nontransparent color—for example.shadowColor = "rgba(80.80.. Shadows work well with SVG. lines.shadowColor = "black".Adding Shadows Any visual element—a shape. a PNG. It cannot be a gradient or pattern. and set a Gaussian blur to make the shadow more realistic.. or an image—can be given a shadow. ctx. set the shadowOffsetX and shadowOffsetY properties. careful use of shadows can create a sense of a third dimension. Shadows from a rectangle. and text are illustrated in Figure 6-1. 0)". For example: 2011-03-14 | © 2011 Apple Inc. All Rights Reserved.

Figure 6-2 Shadow hardness Notice that the white background and black text are visible through the shadow. which is darkened by the shadow but not completely obscured. and an x or y offset of at least 1. no matter what rotation is applied to the context. Canvas shadows mimic this real-wold behavior. Greater transparency lightens the shadow and mimics softer lighting. Once you’ve set the shadow color. Transparency and Blur You can make shadows more realistic by adding transparency and blur to lighten and soften them. A canvas with four different alpha and blur values is shown in Figure 6-2. Shadows and Animation Canvas shadows are not affected by rotation. the shadow is partly transparent. Diffuse lighting throws softer shadows. A blur of 1 is barely noticeable. the shadow falls down and to the right. regardless of an object’s orientation. 58 .shadowOffsetX = 5. You can make a shadow softer by applying a Gaussian blur. If you set the shadowOffsetX and shadowOffsetY properties both to 10. 2011-03-14 | © 2011 Apple Inc.Adding Shadows Transparency and Blur ctx. ctx. Set the blur using the context’s shadowBlur property. as long as the alpha value is less than 1. If you set the shadowColor property to an RGBa color with an alpha value less than 1. Light from a point source causes sharp shadows. for example. The default value is 0 (no blur). A blur of 5 is a soft shadow.shadowBlur=1. For example: ctx. allowing you to see the background.shadowOffsetY = 5. All Rights Reserved. A blur of 10 makes the shadow a vague blob. any drawing operation will include a shadow. A real shadow falls from the light source onto the background at the same angle.

This maintains the shadow angle. A blue rectangle orbits the soccer ball. 2011-03-14 | © 2011 Apple Inc.Adding Shadows Shadows and Animation The example illustrated in Figure 6-3 rotates a soccer ball at the center of the canvas. but the shadowOffsetX and shadowOffsetY properties do not scale with the context. regardless of the rotation. mimicking real-world behavior. Notice that the shadows retain their orientation. Figure 6-3 Shadow rotation A shadow’s size grows or shrinks as the object casting it is scaled up or down. All Rights Reserved. 59 .

if you translate and then rotate. so the center would be anywhere under your drawing.y) method. Translation. rotation. you could expand or shrink it—again without disturbing your existing drawing—and anything you drew subsequently would be scaled up or down precisely. translation. For more about transformations generally.y.0 point. After that. then restore the context and repeat the process. perform two or three transforms. The canvas’s coordinate system is spun clockwise by angle radians. or scale. but if you rotate and then translate. you could spin it around its center. you could slide it effortlessly. and for the most part. the order in which you do things is important. and scaling are all examples of two-dimensional transforms. That magic drawing surface is built into the canvas element. The origin of the canvas’s coordinate system is moved to the point x. If you perform more than one transform on the canvas. you spin the coordinate system around its existing origin.Translation. and any subsequent drawing would be rotated perfectly. Each transform is individually simple and intuitive. without disturbing anything you had already drawn. For example. you move the origin then rotate the coordinate system around it. You spin the drawing surface around the origin by calling the rotate(angle) method. things get complicated very quickly. see “Matrix Transforms” (page 69). Rotation. and Scaling Imagine that you had a magic drawing surface. and taller or shorter by yScale. it’s easy to predict what will happen when you perform two or three transforms. it’s generally wise to save the context. When you change the rotation. All Rights Reserved. then move the origin in the rotated coordinate system. 2011-03-14 | © 2011 Apple Inc. 60 . You scale the drawing surface by calling the scale(xScale. but it has no effect on anything already drawn.yScale) method. To avoid unintended interactions. you are changing the underlying coordinate system of the canvas—the change affects all subsequent drawing operations. or origin—by calling the translate(x. You reposition the center of your drawing surface—the 0. The canvas’s coordinate system is scaled wider or narrower by xScale.

ctx.0 point. drawPath().translate(120. 61 .stroke(). can. then draw the exact same path again.beginPath().40). of the canvas’s coordinate system.strokeStyle="black".y—is to translate the coordinate system to x. ctx. Rotation. or 0. ctx. Call the context’s translate(x.y) method to make the point x. } function drawPath() { ctx.closePath().Translation.40). 2011-03-14 | © 2011 Apple Inc.clearRect(can. ctx. The simplest way to draw a path at a new location—offset from its original location by x.80).lineTo(80. All Rights Reserved.lineTo(40.y. drawPath().getElementById("can").y the new origin. and Scaling Translation Translation Translation changes the origin. ctx. ctx = can. ctx. You specify a path as a series of endpoints using x and y coordinates.height).getContext("2d"). ctx. One use for translation is to redraw a path at a new location without having to respecify the endpoints.width.moveTo(0. ctx.80). as exemplified in Listing 7-1 and illustrated in Listing 7-1. Listing 7-1 Drawing a path at a new location function init() { can = document.

and draw the element at 0. 2011-03-14 | © 2011 Apple Inc. An easy way to draw an element at point x. scaled up or down. for example. All Rights Reserved. If yScale = 0. everything you draw comes out twice as wide. then draw the element at the point x / xScale. Anything you draw subsequently is made bigger or smaller by the xScale and yScale values. Rotation.y.5 everything comes out half as tall. Alternatively. and Scaling Scaling } Figure 7-1 Translating a path Scaling You expand or contract the canvas’s coordinate system by calling the context’s scale(xScale. 62 . If xScale = 2.y.0. is to first translate to x. y / yScale. yScale) method.Translation. If you scale an element. you need to compensate for the changes to the coordinate system when placing the element on the canvas. then set the scale. you can set the scale without translating the coordinate system first.

2. 2011-03-14 | © 2011 Apple Inc. follow these steps: 1. set the rotation. The coordinate system is rotated by angle radians. But that’s really doing it the hard way. clockwise.y). and Scaling Rotation Rotation You rotate the canvas’s coordinate system around its origin by calling the context’s rotate(angle) method.translate(x.restore(). 3. All Rights Reserved.0. Call ctx. is to translate to the point x. In other words.y rotated at an angle. Call ctx. except at the origin. if you want to draw something at x. you can rotate the canvas without translating the coordinate system first. which remains at 0.0. When you rotate the canvas. The simplest way to draw an element at a point x.save(). shape. Rotation. Alternatively. once at 0 degrees and once at 90 degrees of rotation. then draw the element at x * cos(-angle) . and draw the element at 0. 5.y.y. Draw the path.y * sin(-angle).0. but subsequent drawing operations are rotated.rotate(angle). 4. y * cos(-angle) + x * sin(-angle). or image at 0. Anything already on the canvas is unaffected. 63 . The example in Listing 7-2 draws an image and some accompanying text twice. Call ctx.Translation. the x and y coordinates change in complex ways. rotated at an angle. Call ctx.

y = 0. drawSprite(). ctx. var x. ctx. function init() { sprite = document. drawSprite(). ctx. ctx.rotate(rot). var rot = 0. rot = 0. rot = Math.getElementById("sprite").PI / 2.fillStyle="blue". 64 . All Rights Reserved. ctx. ctx = can. can = document. y = 100. x = 100. var sprite. var ctx. } function drawSprite() { ctx. 2011-03-14 | © 2011 Apple Inc.getElementById("can").getContext("2d"). Rotation. var y.textBaseline="top". and Scaling Rotation Listing 7-2 <html> <head> Rotating an image and text <title>Rotation</title> <script type="text/javascript"> var can.Translation.translate(x.font="12pt Helvetica". x = 100.y).save().

all subsequent drawing operations are rotated clockwise by angle radians. } </script> </head> <body onload="init()" > <h2>Rotation</h2> <canvas id="can" height="200" width="300" > </canvas> <img id="sprite" src="lem. if height and width are positive). 2011-03-14 | © 2011 Apple Inc. this is easy. 65 .height / -2. Rotation. 0.png. var yOffset = image. 0.0).drawImage(sprite. ctx.restore().rotate(angle). Images are rotated around their origin (the upper-left corner). ctx. Rectangles are rotated around their x. ctx.0).yOffset). ctx. Other shapes are rotated around the first point on the shape’s path.png" style="display:none"> </body> </html> Rotation Around the Center Once the context’s rotate(angle) method is called. and Scaling Rotation Around the Center ctx.translate(x. In many cases.Translation. The following snippet draws an image rotated around its center at position x. All Rights Reserved. Offset the drawing by negative one-half the height and width of the image or rectangle.y).save(). ctx. you may want to rotate elements around their center instead.drawImage(image. ctx.y coordinate (the upper-left corner.y: var xOffset = image. xOffset. ctx. 1".width / -2.fillText("fig.restore(). For images and rectangles.

png.lineTo(x+25.Translation. Listing 7-3 and Listing 7-4 show two ways to define the same shape. } Listing 7-4 var x=25.moveTo(x-25.0).rotate(angle). var y=25. as shown in the following snippet.lineTo(50. but the second snippet is easily moved and rotated by setting new values for x and y.width / -2.lineTo(x+25.0).50). var yOffset = image.scale(xScale.beginPath(). Listing 7-3 Defining a shape literally function drawPath() { ctx. 66 . Rotation.yScale). ctx.save(). and Scaling Rotation Around the Center If you are scaling the image as well as rotating it.y-25).lineTo(50.restore().moveTo(0. yOffset * yScale). ctx. multiplied by the scale. var xOffset = image.beginPath().lineTo(0.y). ctx. If you are planning to rotate a path or complex shape about its center. ctx. define the points on the path relative to the center point. ctx. ctx. All Rights Reserved. ctx. Defining a shape relative to its center point function drawPath() { ctx. ctx. ctx.y-25). ctx. ctx. offset the drawing operation by negative one-half the height and width. ctx.height / -2. xOffset * xScale. ctx.translate(x. ctx.50).drawImage(image.y+25).closePath(). 2011-03-14 | © 2011 Apple Inc.

Translation. This is probably what you would intuitively expect. whatever you draw is rotated. ctx. whatever you draw is stretched in the direction of the rotated x-axis. rotated at an angle.save(). Combining Asymmetric Scaling and Rotation Most combinations of rotation. Rotation.y).translate(x. and Scaling Combining Asymmetric Scaling and Rotation ctx. 2011-03-14 | © 2011 Apple Inc. and scale behave as you would intuitively expect them to.restore(). ctx. but stretching or compressing the shape by using a different scale on the x-axis and y-axis. 67 . The one exception is combining rotation and asymmetric scaling—not just making things bigger or smaller.closePath(). things are drawn twice as tall. If you rotate the coordinate system a quarter turn. then scale just the x-axis. ctx. ctx. ctx. if you consider what each transform does and perform the operations in a logical sequence. All Rights Reserved. for example.lineTo(x-25. for example. and then increase the x scale by a factor of two.y+25). but retains its stretched shape. If you rotate the coordinate system. } The following snippet draws the shape with its center at any point x.rotate(angle). translation.y. drawPath().

then rotate it. setting the x scale and y scale to the same value. If you scale symmetrically. and Scaling Combining Asymmetric Scaling and Rotation If you scale before you rotate. you can perform the rotate() and scale() operations in either order. then scaling. All Rights Reserved. in that order. 68 . Figure 7-2 Nonintuitive interaction Unmodified rectangle Rotated 90 degrees X scaled x 2 Rotated 90 degress. whatever you draw is stretched in the direction the x-axis was pointing prior to the rotation. This causes the shape of things you draw to change when rotated.Translation. Rotation. The interactions are illustrated in Figure 7-2. This is probably not what you would expect to happen. then rotated 90 degrees To stretch something. then X scaled x 2 X scaled x 2. however. you must perform the transforms by first rotating. retaining the stretched shape. 2011-03-14 | © 2011 Apple Inc.

f) method. Be sure to check the position of parameters in the matrix when copying matrix settings from another source.c. The position of the parameters in the matrix is shown in Figure 8-1. There are convenience methods for the most common transforms—rotation. or scale settings. When you set the transformation matrix. passing in new values for the first two rows of the matrix (the third row is always 0 0 1). translation. All Rights Reserved. such as a bitmap. and scaling—but you can use matrix transforms to achieve other effects as well. Figure 8-1 Matrix parameters and positions a b 0 c d 0 e f 1 Important Other APIs that use transformation matrices may order the parameters differently.Matrix Transforms Rotation. and scaling are all accomplished using a transformation matrix—a set of nine numbers that are used to transform a two-dimensional array. as these settings all use the transformation matrix. such as shearing or reflection. it overrides any rotation.d.e. translation. using linear algebra. Setting the Transformation Matrix You can set the transformation matrix to new values by calling the context’s setTransform(a.b. 69 . translation. 2011-03-14 | © 2011 Apple Inc.

Matrix Transforms Setting the Transformation Matrix

Note If you set the rotation, scale, or translation after setting the transformation matrix, the transformation matrix is modified, but is not reset—the matrix values used by the operation are overridden, but other matrix values are not changed.

Example: Setting the Matrix for Reflection
To set the transformation matrix to reflect everything around the y-axis, call ctx.setTransform(1,0,0,-1,0,0). Thereafter, all drawing operations result in an upside-down image, and all y-coordinates are multiplied by -1. To set up a transformation matrix to reflect everything around the x-axis, call ctx.setTransform(-1,0,0,1,0,0). Thereafter, all drawing operations result in a mirror image, and all x-coordinates are multiplied by -1. To set up a transformation matrix to reflect everything around both the x-axis and y-axis, call ctx.setTransform(-1,0,0,-1,0,0). Thereafter, all drawing operations result in an upside-down mirror image, and all x and y-coordinates are multiplied by -1. An example of text reflected around the y-axis is generated by Listing 8-1 and shown in Figure 8-2.
Figure 8-2 Reflected text

Listing 8-1
<html> <head>

Reflecting text

<title>Reflection Matrix</title> <script type="text/javascript">

var can; var ctx;

function init() { can = document.getElementById("can"); ctx = can.getContext("2d");

2011-03-14 | © 2011 Apple Inc. All Rights Reserved.

70

Matrix Transforms Setting the Transformation Matrix

ctx.fillStyle="blue"; ctx.font="48pt Helvetica"; ctx.fillText("Reflection", 0,100); ctx.setTransform(1,0,0,-1,0,0); ctx.fillStyle="red"; ctx.fillText("Reflection", 0,-100); }

</script> </head>

<body onload="init()"> <canvas id="can" height="200" width="300"> </canvas> </body> </html>

You can set the scale after setting a reflection matrix, because the two operations use different parts of the matrix. Listing 8-2 adds a few lines to the previous example that set the y-scalar to 1.5 and sets the fill style to a gradient instead of a solid color, making the reflection taller and fading it from blue to transparent white. The result is shown in Figure 8-3
Figure 8-3 Reflection with scale and gradient

Listing 8-2

Adding a scalar and gradient

ctx.scale(1,1.5); var grad = ctx.createLinearGradient(0,-50, 0,-140); grad.addColorStop(0, 'blue'); grad.addColorStop(1, 'rgba(255,255,255,0)'); ctx.fillStyle=grad; ctx.fillText("Reflection", 0,-102 / 1.5);

2011-03-14 | © 2011 Apple Inc. All Rights Reserved.

71

Matrix Transforms Transforming the Transformation Matrix

Note The y-coordinates of the gradient and reflected text are modified because of the transformation matrix—the y-coordinates are negated and divided by the y-scalar (1.5).

The Identity Matrix
To reset the transformation matrix to the identity matrix (no transformation), call ctx.setTransform(1,0,0,1,0,0). The identity matrix and the parameter positions are illustrated in Figure 8-4.
Figure 8-4 The identity matrix

a=1 c=0 e=0 b=0 d=0 f=0 0 0 1

Transforming the Transformation Matrix
The transformation matrix can be treated as a two-dimensional array. You can transform the current matrix by applying a second matrix to it. To transform the current matrix by a second matrix, call the context’s transform(a,b,c,d,e,f) method, passing in values for the first two rows of the second matrix (the third row is always 0 0 1). Transforming the transformation matrix potentially changes any rotation, scale, or translation settings. Setting the rotation, scale, or translation after transforming the transformation matrix results in additional changes to the matrix—the matrix values used by the rotation, scale, or translation operation are overridden. Transforming the transformation matrix is a method intended for developers well-versed in linear algebra. A description of the mathematics involved falls outside the scope of this document.

2011-03-14 | © 2011 Apple Inc. All Rights Reserved.

72

Advanced Compositing

By default, images are layered on the canvas in drawing order, with new images layered on top of older images. Also by default, images that have areas with alpha values less than 1 are partly or wholly transparent, letting lower layers show through to varying degrees. The web page background is the lowest layer, followed by any HTML elements positioned beneath the canvas element. Any CSS background applied to the canvas element comes next, followed by anything drawn on the canvas, with the most recent image, line, or shape in the top layer. The canvas element also supports a global alpha channel. The global alpha channel can be set to any value between 0 and 1, inclusive, and defaults to 1. All drawing operations have their alpha values multiplied by the global alpha value, and so can be attenuated to any degree, up to full transparency. This allows you to draw semitransparent images, even if the source image has no alpha channel. The global alpha value, simple layering, and the default alpha-channel compositing mode provide sufficient compositing capabilities for most applications, but canvas has ten other built-in compositing modes in addition to the default mode, and supports vendor-specific extensions as well.

Global Alpha
The global alpha value allows you to draw with varying degrees of transparency. Set the global alpha value by assigning it to the context’s globalAlpha property.
ctx.globalAlpha=alpha

The alpha value must be a number between 0 and 1, inclusive. A value of 1 does not change the native alpha value of images, colors, gradients, or patterns. No transparency is added. A value of less than 1 reduces the alpha values of all drawing operations proportionately. A value of 0 causes all subsequent drawing operations to render transparently.

2011-03-14 | © 2011 Apple Inc. All Rights Reserved.

73

or a combination pixel. including the default mode.globalCompositeOperation = "copy". transparent (alpha = 0). the destination pixel. Source and destination pixels can be opaque (alpha = 1). 2011-03-14 | © 2011 Apple Inc.Advanced Compositing Compositing Modes Compositing Modes The canvas element has eleven built-in compositing modes. which uses values from the source and destination. combined using an algorithm. You set a compositing mode by setting the context’s globalCompositeOperation property to one of these values: ● "source-over" (default) "source-atop" "source-in" "source-out" "destination-over" "destination-atop" "destination-in" "destination-out" "lighter" "copy" "xor" ● ● ● ● ● ● ● ● ● ● ● “vendorName-operationName” Example: ctx. a transparent pixel (erase both source and destination to transparent). or translucent (0 < alpha < 1). Compositing deals with a source (the image being drawn). and a destination (the canvas and anything already on it). and support for vendor-specific extensions. 74 . The result of the composition can be to display the source pixel. All Rights Reserved.

● ● ● ● ● ● ● ● ● ● 2011-03-14 | © 2011 Apple Inc. destination-in—Display the destination image wherever both the destination image and source image are opaque. Display the destination image only where the source image is transparent. All Rights Reserved. Display a blend wherever the source is translucent and the destination is either translucent or opaque. destination-over—Display the destination image wherever the destination image is opaque. Display a blend wherever both the destination and source are translucent. Display the destination image wherever the source is transparent. Display the source image wherever the destination is transparent. Display transparency wherever either the destination or source are transparent. source-atop—Display the source image wherever both images are opaque. Display transparency where the destination is transparent. copy—Display the source image wherever it is opaque or translucent. destination-atop—Display the destination image wherever both images are opaque. 75 . xor—Display the result of a bitwise exclusive-OR operation between the source and destination pixels. Display a blend wherever the source and destination are both translucent. Display a blend wherever both the source and destination are translucent. Display a blend of source and destination wherever the source is translucent (0 < alpha < 1). lighter—Display an image in which the RGBa color values of source and destination are summed. Display a blend wherever the destination is translucent and the source is either translucent or opaque. This operation is reversible—drawing the source a second time using xor composition restores the destination exactly. Display the source image wherever the source image is opaque. Display transparency wherever either the source or destination are transparent. source-out—Display the source image wherever the source image is opaque and the destination image is transparent. Display the source image wherever the destination image is transparent. Display a blend of source and destination wherever the destination is translucent. source-in—Display the source image wherever both the source image and destination image are opaque.Advanced Compositing Compositing Modes A detailed description of the composition modes follows. Display transparency elsewhere. Display a blend wherever the destination and source are both translucent. ● source-over—The default value for compositing. Display transparency elsewhere. with a maximum RGB value of 255 and a maximum alpha value of 1. destination-out—Display the destination image wherever the destination image is opaque and the source image is transparent. Display the destination image wherever the source image is transparent.

There is often text or artwork on the chart as well. You can use a single canvas element for the whole chart. Scaling Your Data A graph or chart normally contains lines or bars plotted against a grid. Use a data visualization JavaScript library. so you need a way to translate from the data value to a canvas y-coordinate. 76 . Write a routine that calculates a y-coordinate from the data. There are three common solutions for resolving y-coordinates for graphs and charts: ● Use the transformation matrix to flip and scale the canvas’s y-axis. and the y-coordinates are scaled to fit the data. All Rights Reserved. the easy way to fit your data to the grid is to use the transformation matrix. The transformation matrix can flip the y-axis. Graphs are usually presented with the y-axis going up. If you’d prefer to use a JavaScript library. scale the y-axis so the vertical display area equals your data range. and pie charts. If you are using a canvas element primarily as a grid. ● ● This chapter shows you how to use the transformation matrix for graphing and how to calculate a y-coordinate for a data point on a graph. This chapter shows you how to create data plots. The main difficulty is scaling the data to fit neatly on the page. 2011-03-14 | © 2011 Apple Inc. Then your data value is the y-coordinate. not down. and translate the y-axis zero point to the zero point for your data.Creating Charts and Graphs It’s easy to generate charts and graphs from data using the canvas element. or you can use a canvas element just as a grid for the data. bar graphs. a web search for “canvas JavaScript libraries” or “canvas JavaScript libraries data visualization” will turn up a current list of libraries. including a vertical scale along the left edge. or positioning a canvas element inside of another canvas element using CSS. surrounding the grid with text and artwork using HTML and CSS.

minVal).0 point on data graph 2011-03-14 | © 2011 Apple Inc. } function transformYAxis() { var displayHeight = can. you probably don’t want to transform the text or art using the matrix—it would be upside down and stretched vertically. you need to know the minimum and maximum data values of your grid and the size of any header or footer area of the canvas that isn’t used for graphing. If you draw art or text on the canvas. var maxVal. Scaling Using the Transformation Matrix To scale the y-axis to fit your data using the transformation matrix.getElementById("can"). you need to use JavaScript to calculate the appropriate y-coordinates. function init() { can = document. var topMargin. var minVal. You need to use JavaScript to calculate the proper y-coordinates for your data.height . positioned relative to the data.botMargin. var yScalar = displayHeight / (maxVal . once you set the variables. If your data needs to be plotted against a log scale instead of a linear scale.getContext("2d"). var leftMargin. Listing 10-1 performs the transformation to scale the y-axis and position the zero point correctly. or you can save the context. //translate to 0.topMargin . var ctx. 77 . and draw your text or art. All Rights Reserved.Creating Charts and Graphs Scaling Your Data If you include text or art on the canvas. graph the data. var botMargin. restore the context. the transformation matrix won’t be much help—the matrix is for linear transforms. You can either render the art or text before you change the transformation matrix. ctx = can. Listing 10-1 Scaling by matrix var can.

you need to know the maximum and minimum values on your graph and the height of any header and footer areas of the canvas that won’t be used for graphing.can.botMargin.topMargin . var bottom.scale(1. var displayHeight.minVal).translate(leftMargin. 78 . bottom = can. var botMargin.getElementById("can"). } function calcY(value) { 2011-03-14 | © 2011 Apple Inc. } Scaling Using a Function To calculate a canvas y-coordinate from a data value. All Rights Reserved. // scale canvas to match data graph and flip y-axis ctx. var yScalar. var ctx. var y.height . once you fill in the maximum and minimum data values and header and footer heights.botMargin. var topMargin. yScalar = displayHeight / (maxVal .height + minVal * yScalar). Listing 10-2 sets the value of y from a given data value. function init() { can = document.Creating Charts and Graphs Scaling Your Data ctx. displayHeight = can.height . ctx = can.getContext("2d"). // var can. Listing 10-2 Creating a scaling function // set these four values var maxVal.-1 * yScalar). var minVal.

The example scales the canvas vertically to the range of sample values. so the y-coordinate increases as you move up the graph. the data sets are usually color coded. All you have to do is supply the data and set a few variables. the number of samples. such as the minimum and maximum sample values.y). you can simply call lineTo(x. 79 . } Data Plots A data plot is a graph showing your sample data plotted on a grid. A sample data plot is shown in Figure 10-1. This is especially useful for graphing rapidly changing data. A template for data plots is provided in Listing 10-3. and any text for the column headers. and horizontally to the number of samples.value * yScalar + yScalar * minVal. The y-axis is scaled to -1 times the scalar. Figure 10-1 Sample data plot The advantage to using canvas for data plots.Creating Charts and Graphs Data Plots y = bottom . 2011-03-14 | © 2011 Apple Inc. is that you can update the artwork just by refreshing the data that it illustrates. If you have more than one data set. To plot a given sample number x . real-time data. instead of drawing them using a graphics tool. or user-entered data. of value y . All Rights Reserved.

var yScalar. 80. var ctx. "Jul". 77. "May". function init() { // set these values for your data numSamples = 12. 90. 100. 60. var rowHead = 50. 60. "Jun". ctx.fillStyle = "black" ctx. var colHead = 50.font = "14pt Helvetica" 2011-03-14 | © 2011 Apple Inc. 68]. 76. 80 . var numSamples. 50. 75. var margin = 5.Creating Charts and Graphs Data Plots Listing 10-3 Data plot template <html> <head> <title>Plotting Data</title> <script type="text/javascript"> var can. "Nov". 78. var maxVal. "Sep". 40. 60. 30. 40.set literally or obtain from a file or . 50. 40]. maxVal = 120. minVal = -30. All Rights Reserved. "Dec"] // can = document.getElementById("can"). 70. 74. 50. 70. var stepSize = 10. var xScalar. 50. 30. var minVal. var kansasCty = [20. ctx = can. "Aug". 70. "Mar".asp call var sanDiego = [72. 0. -20. "Feb". var header = [" ". 90. 50. "Oct". 72. // data sets -. 0]. 74. "Jan".getContext("2d"). var buffalo = [-10. "Apr". 72. 70.

ctx. 81 . plotData(buffalo).height .5)".margin) / (maxVal .width.128. // light blue lines ctx. ctx.lineTo(can.moveTo(rowHead. margin. // print column header and draw vertical grid lines for (i=1. // set horizontal scalar to available width / number of samples xScalar = (can. for (scale=maxVal.Creating Charts and Graphs Data Plots // set vertical scalar to available height / data points yScalar = (can. } 2011-03-14 | © 2011 Apple Inc.strokeStyle="purple". } ctx.margin).y) ctx. ctx.fillText(scale.y) count++. x. ctx.beginPath().minVal). plotData(kansasCty).width .colHead . plotData(sanDiego). colHead).lineTo(x. // set a color and make one call to plotData() // for each data set ctx. can. ctx. } // print row header and draw horizontal grid lines var count = 0.colHead .stroke().moveTo(x.strokeStyle="red". 0.y + margin). All Rights Reserved.margin).rowHead) / numSamples.strokeStyle="green".strokeStyle="rgba(128.fillText(header[i].scale = scale .i<=numSamples.stepSize) { var y = colHead + (yScalar * count * stepSize). ctx. ctx.scale>=minVal.255.i++) { var x = i * xScalar. ctx.height .

for (i=1.identify your data sets --> <span style="color:green"> San Diego = green </span> &nbsp. <span style="color:red"> Kansas City = red </span> &nbsp. but each sample is graphed as a rectangle scaled to the height or width of the sample.lineTo(i * xScalar. 2011-03-14 | © 2011 Apple Inc.Creating Charts and Graphs Bar Graphs function plotData(dataSet) { ctx. dataSet[0]).i<numSamples. All Rights Reserved. } ctx.stroke(). <span style="color:purple"> Buffalo = purple </span> </div> </body> </html> Bar Graphs Bar graphs are similar to data plots. } </script> </head> <body onload="init()"> <div align="center"> <h2>Average Temperature By City</h2> <canvas id="can" height="400" width="650"> </canvas> <br> <!-. 82 .moveTo(0.beginPath().i++) { ctx. dataSet[i]). ctx.

83 . var dataValue = [ 11000. 6200. "Chimp". var maxVal. var y. as shown in Figure 10-2. "Dolphin". var minVal. 300 ]. "Cat" ]. var xScalar. Figure 10-2 Sample bar graph Listing 10-4 Bar graph template <html> <head> <title>Bar Graph</title> <script type="text/javascript"> var can. var ctx.set literally or obtain from a file or .asp call var dataName = [ "Human". All Rights Reserved. 2011-03-14 | © 2011 Apple Inc. // data sets -. var yScalar.Creating Charts and Graphs Bar Graphs The example in Listing 10-4 (page 83) graphs data as vertical bars with a text label floating over each bar. var numSamples. 5800.

xScalar = (can.stepSize) { y = colHead + (yScalar * count * stepSize). ctx.y) ctx.beginPath().lineTo(can. } ctx.font = "12pt Helvetica" var count = 0.width . var header = "Millions" // can = document.height .scale = scale . // label samples ctx. maxVal = 12000.width.getContext("2d"). ctx.textBaseline="bottom".Creating Charts and Graphs Bar Graphs function init() { // set these values for your data numSamples = 4. 2011-03-14 | © 2011 Apple Inc.colHead . All Rights Reserved. 84 . ctx.fillText(header.fillText(scale. for (scale=maxVal. 0. var stepSize = 1000. 0. // print row header and draw horizontal grid lines ctx.y + margin).255. var colHead = 50.getElementById("can"). // print column header ctx. ctx.fillStyle = "black" yScalar = (can. var rowHead = 60. // light blue line ctx.y) count++.strokeStyle="rgba(128.colHead .5)".scale>=0.margin) / (maxVal). ctx. ctx = can.font = "14pt Helvetica" ctx.font = "14pt Helvetica".moveTo(rowHead.stroke().128.margin). var margin = 10.rowHead) / (numSamples + 1). margin.

y to match data ctx.shadowOffsetX = 20. dataValue[i]).fillRect(i+1.i++) { ctx.fillText(dataName[i]. All Rights Reserved.i<4.i++) { calcY(dataValue[i]). 0.height . ctx.i<4. 0.5)' ctx. ctx.fillStyle="green".128.height . ctx.can. // translate to bottom of graph and scale x. // draw bars for (i=0. xScalar * (i+1). } </script> </head> <body onload="init()"> <div align="center"> <h2>Neurons in Cerebral Cortex</h2> <canvas id="can" height="400" width="650"> </canvas> </div> </body> </html> 2011-03-14 | © 2011 Apple Inc.margin).y .Creating Charts and Graphs Bar Graphs for (i=0.value * yScalar.128.margin). 0. ctx.scale(xScalar.5. } // set a color and a shadow ctx. 85 .-1 * yScalar).translate(0.shadowOffsetY = 1.shadowColor = 'rgba(128. } } function calcY(value) { y = can.

Set endAngle to the fraction of the circle represented by a given sample: Math. For subsequent samples. Use the arc(x.arc(midX. Listing 10-5 draws a pie chart from an array of samples and an array of fill colors. All Rights Reserved. then render each portion as its part of a circle. var angle = oldAngle + wedge.Creating Charts and Graphs Pie Charts Pie Charts You create a pie chart by treating each sample as a wedge of pie—add the samples together to get the size of the pie.beginPath().y to the middle of the canvas. startAngle. 2011-03-14 | © 2011 Apple Inc.i<numSamples. determine the proportion of each slice. Listing 10-5 Drawing a pie chart var oldAngle = 0. ctx.y) to connect the end of the arc to the center of the pie.width /2. startAngle equals the first startAngle plus the endAngle of all previous samples. You can outline the shape in another color using the stroke() method.i++) { // draw wedge var portion = dataValue[i] / total. or wherever you want the center of your pie chart. var midY = can. endAngle) method to draw the outside of each wedge of the pie. For the first sample. var midX = can. angle). 86 . var wedge = 2 * Math. startAngle can be any value—you can start anywhere you like on a circle. ctx. var radius = midY. radius. Set radius to no more than half the height or width of the canvas—less if you want to have room for labels. Fill the wedge with a color using the fill() method. radius. // do for each sample: for (i=0.PI * 2 * sample / total Use lineTo(x. oldAngle.height /2. Set x. Use closePath() to connect the center back to the start of the arc and complete the wedge.y.PI * portion. midY.

ctx.fill().fillStyle = fillColor[i]. but one approach is to label each sample with text outside the pie. as it depends on the height and width of the text—you don’t want to run into the pie or off the canvas.lineTo(midX. Listing 10-6 generates a series of pie charts from a menu of quarterly results and labels the samples as shown in Figure 10-3. 87 .closePath().Creating Charts and Graphs Pie Charts ctx. midY). // fill with wedge color // outline in black oldAngle = oldAngle + wedge. ctx. aligned with the center of the wedge. } Labeling a pie chart is more art than science. Figure 10-3 Sample pie chart Listing 10-6 Pie chart generator <html> <head> 2011-03-14 | © 2011 Apple Inc. ctx. All Rights Reserved.stroke(). Positioning the text can be a bit tricky. ctx.

"Midwest".textAlign = "center". All Rights Reserved.height / 3. 900000. 700000. ctx = can. var q3Value = [ 800000. "green". "blue". can = document. function init() { // set this value for your data numSamples = 4. var midX = can. var fillColor = ["red".width / 2. 88 .textBaseline = "middle".getElementById("can"). ctx. 900000 ]. ctx.strokeStyle = "black".set literally or obtain from a file or . "West" ]. var numSamples. var q2Value = [ 900000. 700000.Creating Charts and Graphs Pie Charts <title>Pie Chart</title> <script type="text/javascript"> var can. drawPie(). ctx. } function drawPie() { radius = can. var quarter.font = "18pt Helvetica". 2011-03-14 | © 2011 Apple Inc. var ctx.getContext("2d"). "orange" ]. var midY = can. 3000000 ]. ctx. var q1Value = [ 1200000. 1800000 ].asp call var dataName = [ "East". 600000. quarter = document. // data sets -. var yScalar. "South".getElementById("quarter").height / 2. var xScalar. var radius. 800000. 600000.

midY. ctx. All Rights Reserved. // set x.Creating Charts and Graphs Pie Charts // get data set var dataValue = q1Value.value=="q3") dataValue = q3Value. if (quarter. can.stroke().5. // calculate total value of pie var total = 0. radius.arc(midX.lineTo(midX.closePath().cos(labAngle) * radius * 1. var angle = oldAngle + wedge.value=="q2") dataValue = q2Value.i<numSamples. can. 2011-03-14 | © 2011 Apple Inc. var oldAngle = 0. var wedge = 2 * Math. // fill with wedge color // outline in black // print label // set angle to middle of wedge var labAngle = oldAngle + wedge / 2. if (quarter. ctx. } // get ready to draw ctx.beginPath(). ctx.0.i++) { total = total + dataValue[i].i<numSamples. ctx.fillStyle = fillColor[i]. midY). angle).clearRect(0. // do for each sample: for (i=0. oldAngle.width.y for label outside center of wedge // adjust for fact text is wider than it is tall var labX = midX + Math. 89 . ctx.i++) { // draw wedge var portion = dataValue[i] / total.height). for (i=0. ctx.fill(). ctx.PI * portion.

90 . ctx.fillText(dataName[i].shadowOffsetY= -1. All Rights Reserved. ctx.3 . ctx. labY + 25).fillText("$" + dataValue[i].sin(labAngle) * radius * 1. // update beginning angle for next wedge oldAngle = oldAngle + wedge. labX. labX.Creating Charts and Graphs Pie Charts var labY = midY + Math.restore().save(). ctx. ctx. } } </script> </head> <body onload="init()"> <div align="center"> <h2>Sales by Region</h2> <canvas id="can" height="400" width="500"> </canvas> </div> <br> <select id="quarter" onchange="drawPie()" style="font:18pt Helvetica"> <option value="q1">Q1</option> <option value="q2">Q2</option> <option value="q3">Q3</option> </select> </body> </html> 2011-03-14 | © 2011 Apple Inc.shadowOffsetX = 1. labY).fillStyle = fillColor[i]. ctx.12.shadowColor = "black". // print name and value with black shadow ctx. ctx.

The example in Listing 10-7 (page 91) graphs three sine waves. you can often turn a static graph into an animation with a line or two of code. You can easily add user interaction to a graph by adding a few <input> elements to allow users to change variable values. see “Animating the Canvas” (page 99). Figure 10-4 Frequency addition Adding a few buttons makes the graph interactive by allowing the user to change the frequency and phase of the sine waves and see the result.Creating Charts and Graphs Interactive Data Visualization and Animation Interactive Data Visualization and Animation Students and the general public find science more interesting when data is presented visually. then graphs the combination of the three waves to illustrate frequency addition. then redrawing the waves. For more about animation. especially when it changes in response to user input. turns the graphs of the waves into tiny oscilloscopes. By adding a value that changes over time. transforming a static image into an animation that grabs the eye. All Rights Reserved. as shown in Figure 10-4. 91 . Adding animation and user input makes a graph much more engaging. Adding a global phase variable and incrementing it repeatedly. Listing 10-7 Performing interactive frequency addition <!doctype html> <html> <head> <title>Frequency Addition</title> 2011-03-14 | © 2011 Apple Inc.

var sixDeg = Math. color[3]="white".2.PI. label[1] = document. canvas[1] = document. color[1]="green". var globalPhase = 0. label[2] = document. canvas[0] = document.Creating Charts and Graphs Interactive Data Visualization and Animation <script type="text/javascript"> var canvas = []. var freq = [].5 * Math. var ctx = []. 2011-03-14 | © 2011 Apple Inc. All Rights Reserved. var cHeight = 50. // vertical scale for 3 sine waves above and below x-axis without hitting edges var vScale = (canvasHeight / 6) . function init() { color[0]="red".getElementById("canvas0").getElementById("label2").PI / 30.getElementById("label1"). var label = []. 92 .getElementById("canvas1"). var canvasWidth = 180. color[2]="blue". var canvasHeight = 150. var aCircle = 2 * Math. var phase = [].getElementById("label0"). var ninetyDeg = 0.PI. var color = []. label[0] = document.

i++) { ctx[i] = canvas[i].strokeStyle=color[i]. phase[2]=0. All Rights Reserved. Phase: ' + phaseDeg + '&deg. ctx[i]. drawSinWave(0). drawSinWave(1). freq[2]=3.Creating Charts and Graphs Interactive Data Visualization and Animation canvas[2] = document. } 2011-03-14 | © 2011 Apple Inc.PI * 180). var labelString = 'Frequency: ' + freq[i] + ' &nbsp.i<3.getElementById("canvas3"). labelWaves().innerHTML = labelString. canvas[3] = document. ctx[i]. for (i=0. setInterval("animate()". phase[0]=0.i<4. drawSinWave(2). phase[1]=0. } freq[0]=1.'. 93 .getContext("2d"). label[i].fillStyle="black".lineWidth=2. 40). freq[1]=2. ctx[i]. } } function animate() { globalPhase = globalPhase + sixDeg. } function labelWaves() { for (i=0.i++) { var phaseDeg = parseInt(phase[i] / Math.getElementById("canvas2"). drawAllWaves().

xAxis). All Rights Reserved.0. var yCoord= sinY * vScale + xAxis. // draw X axis var xAxis = cHeight / 2. // plot graph of all waves added together 2011-03-14 | © 2011 Apple Inc.beginPath().Creating Charts and Graphs Interactive Data Visualization and Animation function drawSinWave (index) { var thisCtx = ctx[index].moveTo(canvasWidth.canvasWidth. 94 . var sinY= Math.cHeight).yCoord).xAxis). thisCtx. } function drawAllWaves() { var thisCtx = ctx[3].canvasWidth. // clear to black thisCtx. thisCtx.fillRect(0. var steps=canvasWidth / freq[index]. var xAxis = canvasHeight / 2.lineTo(0.stroke(). // draw X axis thisCtx. j<=steps.sin(radians). thisCtx.moveTo(canvasWidth.lineTo(0. thisCtx.lineTo(xCoord. } } thisCtx. thisCtx.beginPath().xAxis).fillRect(0. var radians= (aCircle / steps) * j + phase[index] + (globalPhase * freq[index]). thisCtx. for (i=0.xAxis). i++) { for (j=0. j++) { var xCoord = i*steps+j. thisCtx.canvasHeight).0. // plot graph of sine wave var xCoord=0. i<freq[index].

if (parseInt(thePhase) == 6) thePhase = 0. labelWaves(). drawSinWave(index). } function decrement(index) { freq[index]--.yCoord). } thisCtx. 95 . for (i=0. } function addPhase(index) { var thePhase = phase[index] + ninetyDeg. } yCoord = yCoord * vScale + xAxis. } function increment(index) { freq[index]++. thisCtx. var sinY = Math. yCoord = yCoord + sinY. 2011-03-14 | © 2011 Apple Inc. for (j=0. All Rights Reserved. drawSinWave(index).stroke(). labelWaves(). var yCoord = 0. if (freq[index] < 0) freq[index] = 0.j++) { var steps = canvasWidth/freq[j].Creating Charts and Graphs Interactive Data Visualization and Animation var xCoord=0. var radians = (aCircle / steps) * i + phase[j] + (globalPhase * freq[j]).i++) { var xCoord = i.i<canvasWidth.j<3.lineTo(xCoord.sin(radians).

width:480px. } </script> </head> <body onload="init()"> <P> <EM> The canvas element is well-suited to display scientific or numeric data. drawSinWave(index). labelWaves(). </EM> <H1>Frequency Addition</H1> <div id="main" style="border: 5px inset #80e080. 96 . especially interactive data. All Rights Reserved."> <B>Wave 1</B><BR> <canvas id="canvas0" width="180" height="50"> test </canvas> <BR> <B>Wave 2</B><BR> <canvas id="canvas1" width="180" height="50"> </canvas> <BR> <B>Wave 3</B><BR> <canvas id="canvas2" width="180" height="50"> 2011-03-14 | © 2011 Apple Inc. width: 200." > <div id="waves" style="margin: 5px.Creating Charts and Graphs Interactive Data Visualization and Animation phase[index] = thePhase.

Creating Charts and Graphs Interactive Data Visualization and Animation

</canvas>

<div> <br><B>Addition of waves 1, 2, and 3</B><BR> <canvas id="canvas3" width="180" height="150"> </canvas> </div>

</div>

<div id="controls" style="width: 120; margin: 5px; position:absolute; left:200px; top:118px;">

<p id="label0">Freq: 1 Phase: 0</p> <input type="button" value=" ^ " onclick="increment(0)"> <input type="button" value=" v " onclick="decrement(0)"> <input type="button" value="+90&deg;" onclick="addPhase(0)">

<P id="label1">Freq: 2 Phase: 0</P> <input type="button" value=" ^ " onclick="increment(1)"> <input type="button" value=" v " onclick="decrement(1)"> <input type="button" value="+90&deg;" onclick="addPhase(1)">

<P id="label2">Freq: 3 Phase: 0</P> <input type="button" value=" ^ " onclick="increment(2)"> <input type="button" value=" v " onclick="decrement(2)"> <input type="button" value="+90&deg;" onclick="addPhase(2)">

</div> </div>

<P> Complex waveforms can be made by adding simple sine waves.

2011-03-14 | © 2011 Apple Inc. All Rights Reserved.

97

Creating Charts and Graphs Interactive Data Visualization and Animation

<P> Increase or decrease the frequencies and increment the phase of the component sine waves to see how they add together.

</body> </html>

2011-03-14 | © 2011 Apple Inc. All Rights Reserved.

98

Animating the Canvas

You can use canvas for animations as well as static images. Animation is a series of graphic images shown one after another, the same way that video is a series of still images. If the sequential images are similar to one another, and the time between images is short, the eye is fooled into seeing continuous motion. You can easily use the canvas element to produce images that differ from each other slightly, and the canvas element is designed to support smooth, rapid changes between images. The JavaScript methods setTimeout() and setInterval() can be used to redraw the canvas at precise repeating intervals.

The Animation Sequence
Animation involves repeatedly clearing the canvas and drawing an image on it. To create smooth animations, you need to minimize the time between clearing the canvas and completing the new drawing, and you need to keep the changes relatively small from image to image. You can use a sequence of predrawn images to change the appearance of some elements from frame to frame, substituting one image for another. You can also change the rotation, scale, or position of a predrawn image, or of a path or shape. For smooth animation, use the following sequence:
1. 2. 3. 4.

Model—Calculate small changes in image substitution, position, rotation, color, or scale. Clear—Clear part or all of the canvas. Draw—Draw any images, using the precalculated values. Stroke or fill any paths, shapes, or strings. Repeat steps 1-3.

If you have an opaque background image or shape that fills the canvas, or the part of the canvas you are animating, you can skip step 2. Drawing the background has the effect of “clearing” the canvas to your background. As with static images, you should draw the elements in order, from backmost to frontmost, so your foreground images are superimposed on any background.

2011-03-14 | © 2011 Apple Inc. All Rights Reserved.

99

Animating the Canvas Animation Timing

When using predrawn images in the foreground, use SVG images or PNG images on transparent backgrounds, to allow automatic alpha-channel compositing. When changing the scale or rotation of an element, save the context by calling save(), change the scale or rotation of the canvas, translating the canvas as needed, draw the element, then restore the context by calling restore(). Use setTimeout() for animations that repeat conditionally, and setInterval() for animations that repeat indefinitely. For examples, see “A Simple Animation” (page 102) and “Intermittent Animation” (page 107).

Animation Timing
Animation is smoothest when you make the changes between frames as incremental as possible, and the time between frames as short as possible. Try not to move objects more than two or three pixels per frame. This means keeping incremental values such as gravity or thrust quite small, to prevent them from accumulating and resulting in jerky movement. Try to keep changes in rotation down to 0.1 radians per frame. Less is better. Changes in scale should be kept to 0.2 or less per frame. If making small changes results in slow animation, you can decrease the time between cycles. A timeout value of 40 ms results in a refresh rate of 25 fps. This is a good minimum refresh rate—the same as PAL television and a touch faster than cinema’s 24 fps. A timeout value of 20 ms yields a frame rate of 50 fps, which should be fast enough for any animation. Important Be careful when using small timeout values with setInterval(). If it takes longer to execute your animation than the timeout value, operations begin to accumulate on the command stack—your animation will gradually slow down and eventually freeze. Be aware that it takes code longer to execute on handheld devices than it does on a desktop system, so leave a margin for error and test your website on all the devices it is targeted for. You can see exactly how long your animation function takes to execute on the desktop by creating a JavaScript profile. To profile your script, follow these steps.
1. 2.

Enable the Developer menu in Safari preferences. Choose Develop > Show Web Inspector.

2011-03-14 | © 2011 Apple Inc. All Rights Reserved.

100

If the values are close. you may have a problem on handheld devices. Figure 11-1 5. With your website loaded and your script running.) If your animation function takes longer to execute than the timeout value. 6. 101 . Divide the total by the number of calls to get the timing for a single call. The number of calls is shown in the Calls column. Toggle the view from % to ms. with none of its subsidiary calls. 2011-03-14 | © 2011 Apple Inc. and reveal the operations to find your animate() function. Click the Profiles icon and enable profiling when prompted. 4. you have a bug. (The Average column shows the average for the animate() function alone. 7. Choose Tree view.Animating the Canvas Animation Timing 3. and click it again. wait a second. You should see a profile similar to the one shown in Figure 11-1. Animation profile The total time spent in your animate() function and all its subsidiary calls is shown in the Total column. Click the icon of the profile. click the record button (the black circle on the lower left). This generates a profile. All Rights Reserved.

Using setTimeout() can result in uneven timing. generates a continuous animation of a bouncing soccer ball. One way to avoid timing problems is to use setTimeout() instead of setInterval(). however. This guarantees that your animation function is completed before being called again. see Safari Developer Tools Guide . Figure 11-2 Animated soccer ball The soccer ball rotates as it falls. and set the timeout as the last step in your animate() function. and bounces from the bottom and sides of the canvas. 102 . Using setInterval() guarantees the same frame rate for all code branches. 2011-03-14 | © 2011 Apple Inc. The canvas has a clear background. but you must allow enough time for the longest branch to execute on the slowest device. All Rights Reserved. allowing the webpage background to show through. A Simple Animation Listing 11-1 (page 103). The rotation direction changes when the ball bounces off the side.Animating the Canvas A Simple Animation Note To learn more about debugging and profiling JavaScript using Safari’s built-in tools. as shown in Figure 11-2 (page 102). The canvas element is positioned using CSS to overlap the heading in the <h2> tag. so the ball covers part of the heading at times. especially if your modeling code has long and short branches. on all devices.

an x-increment is added to the x position. and the image of the ball is drawn at centerOffset. var direc. the canvas is translated to the ball’s x.0. so the ball realistically moves faster as it drops and moves slower as it bounces up.Animating the Canvas A Simple Animation During the model() step. var centerOffset = -75. var bottom=325. This pixel offset is stored in a variable named centerOffset. var rot = 0. 103 . There is a -75 pixel offset from the upper-left corner of the ball image to the center of the ball. var yVec. var ball. var interval. var y. and reverses the x or y increment if needed. instead of a canned series of images. var x. Because the animation is based on a model.y-coordinate and rotated. 2011-03-14 | © 2011 Apple Inc. it does not simply repeat—the ball follows a slightly different path each time it bounces. var xVec. The model() step also checks for a bounce condition. centerOffset to put the ball’s center at 0.0. var right=525. A gravity constant is added to the y-increment. During the draw() step. All Rights Reserved. The ball is then drawn with its center at 0. var ctx. var gravity = 1. eventually stopping and falling again. var left=75. and a y-increment is added to the y position. Listing 11-1 Creating a simple animation <html> <head> <title>Simple Animation</title> <script type="text/javascript"> var can.

y = 75.getElementById("can"). // set animation to repeat every 40 ms interval = setInterval("animate()". ctx = can. // initialize position. } function animate() { model(). bounceIf().stroke(). // draw lines for the ball to bounce off of ctx. speed. ctx.5. y = y + yVec. x = x + xVec. ctx. draw().strokeStyle="black".40). 104 . can = document.0) ctx.can. ctx. yVec = 0. } 2011-03-14 | © 2011 Apple Inc.bottom+ 75). // clear canvas except for lines at edge ctx.width -1 .1 * direc. yVec = yVec + gravity. spin direction x = 98.clearRect(0.Animating the Canvas A Simple Animation function init() { ball = document.lineTo(600. xVec = 5.getContext("2d").can.getElementById("ball").height -1). direc = 1. } function model() { rot = rot + .moveTo(0.0.lineTo(600. All Rights Reserved.bottom + 75).

direc = -1 * direc.translate(x.com/qt4web/soccerball1. ctx. ctx. centerOffset.top:-50"> </canvas> </body> </html> 2011-03-14 | © 2011 Apple Inc.restore(). yVec = -1 * yVec . } } function draw() { ctx.Animating the Canvas A Simple Animation function bounceIf() { if (y >= bottom) { y = bottom.drawImage(ball. } </script> </head> <body onload="init()" style="background-color:#e0e0e0"> <h2>Simple Animation</h2> <img id="ball" src="http://homepage. All Rights Reserved.gravity } if (x >= right || x <= left) { xVec = -1 * xVec.centerOffset). ctx.save(). ctx.png" style="display:none"> <canvas id="can" height="400" width="600" style="position:relative. 105 .rotate(rot).mac.y).

function makeGradient() { grad = ctx.0.Animating the Canvas Adding a Gradient Adding a Gradient You can overlay runtime-generated shapes on predrawn images.0.255. function draw() { ctx.255. All Rights Reserved.save(). ctx.y).0.addColorStop(0. Let’s take the previous example and overlay the soccer ball with a shape.75). Figure 11-3 Animation with gradient fill The following snippet defines a radial gradient the size of the soccer ball that goes from white at 80% opacity at the center to blue at 30% opacity at the outer edge.10. 'rgba(0. 'rgba(255. filled with a gradient.translate(x.createRadialGradient(0. This allows you to make the ball any color you like and add a lighting effect at the same time. var grad.0. grad. 106 . var twoPi = Math. as shown in Figure 11-3.addColorStop(1.0.255. grad.PI * 2. 2011-03-14 | © 2011 Apple Inc. } This next snippet redefines draw() to draw a circle filled with the gradient on top of the soccer ball (the radius of the circle is 73 pixels).8)').0.3)').

ctx. 73. paste in the first snippet. 0. and replace the draw() function with the second snippet. } To add the gradient-filled overlay to the example in Listing 11-1 (page 103). The output is shown in Figure 11-4. The animation is achieved by substituting images of a butterfly with wings in different positions. twoPi). ctx.rotate(rot).centerOffset).restore(). Figure 11-4 Flapping butterfly 2011-03-14 | © 2011 Apple Inc. add a call to makeGradient() in the init() function.fill(). ctx. centerOffset.drawImage(ball. ctx. ctx.0. ctx.arc(0. The example uses shadows and rotation to make the animation more realistic.fillStyle = grad. 107 . Intermittent Animation Listing 11-2 animates a butterfly whose wings flap intermittently.beginPath().closePath. ctx. All Rights Reserved.Animating the Canvas Intermittent Animation ctx.

random interval is used between flaps.80. sprite[1]=document. var ctx. // wait 3 sec. var centerY = -148.3)".getContext("2d"). A short timeout of 40 ms is used during a flap. Listing 11-2 Creating an intermittent animation <html><head> <title>Butterfly</title> <script type="text/javascript"> var can. ctx. var sprite = new Array().Animating the Canvas Intermittent Animation Because the animation is intermittent. offset. sprite[2]=document. var rot = 0. var centerX = -200.getElementById("b1"). // draw butterfly draw(). ctx=can.shadowBlur="5". function init() { can=document. // get images into array sprite[0]=document.shadowYOffsetY="20". var counter=0.getElementById("b3"). ctx. All Rights Reserved. // set shadow color.getElementById("b4"). setTimeout() is used instead of setInterval().shadowColor="rgba(80.shadowOffsetX="10".getElementById("can"). ctx. 3000). } 2011-03-14 | © 2011 Apple Inc.80. then begin animation var t= setTimeout("flap()". sprite[3]=document. and a longer. 108 ..getElementById("b2"). blur ctx.

can.centerY). ctx.centerX.height / 2).0.width. } function draw() { ctx.01. } draw(). if (counter==4) { counter=0.random() * 1500. All Rights Reserved. can.height). rot=rot+. wait). } </script> </head> <body onload="init()"> <h1>Butterfly</h1> <P>Why are they called butterflies?</P> <P>Shouldn't they be flutter-bys?</P> <h2>but-ter-fly</h2> <P><i>--noun</i></p> <ol> <li>any of numerous diurnal insects of the order Lepidoptera. </li> 2011-03-14 | © 2011 Apple Inc.Animating the Canvas Intermittent Animation function flap() { var wait = 40. characterized by clubbed antennae. often conspicuously marked wings. ctx. a slender body. 109 . ctx.can.save().rotate(rot). ctx. and large. ctx.restore(). setTimeout("flap()".width / 2. counter++.clearRect(0. wait=wait + Math.translate(can. broad.drawImage(sprite[counter].

( used with a plural verb ) Informal . The first copy has scrolled off the screen. start by creating an image that is wider than the canvas.png" > <canvas id="can" height="450" width="450" style="position:absolute.left:0. 3. a queasy feeling. etc.png" > <img id="b3" style="display:none" src="http://homepage. This draws a new copy with the left edge flush with the old copy’s right edge.png" > <img id="b2" style="display:none" src="http://homepage.mac. 2011-03-14 | © 2011 Apple Inc.com/qt4web/butterfly/butterfly1. 2.mac. This pans the image to the left. To create a panning background.png" > <img id="b4" style="display:none" src="http://homepage. excitement. All Rights Reserved. 110 . add the image width to the x-coordinate.Animating the Canvas Panning Background <li>a person who flits aimlessly from one interest or group to another: a social butterfly. </li> </ol> <img id="b1" style="display:none" src="http://homepage. When the x-coordinate is less than the image width minus the canvas width.com/qt4web/butterfly/butterfly3. so reset the x-coordinate and start over.top:225"> </canvas> </body> </html> Panning Background Sometimes you want to animate the background. draw a second copy of the image at x + the image width. Draw the image at an ever-decreasing x-coordinate.com/qt4web/butterfly/butterfly4.mac.mac. When the x-coordinate is less than the negative image width. and whose left edge and right edge are very similar. as from nervousness. Then follow these steps: 1. </li> <li>butterflies.com/qt4web/butterfly/butterfly2.

var x. can = document. var imgWidth = 1498. var back. 2011-03-14 | © 2011 Apple Inc.getElementById("back"). All Rights Reserved. var ctx.Animating the Canvas Panning Background Simple Panning Background The example in Listing 11-3 creates a background image that pans smoothly across the canvas. as illustrated in Figure 11-5. 111 . function init() { back = document.getElementById("can"). Figure 11-5 Panning background Listing 11-3 Adding a panning background <html> <head> <title>Simple Panning Background</title> <script type="text/javascript"> var can.

} function animate() { x = x . } </script> </head> <body onload="init()" > <h2>Simple Panning Background</h2> <canvas id="can" height="500" width="500" > </canvas> <img id="back" style="display:none" src="http://homepage.com/qt4web/landscape.Animating the Canvas Panning Background ctx = can.png"> </body> </html> 2011-03-14 | © 2011 Apple Inc. drawBack(). } function drawBack() { ctx. x.width)) ctx. x + imgWidth. setTimeout("animate()". animate().25).0). if (x<= -1 * imgWidth) x = x + imgWidth. x = 0.mac.getContext("2d").drawImage(back. 112 .1.0). if (x < -1 * (imgWidth .drawImage(back. All Rights Reserved.can.

The example in Listing 11-4 creates a more realistic panning background by dividing the background image into three parts—the sky. the landscape. To support three layers of background. the x increment. var back = new Array. A line of text is added to illustrate an image floating over a panning background. All Rights Reserved.Animating the Canvas Panning Background Layered Panning Background A simple panning background tends to have a flat look to it. var ctx. and the x and y coordinates are made into arrays. Figure 11-6 Layered background Listing 11-4 Adding a layered panning background <html> <head> <title>Layered Panning Background</title> <script type="text/javascript"> var can. as shown in Figure 11-6. 113 . and the guardrail in the foreground—and panning each part at a different speed. and the background drawing function is made into a loop that cycles through all three layers. the image. because we expect nearby things to move faster when panning. 2011-03-14 | © 2011 Apple Inc.

if (x[i] < -1 * (imgWidth .can. animate().getElementById("sky").i++) { x[i] = x[i] . x[i].width)) ctx. 376].3. } } </script> </head> <body onload="init()" > <canvas id="can" height="500" width="600" > </canvas> 2011-03-14 | © 2011 Apple Inc.getContext("2d"). 4]. ctx.y[i]). 0].drawImage(back[i]. 114 .drawImage(back[i]. var y = [ 0.getElementById("back"). var imgWidth = 1498. if (x[i]<= -1 * imgWidth) x[i] = x[i] + imgWidth. ctx = can.getElementById("can"). back[2] = document. x[i] + imgWidth. } function drawBack() { for (i=0. 0.Animating the Canvas Panning Background var x = [0. All Rights Reserved.xIncr[i]. can = document.var xIncr = [0. -2.i<3. } function animate() { drawBack(). back[1] = document.getElementById("front"). setTimeout("animate()".25). 1.y[i]). function init() { back[0] = document.

color:#ffffff"> Grand Canyon Vacation </h2> <img id="sky" style="display:none" src="http://homepage. All Rights Reserved.com/qt4web/foreground.png"> <img id="front" style="display:none" src="http://homepage.Animating the Canvas Panning Background <h1 style="position:relative.com/qt4web/sky.png"> <img id="back" style="display:none" src="http://homepage. top:-300.com/qt4web/landscape2.mac. left: 125.png"> </body> </html> 2011-03-14 | © 2011 Apple Inc.mac. 115 .mac.

Figure 12-1 CSS bulletin board Listing 12-1 Adding a CSS background and border <html> <head> <title>Background Image and Border</title> 2011-03-14 | © 2011 Apple Inc. you can use WebKit transitions to smoothly animate changes in CSS properties.y.height) method clears a section of the canvas. you can use CSS styles to modify its position. so it does not interfere with image processing. A CSS background does not appear in the canvas bitmap.Modifying the Canvas with CSS Because the canvas is an HTML element. without redrawing the background image. 116 . as illustrated in Figure 12-1. Because the canvas can have a transparent background. Assigning a Border and Background The example in Listing 12-1 uses CSS to assign a background image and a border to the canvas element. All Rights Reserved. assign it a background color or image. allowing you to use a background image and clear small areas of the canvas quickly. add a border. you can use CSS to create animated graphics that roam freely across the webpage. revealing the CSS background. The clearRect(x. width. and so on. In Safari and other WebKit-based browsers.

All Rights Reserved.fillText("Notice:". ctx.25. 2011-03-14 | © 2011 Apple Inc. ctx. ctx.fillStyle = "black". click.Modifying the Canvas with CSS A Pop-Up Canvas—Animating Position and Opacity <style> canvas { background-image:url('http://homepage.120. border: 10px inset brown. or rollover event on another webpage element.fillRect(25. ctx.getContext("2d"). ctx.fillStyle = "white".getElementById("can"). } </script> </head> <body onload="init()"> <canvas id="can" height="200" width="400"> </canvas> </body> </html> A Pop-Up Canvas—Animating Position and Opacity You can make the canvas element into a pop-up that responds to a hover.mac.55). } </style> <script type="text/javascript"> function init() { var can = document.png').100).font = "24pt Helvetica". 117 . 30.com/qt4web/cork. var ctx = can.

or opacity properties to make the canvas appear at the appropriate place on the page.png'). Touching or clicking and holding a particular text element on the page changes the class name of the canvas. left. A mouseup event anywhere on the page changes the canvas’s class name to hide it again. and animates a bouncing ball. any changes are automatically animated in Safari and other WebKit-based browsers. If you set the position and opacity properties as -webkit-transition properties.mac. When the event occurs that triggers the pop-up. Figure 12-2 Animating a pop-up canvas Listing 12-2 Making the canvas a pop-up <html> <head> <title>Pop-up Canvas</title> <meta name="viewport" content="width=device-width″ /> <style> canvas { background-image:url('http://homepage. The CSS class definition makes the canvas visible and positions it on the page. The example in Listing 12-2 creates a canvas and uses CSS to position it offscreen and make it transparent by default. so the canvas fades in and out over 1 second in Safari. 118 .com/qt4web/cork. In non-WebKit-based browsers.Modifying the Canvas with CSS A Pop-Up Canvas—Animating Position and Opacity Begin by using CSS to position the canvas element offscreen or make it transparent. just for fun. The canvas is given a CSS background image and a border. All CSS properties are set as webkit-transition properties for the canvas element. the changes take place immediately and the canvas simply appears in the specified position. use CSS to change the canvas top. All Rights Reserved. 2011-03-14 | © 2011 Apple Inc.

animate(). } </style> <script type="text/javascript"> var can. } .getContext("2d"). 'red'). } 2011-03-14 | © 2011 Apple Inc. position:absolute. 119 .Modifying the Canvas with CSS A Pop-Up Canvas—Animating Position and Opacity border: 10px inset brown. } . ctx. opacity: 0. 'brown'). grad. All Rights Reserved.getElementById("can"). grad.10.can-pop { top: 120. var x = 25. 0. -webkit-transition: all 1s.addColorStop(1.fillStyle=grad.190).createLinearGradient(0. left: 200.can-hide { top: -225.addColorStop(0. var xdir = 1. function init() { can = document. opacity:1. top: -225. var grad = ctx. var ydir = 1. ctx = can. var ctx. var y = 25.

setTimeout("animate()". All Rights Reserved. 0. } function unpop() { can. ctx.className="can-pop".200). 400.Modifying the Canvas with CSS A Pop-Up Canvas—Animating Position and Opacity function animate() { x = x + xdir.arc(x. ctx. if (x < 25) xdir = 1.fill().PI). 25). ctx. ctx.0.closePath(). if (x > 375) xdir = -1.className="can-hide".2 * Math. } </script> </head> <body onload="init()" onmouseup="unpop()"> <canvas class="can-hide" id="can" height="200" width="400"> </canvas> <h1> Click to view: <h1> 2011-03-14 | © 2011 Apple Inc. if (y < 25) ydir = 1. ctx.clearRect(0. y = y + ydir. 120 . 25.y. if (y > 175) ydir = -1.beginPath(). } function pop() { can.

All Rights Reserved. and pop-ups notwithstanding. The onclick="void()" attribute is added to alert iOS that the element should be considered clickable. so a mouseup or touchend event anywhere on the page releases the pop-up. Free Range Canvas The static webpage is a paradigm we’re all familiar with. Because the canvas element can have a transparent background. covering or casting shadows on other elements. so it responds to either a click or a touch. Video.[x] Bulletin Board </h1> </body> </html> Note The HTML <h1> element is set to respond to either a mousedown or a touchstart event. and they tend to stay in boxes. 2011-03-14 | © 2011 Apple Inc. it offers a way to visually break the static boxes paradigm. animated banners.Modifying the Canvas with CSS Free Range Canvas <h1 onmousedown="pop()" ontouchstart="pop()" onclick="void()"> &nbsp. animated images on the canvas appear to range freely about the page. and should act as a target for touch events. 121 . and because it supports alpha channel compositing on the fly. things are laid out in boxes. When the canvas itself is animated using CSS. The onmouseup event is registered on the body element.

Modifying the Canvas with CSS Free Range Canvas The example in Listing 12-3 (page 122) creates a simple animated butterfly. var ctx. Figure 12-3 Butterfly Listing 12-3 Making a free-roaming canvas <html> <head> <title>Butterfly</title> <script type="text/javascript"> var can. All Rights Reserved. var canY = 225. 2011-03-14 | © 2011 Apple Inc. then moves the canvas using CSS to allow the butterfly to wander across the page. var centerY = -127 var canX = 0. as shown in Figure 12-3. var centerX = -176. var sprite = new Array(). var counter=0. 122 . var rot = 0.

// get images into array sprite[0]=document. } function flap() { // flap wings four times quickly. 3000).80. sprite[3]=document.shadowOffsetX="10". move canvas.shadowBlur="5". // set shadow color.80. rot=rot+.01. blur ctx. sprite[1]=document.shadowColor="rgba(80. // draw butterfly draw(). // wait 3 sec. counter = counter + 1.getElementById("can").getElementById("b4").5sec var wait = 40.getElementById("b3"). } // draw butterfly.getContext("2d"). moveCan(). if (counter==4) { counter=0. } 2011-03-14 | © 2011 Apple Inc.getElementById("b2").. repeat draw().3)". sprite[2]=document.random() * 1500. All Rights Reserved.getElementById("b1"). ctx=can. then begin animation var t= setTimeout("flap()". ctx. wait).shadowYOffsetY="20". ctx. ctx. setTimeout("flap()". then wait randomly up to 1.Modifying the Canvas with CSS Free Range Canvas function init() { can=document. 123 . wait=wait + Math. offset.

if (canX > 400) canX = 400.can.Modifying the Canvas with CSS Free Range Canvas function draw() { // draw rotated: translate to x.centerY). } </script> </head> <body onload="init()" onmousedown="moveToMouse()"> <H1>Butterfly</h1> <P>Why are they called butterflies?</P> <P>Shouldn't they be flutter-bys?</P> <h2>but-ter-fly</h2> <P><i>--noun</i></p> <ol> 2011-03-14 | © 2011 Apple Inc.clearRect(0.centerX. 124 .width / 2.height).0. ctx.left = canX. rotate.style.y. ctx.restore().drawImage(sprite[counter].width. draw at 0.0.style.2. canY = canY .translate(can. can.top = canY. offset to center ctx.save(). can. ctx. if (canY < -80) canY = -80. } function moveCan() { canX = canX + 1. // use CSS style to move canvas can. ctx. ctx. All Rights Reserved.rotate(rot).height / 2).can.

com/qt4web/butterfly/butterfly3.mac. broad. ( used with a plural verb ) Informal . a slender body. 125 . </li> <li>a person who flits aimlessly from one interest or group to another: a social butterfly. characterized by clubbed antennae.Modifying the Canvas with CSS Free Range Canvas <li>any of numerous diurnal insects of the order Lepidoptera.png" > <img id="b4" style="display:none" src="http://homepage.png" > <canvas id="can" height="380" width="380" style="position:absolute.mac.png" > <img id="b3" style="display:none" src="http://homepage.left:0.mac. a queasy feeling.png" > <img id="b2" style="display:none" src="http://homepage. often conspicuously marked wings. All Rights Reserved. and large.com/qt4web/butterfly/butterfly2.com/qt4web/butterfly/butterfly1. excitement. </li> </ol> <img id="b1" style="display:none" src="http://homepage. </li> <li>butterflies.mac.com/qt4web/butterfly/butterfly4. etc.top:225"> </canvas> </body> </html> 2011-03-14 | © 2011 Apple Inc. as from nervousness.

This chapter covers four input variants. Text input fields bring up the soft keyboard on iOS-based devices. try setting a smaller viewport or a larger initial scale using the <meta> tag. 126 . or choose a different kind of input. or making the button font larger using the CSS font style. or choose a different kind of input. This section shows you how to create canvas webpages that respond equally well to both mouse and touch input. again covering the lower half of the screen. Make sure the relevant parts of the canvas aren’t obscured by the keyboard. Because the canvas element responds to JavaScript. it can be interacted with by either mouse or touch. 2011-03-14 | © 2011 Apple Inc. ● Control using standard HTML inputs elsewhere on the page Superimposing standard inputs on the canvas Responding to mouse and touch events on the canvas generally Creating custom controls on the canvas ● ● ● Using Standard Inputs with Canvas There are a few things to bear in mind when using standard HTML inputs with canvas. Make sure the relevant parts of the canvas aren’t obscured by the picker. All Rights Reserved. Button inputs with default settings tend to be quite small on iPhone and iPod touch. Selection input fields with alternates bring up the rotary picker on iPhone and iPod touch. To make buttons easier to find with a finger. covering the lower half of the screen.Adding Mouse and Touch Controls to Canvas Because the canvas element works both on the desktop and in iOS. you can include controls for the canvas anywhere on the page.

The desktop version is illustrated in Figure 13-1. All Rights Reserved. 127 . var n = 0. the initial scale to 2. var hun. and the input font to larger and bold.zoom in for iOS-based devices --> <meta name="viewport" content="width=300" /> <meta name="viewport" content="initial-scale=2" /> <style> input { font: larger bold. Figure 13-1 Big buttons Listing 13-1 Make big buttons <html> <head> <title>Big Buttons</title> <!-. 2011-03-14 | © 2011 Apple Inc. resulting in large.Adding Mouse and Touch Controls to Canvas Using Standard Inputs with Canvas The example in Listing 13-1 sets the viewport width to 300. var ctx. easy-to-push buttons on iPhone and iPod touch. } </style> <script type="text/javascript"> var can.

can.font="24pt Helvetica". ctx. ctx.0.textBaseline="middle".value.getElementById("hundred").height / 2). } function decr() { n--. showN().width. 128 .clearRect(0. showN(). } function incr() { n++. ctx. } function showN() { ctx.can. 99). All Rights Reserved. ctx.textAlign="center".getContext("2d"). showN(). } function setHundred() { n = hun. } </script> </head> <body onload="init()"> 2011-03-14 | © 2011 Apple Inc. can.64)". can.width /2.Adding Mouse and Touch Controls to Canvas Using Standard Inputs with Canvas function init() { can = document. ctx = can.getElementById("can").height. showN(). hun = document.fillText(n.fillStyle="rgb(64.255. ctx.

The inputs cover any graphics or animation drawn on the canvas. All Rights Reserved. The example in Listing 13-2 positions a pair of buttons and a selector on top of the canvas." onclick="decr()"> <select id="hundred" onchange="setHundred()"> <option value=0> -.</option> <option value=100> 100 </option> <option value=-100> -100 </option> </select> </body> </html> Using Standard Inputs on Canvas You can superimpose standard HTML inputs—or custom inputs—on the canvas simply by declaring the inputs after the <canvas> tag and using CSS to position the inputs on top of the canvas. 129 . as illustrated in Figure 13-2.Adding Mouse and Touch Controls to Canvas Using Standard Inputs on Canvas <canvas id="can" height="100" width="100" style="background-color:black"> </canvas> <br> <input type="button" value=" + " onclick="incr()"> <input type="button" value=" . Figure 13-2 Controls on canvas 2011-03-14 | © 2011 Apple Inc.

bright green text ctx. showN(). All Rights Reserved.2). ctx.Adding Mouse and Touch Controls to Canvas Using Standard Inputs on Canvas Listing 13-2 Putting controls on canvas <html> <head> <title>Controls on Canvas</title> <!-.textAlign="center". function init() { can = document. var hun.getElementById("can").width /2.can.fillText(n. can. 2011-03-14 | © 2011 Apple Inc. ctx. can.64)". hun = document. var ctx.255. can. // draw text at center. ctx.textBaseline="middle". ctx = can.getContext("2d"). max length to fit on canvas ctx.0.width.width .fill iPhone screen with canvas --> <meta name="viewport" content="width=200" /> <meta name="viewport" content="initial-scale=2" /> <script type="text/javascript"> var can. showN(). ctx. var n = 0.getElementById("hundred"). centered. 130 .fillStyle="rgb(64.clearRect(0.height). } function showN() { // large. } function incr() { n++.font="24pt Helvetica".height / 2. can.

</option> <option value=100> 100 </option> <option value=-100> -100 </option> </select> </div> </body> </html> 2011-03-14 | © 2011 Apple Inc.float inputs over canvas --> <div style="position:relative. showN().give canvas rounded corners--> <canvas id="can" height="200" width="200" style= "background-color:black. border-radius: 25 px. All Rights Reserved. } </script> </head> <body onload="init()"> <!-. showN().Adding Mouse and Touch Controls to Canvas Using Standard Inputs on Canvas } function decr() { n--."> </canvas> <br> <!-. top:-50. } function setHundred() { n = hun." onclick="decr()"> <select id="hundred" onchange="setHundred()"> <option value=0> -.value. 131 . left:25"> <input type="button" value=" + " onclick="incr()"> <input type="button" value=" .

All Rights Reserved. but when the finger is lifted off the screen. and install event listeners on the body element for mouseup events.Adding Mouse and Touch Controls to Canvas Responding to Mouse and Touch Events on Canvas Responding to Mouse and Touch Events on Canvas For some applications. To obtain the mouse or touch coordinates in terms of the canvas. prevent the default panning behavior by adding preventDefault() to your touchstart event handler. By default. dragging a finger in iOS pans the browser window. as shown in Figure 13-3. The results are displayed on the canvas. Similarly. but listen for touchcancel events on the HTML body.y position and state (up or down) of the mouse or touch. and draws a white cursor at that position on the canvas. get the pageX and pageY properties. or touchstart and touchend. To allow touch to flow smoothly over the canvas on iOS. Install event listeners on the canvas element for mousedown or click events. there is no touch to track. listen for touchstart and touchend events on the canvas. The example then shows the current x. then subtract the canvas’s offsetLeft and offsetTop properties. in case a mouse event begins on the canvas and ends off the canvas. 132 . Figure 13-3 Tracking events on canvas The example listens for mousemove or touchmove events on the canvas to track the mouse or finger position on the canvas. displaying the canvas coordinates and whether the mouse button or finger is down. 2011-03-14 | © 2011 Apple Inc. Note that the mouse is still tracked when the mouse button is released. you don’t need a specific input object—just a way to respond to mouse and touch events on the canvas as a whole. Tracking a Single Touch The example in Listing 13-3 tracks mouse and touch movements that originate on the canvas. The example also listens for mousedown and mouseup. to determine if the mouse button or finger is down.

} function mouseUp() { mouseIsDown = 0.addEventListener("mousemove".Adding Mouse and Touch Controls to Canvas Responding to Mouse and Touch Events on Canvas Only a single touch event is tracked. mouseUp. can.fill iPhone screen with canvas --> <meta name="viewport" content="width=400" /> <title>Tracking Mouse and Touch Events on Canvas</title> <script type="text/javascript"> var can. document. See “Tracking Multiple Touches and Testing for Hits” (page 135) for details. can.addEventListener("mousedown". false). touchUp. To obtain all the touch events that begin on the canvas. false). iterate through the event’s targetTouches array. All Rights Reserved. can. var canY. var canX. document. mouseXY.body. false). Listing 13-3 Track mouse and touch events <html> <head> <!-. false).addEventListener("touchcancel".addEventListener("touchstart". additional simultaneous touches are ignored. mouseXY(). var ctx. can.addEventListener("touchmove". can.addEventListener("touchend". true).getElementById("can"). false). touchUp. var mouseIsDown = 0.body. ctx = can. false). function init() { can = document. touchXY.addEventListener("mouseup". 133 . touchDown. mouseDown. 2011-03-14 | © 2011 Apple Inc.getContext("2d").

pageY . canX = e. canY = e. } function mouseXY(e) { if (!e) var e = event.offsetLeft. so just show state showPos(). } function showPos() { 2011-03-14 | © 2011 Apple Inc.offsetTop. } function touchDown() { mouseIsDown = 1.can.offsetLeft. touchXY().can. } function mouseDown() { mouseIsDown = 1.offsetTop.pageX . 134 . canY = e. showPos().targetTouches[0].targetTouches[0]. // no touch to track. e. mouseXY().pageY .can. } function touchXY(e) { if (!e) var e = event.Adding Mouse and Touch Controls to Canvas Responding to Mouse and Touch Events on Canvas } function touchUp() { mouseIsDown = 0. All Rights Reserved.pageX .preventDefault(). canX = e. showPos().can.

Clicking the mouse on a bubble.64)". pops the bubble. bright green text ctx. 135 . allowing the user to pop up to four bubbles at a time on iOS-based devices. can.can.0. // plot cursor ctx. 2011-03-14 | © 2011 Apple Inc. max length to fit on canvas ctx. This example tracks up to four simultaneous touch events.width /2.font="24pt Helvetica".10). ctx. var str = canX + ". can. if (mouseIsDown) str = str + " down". as illustrated in Figure 13-4. centered. } </script> </head> <body onload="init()"> <canvas id="can" height="200" width="300" style= </canvas> "background-color:black"> </body> </html> Tracking Multiple Touches and Testing for Hits The example in Listing 13-4 draws an endless series of descending red bubbles on the canvas. if (!mouseIsDown) str = str + " up". ctx.fillText(str. ctx.width . " + canY.10).height / 2.textAlign="center".textBaseline="middle".clearRect(0.fillRect(canX -5.fillStyle="white".255. can.Adding Mouse and Touch Controls to Canvas Responding to Mouse and Touch Events on Canvas // large. // draw text at center.fillStyle="rgb(64. All Rights Reserved. 10.width. ctx. can.height).canY -5. or touching a bubble with a finger on iOS. ctx.

touchDown. mouseXY. var bubble = [].addEventListener("mousemove". 2011-03-14 | © 2011 Apple Inc.getElementById("can").addEventListener("touchend".fill iPhone screen with canvas --> <meta name="viewport" content="width=400" /> <title>Pop the Bubbles</title> <script type="text/javascript"> var can. can. var canY = []. can. ctx = can. The length of the touches array is stored in a global variable. ends. Figure 13-4 Bubbles Listing 13-4 Track multiple touches <html> <head> <!-. can. and the variable is updated whenever a touch starts. var canX = []. var len = 0. var ctx. touchUp. false). All Rights Reserved. var mouseIsDown = 0.addEventListener("touchstart". false). 136 . can.getContext("2d"). mouseDown.addEventListener("mousedown". function init() { can = document. false).Adding Mouse and Touch Controls to Canvas Responding to Mouse and Touch Events on Canvas The example uses isPointInPath() to test each bubble against each touch. false). or is canceled.

mouseXY().can. touchXY().addEventListener("mouseup". } function mouseXY(e) { if (!e) e = event. document.can. } function touchUp(e) { if (!e) e = event.i<4. touchXY.body. false).length.addEventListener("touchcancel".Adding Mouse and Touch Controls to Canvas Responding to Mouse and Touch Events on Canvas can. } function mouseUp() { mouseIsDown = 0. len = 1.targetTouches. touchUp.body. mouseXY(). canY[0] = e.offsetLeft. 137 . false).i++) { bubble[i] = 0. for (i=0. canX[0] = e.pageX . len = e. mouseUp. } function touchDown() { mouseIsDown = 1.pageY . false). All Rights Reserved. } animate(). document.offsetTop. 2011-03-14 | © 2011 Apple Inc.addEventListener("touchmove". } function mouseDown() { mouseIsDown = 1.

height).i<4. len = e. canY[j]) && mouseIsDown) bubble[i]=-30. canY[i] = e.preventDefault().offsetLeft. can.strokeStyle = "red".length. ctx.targetTouches. e. can. i<len. for (i=0. } ctx.clearRect(0.targetTouches[i]. radius.offsetTop. 2011-03-14 | © 2011 Apple Inc.can.isPointInPath(canX[j]. } setTimeout("animate()". ctx.PI). i++) { canX[i] = e.height + 10) bubble[i] = -10.beginPath(). // test each extant touch to see if it is on the bubble for (j=0.j<len. var radius = 20. var y = bubble[i].stroke().closePath().j++) { if (ctx. 0. 2 * Math. var x = (i + 1) * 50.can.y. } } function animate() { ctx. 40). 138 .pageX .width. // create a path for each bubble for (i=0.targetTouches[i].i++) { bubble[i]++. if (bubble[i] >= can.pageY . ctx. ctx.arc(x. All Rights Reserved.0.Adding Mouse and Touch Controls to Canvas Responding to Mouse and Touch Events on Canvas } function touchXY(e) { if (!e) e = event.

and you can respond to touches on the control itself. That way. If your custom control is part of the canvas itself. and use the techniques described in “Responding to Mouse and Touch Events on Canvas” (page 132) to determine if the input is on your custom control.Adding Mouse and Touch Controls to Canvas Creating Custom Canvas Controls } </script> </head> <body onload="init()"> <canvas id="can" height="200" width="300" style= </canvas> "background-color:black"> </body> </html> Creating Custom Canvas Controls You can draw any kind of control you like on the canvas. and compare the coordinates of each touch to each control you draw. but there is a faster and better way to implement most custom controls. create an HTML div or img element and use CSS to style the element and position it on top of the canvas. 2011-03-14 | © 2011 Apple Inc. not a targetable element. All Rights Reserved. the control is just a graphic. A better approach is to build the control using HTML and CSS. or to track the mouse or finger coordinates at all. There’s no need to compare multiple touches with multiple controls. It can get complicated. Safari sorts the touches for you. then position the control on top of the canvas using CSS. you can make the control a target for mouse and touch events. Adding a Custom Button To add a custom button to the canvas. By creating the control as an element in HTML. you need to track all touches on the canvas. 139 . Consequently. Your control can still be a graphic image—or multiple images—alpha channels in images are automatically composited onto the underlying canvas.

such as onmousedown and ontouchstart. and add a listener function for touchend and mouseup events to detect the button being released. in case the user clicks your button. You might want to take an action when the button is pressed or when it is released. for example). or both. the button state changes and a different style is applied to the button. The example in Listing 13-5 creates a div element. Figure 13-5 Clicking a custom button The listener functions are added to the button and the HTML body using HTML attributes. All Rights Reserved. Add a state variable to track whether the button is pressed or released. When the touch ends or the mouse button is released. Similarly. The canvas also changes. then moves the mouse pointer off your button before releasing the mouse button. styles it as a button. in this case to blue. When the button is clicked or touched. Listing 13-5 Creating a custom button on canvas <html> <head> <title>Custom Button</title> <meta name="viewport" content="width=300" /> <meta name="viewport" content="initial-scale=2" /> <style> . The results are illustrated in Figure 13-5. 2011-03-14 | © 2011 Apple Inc.Adding Mouse and Touch Controls to Canvas Creating Custom Canvas Controls Add a listener function for touchstart and mousedown events to detect a custom button being pressed. 140 . in case the touch is canceled for some reason (such as an incoming phone call. and positions it on the canvas. Add a listener function for mouseup events to the page as a whole.myButton { position: relative. add a listener function for touchcancel events to the page as a whole. the button style reverts to the unpressed state and the canvas changes to black.

141 . All Rights Reserved.mybutton. } </style> <script type="text/javascript"> var can. left:10. 2011-03-14 | © 2011 Apple Inc. background-color: #808080. padding: 10 px. ctx = can. // change button style but1.pressed { border: 4 px outset black. // state variable tracks whether button is pressed var but1press.Adding Mouse and Touch Controls to Canvas Creating Custom Canvas Controls top:-60. border: 4 px outset #c0c0c0. but1press = 0. background-color: #e0e0e0. width:100 px. can = document.getElementById("can"). } . var ctx.getContext("2d"). function init() { but1 = document. border-radius: 18 px. } function press1() { // change state variable but1press = 1. text-align:center.className="myButton pressed".getElementById("but1"). var but1.

// do something on the canvas ctx.Adding Mouse and Touch Controls to Canvas Creating Custom Canvas Controls // do something on the canvas ctx.height). can. can.width.0.can. // if button is pressed. 142 . // revert button style but1. ctx.className="myButton". All Rights Reserved.0. } function release1() { // button 1 may or may not be pressed when mouse button comes up // or touch ends. ctx.height). release it and do something on the canvas if (but1press) { but1press = 0. } } </script> </head> <body onload="init()" onmouseup="release1()" ontouchcancel="release1()"> <canvas id="can" height="200" width="300" </canvas> <div id="but1" class="myButton" onmousedown="press1()" onmouseup="release1()" ontouchstart="press1()" ontouchend="release1()"> Click Me </div> </body> 2011-03-14 | © 2011 Apple Inc.width.fillRect(0.can.fillStyle="beige".fillRect(0.fillStyle="white".

To create a slider with a range of n1 to n2 in 100 steps. The example in Listing 13-6 creates a slider using three nested div elements. 2011-03-14 | © 2011 Apple Inc. To build a slider. Position the knob relative to the bar using CSS. but you can also use CSS to style a set of nested div elements to act as a slider. The knob value should be clamped at 0 and the bar width minus the knob width. Begin your touch event handlers with preventDefault() to allow the finger to drag the slider instead of scrolling the page. and are positioned relatively within the slider. The range of the slider can be anything. for a circular knob of radius 25. The knob value is the mouse or touch event’s pageX property. The two inner div elements are the bar and the knob. On iOS-based devices. The outermost div element is the slider and is positioned absolutely.Adding Mouse and Touch Controls to Canvas Creating Custom Canvas Controls </html> Adding a Slider A slider is a useful control for user input having a fixed range. minus the slider’s offsetLeft property. A circle of radius 25 meets these criteria and makes a comfortable target for a finger. The slider has a dragable range from 0 to the width of the bar. touchbegin. the minimum bar width is 150 pixels to allow 100 dragable steps while keeping the knob on the bar. A slider consists of a knob and a bar for the knob to slide on. listen for mousedown. and touchmove events on the slider element. All Rights Reserved. For example. but the number of dragable steps is constrained by the length of the bar. mousemove. you need to create a bar at least 100 pixels long—a single pixel is the minimum finger-controllable movement of the knob. for example. A slider can be a graphic image. using only text. so the mouse or finger drags the center of the knob. The knob should be positioned half its width to the left of the knob value. the minimum comfortable size for the knob on a slider is 44 x 44 pixels. Listen for mouseup events on the body element and track the mouse button state. 143 . minus the width of the knob. The bar div is styled into a horizontal line and the knob div is styled into a circle using CSS.

Adding Mouse and Touch Controls to Canvas Creating Custom Canvas Controls The knob is 52 pixels wide.25 to 0. } . 2011-03-14 | © 2011 Apple Inc. All Rights Reserved. background-color: beige.slider { position: absolute.75 in 100 steps. left:10. Safari redraws the knob automatically. top:10. by setting the offsetLeft property. border-radius: 25. The knob is repositioned using CSS. so as not to overload the event handlers and make them unresponsive. giving the slider a positional range of 0-100. Listing 13-6 Adding a slider using CSS <html> <head> <title>Custom Slider</title> <!-. The slider is used to scale a graphic from a size of 0.Fill the iOS screen /--> <meta name="viewport" content="width=400" /> <style> canvas { position:absolute. to demonstrate that the value range driven by the slider is not constrained by the positional range. border: 1 px solid #404040. The result is illustrated in Figure 13-6. 144 . including the border. and the bar is 152 pixels wide. Figure 13-6 Slider controlling image size The knob value is displayed and the image is redrawn in an independent animation loop.

var ctx. var slider. top:30. width: 152. border-radius: 25 px. height = 52.bar { position: relative. width:50 px. 145 . height: 50 px. var knobMid. background-color: #C0C0C0. border: 1 px solid #404040. All Rights Reserved. } .Adding Mouse and Touch Controls to Canvas Creating Custom Canvas Controls top: 115. left:0.knob { position: relative. 2011-03-14 | © 2011 Apple Inc. background-color: #404040. width: 152. var mouseIsDown. var knob. left:85. var image. } . } </style> <script type="text/javascript"> var can. height: 2 px.

ctx.offsetWidth / 2. } 2011-03-14 | © 2011 Apple Inc. 146 . knobMid = knob.getElementById("slider").scale(scale. setTimeout("showVal()". ctx.0. can. image = document.height .getElementById("image").font = "24pt Helvetica". mouseIsDown = 0.getContext("2d"). } function textInit() { ctx.width. showVal().fillStyle = "blue". knob = document. ctx. ctx.0). scale). ctx.textAlign = "center". ctx.restore().getElementById("can").drawImage(image. var scale= . textInit(). can.textBaseline = "bottom". ctx.width / 2.offsetLeft.25 + sliderVal / 200. ctx. ctx. margin = can.getElementById("knob").offsetLeft . ctx = can. } function showVal() { // value goes from 0 to slider-width minus knob width var sliderVal = knob.clearRect(0.1. can.height). 0.save().5). 25). can.fillText(sliderVal.Adding Mouse and Touch Controls to Canvas Creating Custom Canvas Controls function init() { slider = document. can = document. All Rights Reserved.

offsetWidth).pageX . don't scroll e.offsetWidth) { setKnob(touchX). 147 . mouseXY(). if (mouseX >= 0 && mouseX <= slider.preventDefault(). knobX = Math.left = knobX.touches[0]. knob.style. } 2011-03-14 | © 2011 Apple Inc.offsetWidth ) { setKnob(mouseX). } } function setKnob(x) { var knobX = x .offsetLeft. // slide. if (touchX >= 0 && touchX <= slider.knobMid. All Rights Reserved. knobX = Math.offsetLeft.slider. var touchX = e.offsetWidth .min(knobX. } function mouseXY(e) { if (mouseIsDown) { if (!e) var e = event. var mouseX = e. slider.max(knobX.pageX .slider.Adding Mouse and Touch Controls to Canvas Creating Custom Canvas Controls function mouseDown() { mouseIsDown = 1.knob. 0). } function mouseUp() { mouseIsDown = 0. } } } function touchXY(e) { if (!e) var e = event.

Adding Mouse and Touch Controls to Canvas Creating Custom Canvas Controls </script> </head> <body onload="init()" onmouseup="mouseUp()" > <canvas id="can" height="200" width="300"> </canvas> <div class="slider" id="slider" onmousedown="mouseDown()" onmousemove="mouseXY()" ontouchstart="touchXY()" ontouchmove="touchXY()"> <div class="bar"> </div> <div id="knob" class="knob" </div> <div> <img id="image" style="display:none" src="http://homepage.com/qt4web/butterfly/butterfly1. All Rights Reserved.mac.png" > </body> </html> 2011-03-14 | © 2011 Apple Inc. 148 .

the loop attribute for HTML5 audio is not supported on iOS. slide shows. If you need to have your audio track loop. then call the play() method to restart the sound. iOS supports playback of a single audio stream at a time when playing audio inline on a website. use the HTML5 <audio> element. To include audio in your canvas presentation. On iOS. it’s slightly more complicated. make the background music available in iTunes or another iOS app that can play music in the background while the user plays your game. and trigger the sound effects one by one from within your game. ● ● ● ● 2011-03-14 | © 2011 Apple Inc. controlled by the same JavaScript that controls your canvas animation. Adding HTML5 audio to your canvas presentation on the desktop is simple—just include an <audio> tag and use the JavaScript play() method. This section provides a brief summary of using HTML5 audio in the context of creating canvas-based rich media websites. You can’t programmatically load and play audio unless the user authorizes it by clicking a button. see Safari HTML5 Audio and Video Guide . Currently. paying for data by the megabyte. If your game includes background music as well as sound effects. Currently.Adding Sound to Canvas Animations Canvas provides the visual tools for creating animations. For a more in-depth discussion of HTML5 audio and video. All Rights Reserved. To play different sounds. follow these steps: ● Include an “Enable sound” button to initiate playback. Safari on iOS downloads audio only as a direct result of user action. and have it work on the desktop and iOS. Use a single HTML5 audio element for all your sounds. change the src property of the audio element. and games. listen for the ended event on the audio element. 149 . Because the user may be viewing your website over a cellular connection. but to complete a rich media experience you typically want to add sound as well. To add sound to a canvas-based animation or game. and because audio uses a lot of data.

audio format. The amount of time between the user pressing the play button and the start of the sound varies—file size. and network speed are all factors. Setting the audio element’s volume property on iOS has no effect. you can use the audio controller’s start button to start your animation as well. You can also pause your animation when the audio element’s pause or ended events are triggered. as illustrated in Figure 14-1. providing iOS compatibility. By installing an event listener on the audio element. 150 .Adding Sound to Canvas Animations Adding a Soundtrack Note There is no software volume control for HTML5 audio on iOS—the user controls the audio volume using the hardware volume control. If your presentation includes a “Start” button. The simplest way to add a soundtrack is to include the controls attribute in the <audio> tag. To synchronize your animation more closely with the start of the audio. The example in Listing 14-1 uses an <audio> tag with the controls attribute to add a user-initiated soundtrack to the canvas. The audio source can loop or be timed to match the canvas presentation. Adding a Soundtrack A soundtrack is a single audio source—such as music or a voice-over—that plays during your presentation. the same button can initiate the presentation and play the audio. You can use CSS to position the audio controller on the canvas if you like. create an audio element in your HTML and use the play() method to start it. and animates a gradient when the music is playing. reading the volume property always returns a value of 1 (full volume). Figure 14-1 Soundtrack Listing 14-1 Adding a simple soundtrack <html> <head> 2011-03-14 | © 2011 Apple Inc. To add a soundtrack. install an event listener on the audio element and begin your animation when the "playing" event is triggered. All Rights Reserved.

Adding Sound to Canvas Animations Adding a Soundtrack <title>Animated Gradient with Sound Track</title> <!-. audio. left:10.Fill the iOS screen /--> <meta name="viewport" content="width=400" /> <style> canvas { position:absolute. top: 195. } audio { position: absolute. function init() { audio = document. stop. } </style> <script type="text/javascript"> var can.addEventListener("ended". border-radius: 25. 2011-03-14 | © 2011 Apple Inc.getElementById("can"). stop. start. false).getElementById("audio"). var angle = 0. audio. ctx = can. All Rights Reserved. var ctx. 151 . can = document. var audio. audio. top:10. border: 1 px solid #404040. left:60. false).addEventListener("pause".getContext("2d").addEventListener("playing". var timer. false).

if (angle >= 6.1.Adding Sound to Canvas Animations Adding a Soundtrack } function start() { drawGradient().abs(Math.0). rgbCol).sin(angle))).PI. 2011-03-14 | © 2011 Apple Inc. var b = parseInt(255 * Math.can.height. var rgbCol = "rgb(" + r + ". // create gradient that goes from bottom to top of canvas var grad = ctx.abs(Math. var bAngle = gAngle + Math. // add color stop with new rgb colors grad. // create changing rgb color values that go from 0 to 255 var gAngle = angle + Math.PI / 2. 'black'). } function stop() { clearTimeout(timer). // fill canvas with gradient ctx." + g + ".addColorStop(0.save().addColorStop(1. var g = parseInt(255 * Math. // start gradient at black grad.2) angle = 0.abs(Math.createLinearGradient(0." + b + ")". } function drawGradient() { // increment angle slowly from 0 to 2 PI angle = angle + 0.sin(gAngle))). 152 . var r = parseInt(255 * Math. All Rights Reserved.sin(bAngle))). 0.

instead of just clearing the animation timeout in the "pause" and "ended" event handlers.com/qt4web/myAudio.width. so it’s important not to reset the timer without testing the play state. can.querySelector("audio").m4a" controls > </audio> </body> </html> Note that the previous example tests whether the audio is playing at the end of the animation cycle. ctx. The loop attribute is not currently supported for inline HTML5 audio on iOS. so to make your audio loop you need to install an event listener on the audio element—the event listener listens for the "ended" event and invokes the play() method to immediately restart the audio at the beginning.restore().height).fillStyle=grad. Looping Audio Sometimes sound isn’t tightly-coupled to the visual display—for example. 153 . looping audio may provide ambience to your site. } </script> </head> <body onload="init()"> <canvas id="can" height="200" width="300"> </canvas> <audio id="audio" src="http://homepage.fillRect(0. 2011-03-14 | © 2011 Apple Inc. but not be a requirement to enjoy the visuals.mac. can. ctx. 100).Adding Sound to Canvas Animations Looping Audio ctx. The audio events can occur while the animation cycle is in mid-execution. All Rights Reserved.0. // repeat while audio is not paused if (!document.paused) timer = setTimeout("drawGradient()".

The example is illustrated in Figure 14-2. If the user stops playback manually. and the audio is not automatically restarted. left:160. border-radius: 25. left:10. the "pause" event is triggered.Fill the iOS screen /--> <meta name="viewport" content="width=600" /> <style> canvas { position:absolute. Figure 14-2 Looping audio Listing 14-2 Adding looping audio <html> <head> <title>Happy Holidays</title> <!-. } </style> 2011-03-14 | © 2011 Apple Inc. but loops the audio when it is playing. top:10. Note that the audio loops only when the "ended" event is triggered.Adding Sound to Canvas Animations Looping Audio The example in Listing 14-2 runs the visual part of the presentation whether or not the user chooses to play the audio. 154 . top:307. All Rights Reserved. } audio { position:absolute.

height.can. 0. 'aqua'). 'purple').getContext("2d"). var yIncr = []. 'green'). rainbow. ctx = can.width . All Rights Reserved.255)').140. can.addColorStop(0.0). 'blue'). rainbow.20.addColorStop(1.getElementById("audio").createLinearGradient(0. rainbow. rainbow. // set font properties ctx.20.Adding Sound to Canvas Animations Looping Audio <script type="text/javascript"> var can. // create blue background gradient grad = ctx. 'rgb(20.128)'). 155 . grad.font="140pt Papyrus bold italic". var xIncr = []. 'red'). var grad.addColorStop(4 / 6. var audio.addColorStop(5 / 6.addColorStop(6 / 6.addColorStop(0. var rainbow. function init() { flake = document.addColorStop(3 / 6. var ctx. 'rgb(140. var flake. rainbow. rainbow. audio = document.addColorStop(2 / 6.addColorStop(1 / 6. 'yellow'). 'orange').createLinearGradient(20.0). can = document.getElementById("can"). rainbow. grad. // create rainbow gradient for letters rainbow = ctx. 2011-03-14 | © 2011 Apple Inc. var x = [].0. var rot = 0. var y = [].getElementById("flake").

x[i] = Math. // set animation on perpetual loop setInterval("animate()".height + 45) { y[i] = -45. false). give new x coord and start above top if (y[i] > can.3 . 0. yIncr[i] = Math. } // add listener function to loop on end audio.random() * . } function model() { // increment flakes x and y coords for (i=1.textBaseline = "bottom".height / -2.0.i<=16.i<=16.width.max(Math.i++) { x[i] = Math. loop. y[i] = Math.random() * can.textAlign="center".play(). All Rights Reserved.15).random() * 0. 30). 2011-03-14 | © 2011 Apple Inc. x[i] = x[i] + xIncr[i].15. } function animate() { model().4.width. 156 .i++) { y[i] = y[i] + yIncr[i].Adding Sound to Canvas Animations Looping Audio ctx.random() * can. draw(). // create snowflakes above screen // drifting slowly down and randomly left or right for (i=1. xIncr[i] = Math. } function loop() { audio. // if off bottom. ctx.random() * can.addEventListener("ended".

fillText("Peace". All Rights Reserved. 157 .height + 2).max(Math.fillStyle = 'black'.005. ctx.2).15). can.fillText("Peace". ctx.y[i]).height).Adding Sound to Canvas Animations Looping Audio yIncr[i] = Math.height ). ctx.width / 2.fillRect(0.translate(x[i].width. ctx.4. } // draw word with black and white borders for depth ctx. // rotate flakes as they fall rot=rot + 0. can.i<=16. can.can.save(). ctx. can. ctx.restore(). can. 0.drawImage(flake.random() * . ctx. -37. ctx. ctx.fillStyle = 'white'.height .rotate(rot). -43). } } } function draw() { // draw background ctx. } </script> </head> <body onload="init()"> <canvas id="can" height="300" width="500"> </canvas> 2011-03-14 | © 2011 Apple Inc.fillStyle = rainbow.width / 2 -2.fillText("Peace".width / 2 + 2.0.can. // draw flakes offset so they rotate around center for (i=1.fillStyle = grad. can. ctx.i++) { ctx.

for example.png" style="display:none"> </body> </html> Adding a Voice-Over For a slideshow or a Keynote presentation. reset the audio and visual elements so that. To synchronize the slides with your audio.mp4" controls id="audio"> </audio> <img id="flake" src="flake. the presentation starts from the beginning. or rearrange your presentation on a slide-by-slide basis. and by changing the canvas to display the next slide. It’s easiest to create and maintain such a presentation if you record a separate audio file for each slide—that way you can add. The example in Listing 14-3 plays a series of images exported from Keynote. synchronized to a series of voice-overs created using QuickTime Player. change. When the last slide has played.Adding Sound to Canvas Animations Adding a Voice-Over <br> <audio src="peace. All Rights Reserved. 158 . Figure 14-3 Voice-over 2011-03-14 | © 2011 Apple Inc. The example is illustrated in Figure 14-3. install an event listener on the audio element and listen for the "ended" event. Respond to the event by changing the src property of the audio element to the audio file for the next slide. if the user presses play again. you may want to add a voice-over that narrates each slide individually.

2011-03-14 | © 2011 Apple Inc.getElementById("audio"). top:10. 159 . var audio. var index = 0. } audio { position: absolute. var image.com/qt4web/". All Rights Reserved. var ctx. function init() { audio = document. var maxSlides = 4. var slide = [].mac.Adding Sound to Canvas Animations Adding a Voice-Over Listing 14-3 Displaying slides with voice-overs <html> <head> <title>Slides with Voice-Over</title> <!-. } </style> <script type="text/javascript"> var can.Fill the iOS screen /--> <meta name="viewport" content="width=600" /> <style> canvas { position:absolute. top: 380. var voice = []. var base = "http://homepage. left:160. left:10.

} // draw initial "splash screen" slide image. can. 0.src = slide[0].src = slide[index].width.jpg". ctx = can. } } function next() { // if audio for this slide ended.src=slide[i]. 0.addEventListener("ended". 160 . can. audio.drawImage(image.0. All Rights Reserved. can. can. next. } function start() { // respond to playing event only if index = 0 if (index==0) { // set index to 1 and show next slide index = 1.0. voice[i] = base + "slide" + i + ".Adding Sound to Canvas Animations Adding a Voice-Over image = document.i++) { // get filenames into JavaScript slide[i] = base + "slides" + i + ". false). image.height). // preload images image. // increment audio src 2011-03-14 | © 2011 Apple Inc. if (index<maxSlides) { // increment slide image src image. for (i=0.getContext("2d"). ctx.getElementById("can").width.src = slide[1]. can = document.mp4". false).i<maxSlides.drawImage(image.getElementById("image"). ctx.addEventListener("playing".height). advance index index++. start. audio.

2011-03-14 | © 2011 Apple Inc. } // if last slide shown.height). audio. reset index and audio src to start over if (index==4) { index = 0. can.mac. can. 0.com/qt4web/slide1. load the first sound in response to a user-activated control. // play new audio src audio.width. To make this work on iOS as well as the desktop.jpg" style="display:none"> <audio id="audio" src="http://homepage. you typically want to trigger different sounds in response to events in the game. // show new slide ctx.drawImage(image. such as a start button.com/qt4web/slides0.mac. 161 . play other sounds by changing the src property of the audio element and calling the play() method. } } </script> </head> <body onload="init()"> <canvas id="can" height="384" width="512"> </canvas> <img id="image" src="http://homepage. All Rights Reserved.src = voice[index].Adding Sound to Canvas Animations Adding Sound Effects audio.mp4" controls > </audio> </body> </html> Adding Sound Effects For games.play().src = voice[1].0.

All Rights Reserved. var ctx. var ball. var x. var direc. var xVec. var gravity = 1. 162 . var yVec. When sounds are enabled.Adding Sound to Canvas Animations Adding Sound Effects The example in Listing 14-4 includes an audio element and an Enable Sounds button. var y. The example is illustrated in Figure 14-4. Figure 14-4 Animation with sound effects Listing 14-4 Creating animation with sound effects <html> <head> <title>Animation with Sound Effects</title> <script type="text/javascript"> var can. the ball makes different sounds when it bounces off the bottom or the side. 2011-03-14 | © 2011 Apple Inc. var rot = 0.

var centerOffset = -75.lineTo(600. function init() { audio = document.mac.mp4".bottom + 75). ball = document. var interval. button = document. var boing = "http://homepage. var bottom=325. ctx.moveTo(0. var button.strokeStyle="black".getElementById("ball"). // set animation to repeat every 50 msec interval = setInterval("animate()". spin direction x = 98.Adding Sound to Canvas Animations Adding Sound Effects var left=70.mp4". var audio. ctx = can. can = document. 2011-03-14 | © 2011 Apple Inc. ctx.lineTo(600.getContext("2d"). xVec = 5. var bing = "http://homepage. 163 . // initialize position.50).5.0) ctx.getElementById("button").mac.getElementById("audio"). // draw lines for the ball to bounce off of ctx.stroke().com/qt4web/bing.com/qt4web/boing. All Rights Reserved. var right=525. ctx. direc = 1.bottom+ 75). } function animate() { model().getElementById("can"). var sound = 0. yVec = 0. speed. y = 75.

2011-03-14 | © 2011 Apple Inc. } } function draw() { ctx.Adding Sound to Canvas Animations Adding Sound Effects // clear canvas except for lines at edge ctx.src = bing.can. } if (x >= right || x <= left) { xVec = -1 * xVec.gravity // set audio for bottom bounce audio.play(). yVec = -1 * yVec .src = boing. // set audio for side bounce audio. y = y + yVec.clearRect(0. } function model() { rot = rot + . ctx. yVec = yVec + gravity.width -1 .can. All Rights Reserved. ctx.translate(x. direc = -1 * direc.y). x = x + xVec. if (sound) audio. } function bounceIf() { if (y >= bottom) { y = bottom. bounceIf(). if (sound) audio.1 * direc.rotate(rot). draw().0.height -1). 164 .play().save().

button.mac.centerOffset).mac.com/qt4web/boing.drawImage(ball.restore().Adding Sound to Canvas Animations Adding Sound Effects ctx. 165 .load(). centerOffset.value="Turn Sounds Off". button. sound = 1.png" style="display:none"> <canvas id="can" height="400" width="600" style="position:relative. } else { sound = 0. All Rights Reserved. } function soundToggle() { if (!sound) { audio.top:-50"> </canvas> <audio id="audio" src="http://homepage.mp4" style="display:none"> </audio> <input id="button" type="button" value="Enable Sounds" onclick="soundToggle()"> </body> </html> 2011-03-14 | © 2011 Apple Inc. ctx. } } </script> </head> <body onload="init()" style="background-color:#e0e0e0"> <h2>Animation with Sound</h2> <img id="ball" src="http://homepage.value="Enable Sounds".com/qt4web/soccerball1.

but an actual reference to the data. and multiplied by 255. You can pass in either a width and height or another imageData object. Any changes you make to the pixel array you obtain are also made immediately to the imageData object. The pixel array you obtain using the imageData. 255. and alpha—expressed as integers from 0-255. The alpha value in the imageData. Getting Pixel Data From the Canvas You can get an imageData object for any rectangular section of the canvas by calling the context’s getImageData(x. blue. inclusive.0 and 1.128.y. inclusive—the normal alpha value quantized to the nearest 1 / 255th.1. 20.20. Note Alpha is normally expressed as a floating point value between 0. 128.height) method. For example: var pixArray = myImageData.Pixel Manipulation You have direct access to the canvas bitmap as an array of RGBa pixels. then multiplied by 255.0. in which case the new imageData object has the same dimensions as the one you pass in. the first four values in the array are 255. green. All Rights Reserved.data property is not a copy of the pixel data. Creating Buffer Objects You can create additional imageData objects by calling the context’s createImageData() method.data The imageData object’s data property is a one-dimensional array having four values per pixel—red. New imageData objects are initialized to transparent black—each pixel in the imageData object’s data property is represented by four consecutive zeroes. 2011-03-14 | © 2011 Apple Inc. If the first pixel in the array is RGBa (255. You obtain an array of pixel values using the imageData object’s data property.data array is expressed as an integer between 0 and 255. width.0) for example. 166 . The alpha value is quantized to the nearest 1 / 255th.

var theImage = ctx.getImageData(0.data for (i=0.y).putImageData(imageData. subY.createImageData(theImage). and height of the subsection as well: context. 2011-03-14 | © 2011 Apple Inc. width. To blit only a subsection of the imageData object to the screen. creates an image buffer. 167 . subHeight) The subsection is blitted in at x+subX. All Rights Reserved.Pixel Manipulation Modifying Pixel Data The following snippet gets the image data for the whole canvas. specifying the x. x.y coordinates of the upper left corner of the rectangle into which you want to blit the image data.data. var pixArray = theImage. y. subX.width.can.y.height). pass in the x. y+subY. and copies the image data into the new buffer. call putImageData(theImageData. subWidth. y. var bufArray = buffer. just as if you had blitted the whole imageData object at x. To blit the entire imageData object to the screen.length. x. var buffer = ctx. } Modifying Pixel Data Modify pixel data on the canvas by assigning an array of pixel values to an imageData object’s pixel array and calling the context’s putImageData() method.can.i++) { bufArray[i] = pixArray[i].0.i<pixArray.

getElementById("can"). var theImage. var red = []. The result is illustrated in figure Figure 15-1. blue. 0 and sets the alpha values for those pixels to 0. Figure 15-1 Green clear and restore Listing 15-1 Manipulating pixels <html> <head> <title>Pixel Manipulation</title> <script type="text/javascript"> var can. 255. // fill screen with red. All Rights Reserved.Pixel Manipulation Example: Reading and Modifying Pixel Data Example: Reading and Modifying Pixel Data The example in Listing 15-1 gets an imageData object for the entire canvas and creates arrays of red. 2011-03-14 | © 2011 Apple Inc. green. A Restore Green button sets the same pixels’ alpha values to 255 (equivalent to RGBa alpha value of 1. When the Clear Green button is pushed. var blue = []. green. 168 . function init() { can = document. var ctx. ctx = can. var pix.fillStyle="red". the example searches for pixels with the RGB value 0. var green = []. and alpha values from the pixel data.getContext("2d"). and blue stripes ctx. var alpha = [].0).

and alpha values // every four values equals one pixel for (i=0.i<can.putImageData(theImage.width* 2/ 3. can.fillRect(can.i++) { if (red[i]==0 && green[i]==255 && blue[i]==0) // set alpha value for pixel to 0 pix[i*4 +3]=0.height.can.height).width / 3.width * can. // get pixel data for entire canvas theImage=ctx.length.fillRect(can. } } function clearGreen() { // check each pixel's RGB value for (i=0.height. can. } ctx. pix = theImage.0). ctx.width * can.can.putImageData(theImage. } // blit modified image object to screen ctx.height). can. } function restoreGreen() { for (i=0.i<pix. ctx. blue.0.Pixel Manipulation Example: Reading and Modifying Pixel Data ctx. blue[i/4]=pix[i+2].width.height).width /3.0.0)'.i++) { if (red[i]==0 && green[i]==255 && blue[i]==0) pix[i*4 +3]=255.fillStyle='rgb(0.i=i+4) { red[i/4]=pix[i]. can. All Rights Reserved. can. 2011-03-14 | © 2011 Apple Inc.0.getImageData(0. // separate out red. 169 . ctx.data.fillRect(0. green[i/4]=pix[i+1]. can.255.width * 2/3. ctx. green.0).height).width.fillStyle="blue".0. alpha[i/4]=pix[i+3].0.0.i<can.

170 . All Rights Reserved.Pixel Manipulation Example: Reading and Modifying Pixel Data } </script> </head> <body onload="init()"> <canvas id="can" height="200" width="300"> </canvas> <br> <input type="button" value="Clear Green" onClick="clearGreen()"> <input type="button" value="Restore Green" onClick="restoreGreen()"> </body> </html> 2011-03-14 | © 2011 Apple Inc.

you can use the isPointInPath(x. adding touch and mouse controls. You can examine the canvas bitmap. Space Arcade Game The example in Listing 16-1 is the skeleton of an arcade game. and triggering sounds. to see if a target color is anywhere inside a given area—this is best when the target is a unique color. missiles that fire when the mouse is clicked or the screen is tapped. If at least one of the objects is a shape. You can compare the x and y coordinates of the various elements—this is best for detecting collisions with walls or between rectangular objects. The game detects collisions between missiles and targets. In addition. All Rights Reserved. for many games you need to detect collisions. a ship that responds to touch or mouse control. images. 2011-03-14 | © 2011 Apple Inc. and maintains a score that advances when a target is hit.Creating Games Creating games involves all the subjects covered in this document up to this point: drawing shapes. Collision Detection There are several ways to detect collisions between objects in a game.y) method to see if a given point is inside the path—this is best for detecting collisions between missiles and shapes. which may involve reading the canvas pixels. It has a scrolling background. as illustrated in Figure 16-1. and text. 171 . and targets that follow a path. animation.

With a little work. var targetSpeed = 1.Creating Games Space Arcade Game All the elements of a side-scrolling arcade game are present. var targetX = new Array. var targets = 4. 1]. var target = [1. var phase=0. var back. All Rights Reserved. var imgWidth = 1498. var shipY = 140. var shipX = 10.1. 1. you can modify the skeleton to support any number of arcade shoot-em-ups. 1. var xBack=0. var targetY = new Array.5. var xIncr = 1. Figure 16-1 Space arcade game Listing 16-1 Space arcade game <html> <head> <title>Space Arcade Game</title> <meta name="viewport" content="width=600" /> <script type="text/javascript"> var can. var ctx. 1. 172 . 2011-03-14 | © 2011 Apple Inc.

173 . var score=0.tMove. targetX[i] = can. animate().i<targets.move. } function animate() { 2011-03-14 | © 2011 Apple Inc.getElementById("back"). } } // There is a maximum of 1 bullet // If user shoots again. false).getContext("2d"). var bulletY.newBullet. can = document. ctx = can. can. } function newTargets() { for (i=0.font="14pt Helvetica". there is no bullet // User clicked mouse or lifted finger function newBullet(e) { bulletX = 35.getElementById("can"). old bullet is gone // If bulletX = 0. newTargets().addEventListener("mousemove". All Rights Reserved. function init() { back = document.PI / 2. false). bulletY = shipY + 10.i++) { target[i] = 1. false). can.addEventListener("touchmove".Creating Games Space Arcade Game var bulletX. can.width + 10 + i * 50. ctx.addEventListener("mouseup". targetY[i] = i * Math.

targetSpeed. drawBack(). } function drawBack() { // pan background xBack = xBack . } function model() { // if there is a bullet. // draw new copy at right edge of old copy ctx. xBack + imgWidth.i++) { targetX[i] = targetX[i] . drawShip(). setTimeout("animate()".i<targets. drawBullet(). All Rights Reserved. xBack.0). reset 2011-03-14 | © 2011 Apple Inc.xIncr. targetY[i] = targetY[i] + phase. // generate new set if (targetX[targets -1] < 0) newTargets(). ctx.0).drawImage(back.15). drawTarget(). // if background scrolled off screen. // if the bullet goes off the right edge. 174 .Creating Games Space Arcade Game model(). drawScore().width) bulletX = 0. } // if last target off left edge of screen.drawImage(back. zero it if (bulletX > can. // move targets for (i=0. advance it if (bulletX) bulletX = bulletX + 2.

// for each target: for (i=0.closePath().shipY). ctx.lineTo(shipX. tY.sin(targetY[i]).fill(). ctx.i++) { // if target not yet hit: if (target[i]) { // draw a circle ctx. ctx.arc(targetX[i].bulletY)) { target[i]=0.Creating Games Space Arcade Game if (xBack<= -1 * imgWidth) xBack = xBack + imgWidth. // if bullet inside circle.lineTo(shipX + 30. } function drawTarget() { ctx.i<targets.beginPath().fillStyle="white". 0. } 2011-03-14 | © 2011 Apple Inc. ctx. 10. var tY = 150 + 25 * Math.beginPath(). ctx.PI). shipY + 20).moveTo(shipX. score = score + 10. } } } } function drawShip() { ctx. 175 . ctx. shipY + 10). All Rights Reserved. ctx. target is hit if (bulletX && ctx. 2 * Math.closePath().isPointInPath(bulletX. ctx.strokeStyle = "red". ctx.stroke().

176 .fillRect(bulletX. } function drawScore() { var sc = "Score: " + score. shipY = e. } // move ship in response to touch function tMove(e) { if (!e) e= event.touches[0]. } // move ship in response to mouse function move(e) { if (!e) e= event.2.pageY.Creating Games Space Arcade Game function drawBullet() { if (bulletX) ctx. return false. return false. } </script> </head> <body onload="init()" style="background-color:black"> <canvas id="can" height="300" width="500"" 2011-03-14 | © 2011 Apple Inc.fillText(sc. 10.25). shipY= e.1). All Rights Reserved. ctx.pageY.bulletY.

a button and a slider. including sound effects.png"> </body> </html> Loony Lander Game The example in Listing 16-2 (page 177) is a complete game. The game has two custom controls. The controls respond to touch or mouse input.left:0"> </canvas> <img id="back" style="display:none" src="starback. to control thrust and rotation.top:0. The game is illustrated in Figure 16-2 (page 177). All Rights Reserved. Figure 16-2 Lander game Listing 16-2 Lander game <html> <head> 2011-03-14 | © 2011 Apple Inc.Creating Games Loony Lander Game style="position:absolute. simulating a Lunar Excursion Module (LEM) landing on the moon. 177 . This game uses PNG images—scaled and rotated—as sprites over a fixed background. A very simple physics model adds gravity to the LEM at regular intervals. created using CSS-styled div elements.

text-align:center.pressed { border: 4 px inset red. top: 410. left:170. top:30.bar { position: relative. } . padding: 10 px. width: 152.Creating Games Loony Lander Game <title>lander</title> <meta name="viewport" content="width=900" /> <style> . border-radius: 18 px. 2011-03-14 | © 2011 Apple Inc. background-color: #C0C0C0. All Rights Reserved.myButton { position: absolute. top:420. 178 . width:75 px. } . background-color: #808080. } . width: 152. height: 2 px. border: 4 px outset red.slider { position: absolute. height = 52. left:500.myButton.

02. height: 50 px. var ctx.2. var flames. var lemW=88. 2011-03-14 | © 2011 Apple Inc. } . All Rights Reserved. var sprite. var delay = 100. left:0. var offset.Creating Games Loony Lander Game background-color: red. text-align:center. background-color: #C0C0C0. } </style> <script type="text/javascript"> var can. var back. var yVector = 0.2. border-radius: 25 px. var xVector = 1. width:50 px. 179 . border: 2 px solid red. var x = 30. var fuel = 250.knob { position: relative. var lemH=88. var gravity = . var flag. var y = 30. var thrust = .

flames = document. All Rights Reserved. xVector = 1. x = 30. setKnob(75). yVector = 0. ctx = can. sprite = document.getElementById("back"). var thrustIsDown = 0. y = 30.getElementById("can"). var mouseIsDown = 0. var theta = 0.offsetWidth / 2.getContext('2d'). var sound. function init() { can = document.getElementById("knob"). var gameover = 0. flag = document. var bottom = 380.body. var knobMid. 2011-03-14 | © 2011 Apple Inc. 180 . var scale. fuel = 250. knob = document. var slider. var thrustButton. var interval.getElementById("flag"). slider = document.getElementById("sprite"). thrustButton = document. var knob. knobMid = knob.getElementById("flames"). document. theta=0. sound = document.addEventListener("mouseup".2. back = document.getElementById("slider").getElementById("sound").Creating Games Loony Lander Game var t.getElementById("thrustButton"). mouseUp. true).

300. ctx. var dispRot = parseInt(theta * 100) / 100. gameover = 0. 100).load(). var dispY = parseInt(yVector * 100) / 100. 2011-03-14 | © 2011 Apple Inc.drawImage(back. drawSprite(). 285.fillText("Loony Lander". 0. All Rights Reserved. var status = "Velocity X: " + dispX + " ctx. init().font="18pt Helvetica". drawFuel().delay). ctx. ctx. drawStatus(). } function drawStatus() { var dispX = parseInt(xVector * 100) / 100. } function drawAll() { drawBack().m4a".fillStyle="yellow". clearTimeout(t).font="36pt Helvetica". 0). Y: " + dispY + " Rot: " + dispRot. interval = setInterval("addGravity()". } function drawBack() { ctx. update().Creating Games Loony Lander Game drawAll(). sound.src="noise. 50). 181 . } function restart() { sound. ctx.fillText(status. clearInterval(interval).

Creating Games Loony Lander Game } function drawFuel() { ctx. ctx. ctx.0. 2011-03-14 | © 2011 Apple Inc.rotate(theta).shadowColor = "black". if (y == bottom && yVector < 1 && Math. 52. ctx.0. lemH * scale).5.fillText("Fuel:".1) { theta = 0. fuel. offset = -1 * lemW * scale / 2 ctx. ctx.5 + (y / 600). 182 . clearInterval(interval). } function gameOver() { gameover = 1.abs(xVector) < 1 && Math.shadowOffsetX= 10. 375).y). ctx. offset. clearTimeout(t). ctx.fillRect(100. } function addGravity() { yVector = yVector + gravity * scale. scale = 0.abs(theta) < .globalAlpha = 1.font="14pt Helvetica".shadowColor="rgba(0. ctx. 360. 20).translate(x.shadowOffsetY= -5. } function drawSprite() { ctx. All Rights Reserved.restore().0)".drawImage(sprite.globalAlpha = 0. lemW * scale.save(). offset. ctx. ctx. ctx. ctx.

(thrust * scale * Math. if (y >= bottom) { y = bottom.font="14pt Helvetica". All Rights Reserved. x -50.cos(theta)). drawAll(). } else { t = setTimeout("update()". 280). } function update() { x = x + xVector. ctx. sound. sound. x.75. ctx. ctx. 280).play(). x. } } function burn() { if (fuel > 0 && y < bottom) { clearTimeout(t).drawImage(flag. drawAll(). } else { theta = 1.fillText("Nice landing!".fillStyle="red".src="chord. gameOver().m4a".m4a" } sound.fillStyle="green". ctx. y -120). 183 .font="14pt Helvetica". Hard landing. 2011-03-14 | © 2011 Apple Inc.Creating Games Loony Lander Game drawAll(). ctx. yVector = yVector . delay).". ctx. y = y + yVector. ctx.src="ohno.fillText("Oops.

Creating Games Loony Lander Game xVector = xVector + (thrust * scale * Math.translate(x. 200).0)". setTimeout("burn()". ctx. } function thrustUp() { thrustIsDown = 0. lemW * scale.play().className="myButton pressed". ctx. ctx. } } } function mouseDown() { mouseIsDown = 1. fuel = fuel .play(). burn(). lemH * scale).drawImage(flames. ctx. } function mouseUp() { mouseIsDown = 0.globalAlpha = .5. 184 .rotate(theta). ctx. 2011-03-14 | © 2011 Apple Inc. thrustButton.y). delay). offset.75. offset. ctx.save().sin(theta)).0. ctx.shadowColor="rgba(0.0. } function thrustDown() { sound. All Rights Reserved.restore(). t = setTimeout("update()". if (thrustIsDown) { sound. thrustIsDown = 1.

don't scroll e. if (touchX >= 0 && touchX <= slider. } function mouseXY(e) { if (mouseIsDown) { if (!e) var e = event. All Rights Reserved. 185 . if (!gameover) { theta = (x .pageX .className="myButton".offsetWidth) { setKnob(touchX). var touchX = e. } } } function touchXY(e) { if (!e) var e = event.preventDefault().style.knobMid.left = knobX.offsetLeft. knob. knobX = Math.slider. 0).touches[0].Creating Games Loony Lander Game thrustButton. knobX = Math.offsetWidth ) { setKnob(mouseX).max(knobX. var mouseX = e. drawSprite().offsetLeft.min(knobX. // slide.offsetWidth).offsetWidth .slider. if (mouseX >= 0 && mouseX <= slider.knob.pageX .75) / 75. } } 2011-03-14 | © 2011 Apple Inc. slider. } } function setKnob(x) { var knobX = x .

186 .png" style="display:none"> <audio id="sound" src="noise.png" style="display:none"> <img id="flames" src="flames.Creating Games Loony Lander Game </script> </head> <body onload="init()"> <img id="back" src="lunarsurface.m4a"> </audio> <canvas id="can" height="436" width="810"> </canvas> <div id="thrustButton" class="myButton" onmousedown="thrustDown()" onmouseup="thrustUp()" ontouchstart="thrustDown()" ontouchend="thrustUp()"> Thrust </div> <div class="slider" id="slider" onmousedown="mouseDown()" onmousemove="mouseXY()" ontouchstart="touchXY()" ontouchmove="touchXY()"> <div class="bar"> </div> <div id="knob" class="knob" > <br>Spin </div> </div> <br> <h2 style="text-align:center"> 2011-03-14 | © 2011 Apple Inc. All Rights Reserved.jpg" style="display:none"> <img id="sprite" src="lem.png" style="display:none"> <img id="flag" src="flag.

All Rights Reserved. land with velocity X < 1.Creating Games Loony Lander Game To win.1 </h2> <input type="button" value="Restart" onclick="restart()" > </body> </html> 2011-03-14 | © 2011 Apple Inc. 187 . Rot < 0. Y < 1.

Generally speaking. To composite canvas text or animations over moving video. capturing a series of stills from a video—or special effects such as putting segments of a moving video image on tiles and moving them independently. On iOS-based devices with larger screens. the video is played back smoothly on the canvas. you can superimpose canvas graphics on playing video. you can display video on canvas by calling drawImage() using a video element as the image source. You can play a video without displaying it outside of the canvas by setting the video element’s display attribute to none. 2011-03-14 | © 2011 Apple Inc. extracting the average color value from a video frame.Putting Video on Canvas On the desktop. Video on canvas is useful for things such as realtime image processing—green screen effects. The frame currently under the playhead is rendered on the canvas. 188 . If you call the play() method on the video. On iOS-based devices with small screens—such as iPhone and iPod touch—video always plays in fullscreen mode. Use video as an image source for the canvas only when you need to access the pixel data of a video. just as you can on the desktop. Using video as a source for drawImage() involves a lot of system resources. it’s better to use a video element behind the canvas—the video shows through the transparent background of the canvas without the overhead of displaying video on the canvas itself. Note Video as a source for the canvas drawImage() method is not currently supported on iOS. video is best displayed using the video element. so the canvas cannot be superimposed on playing video. All Rights Reserved. not the canvas element. such as iPad. and call drawImage() at frequent intervals.

with spinning text overlaid on the video.getElementById("vid"). The output is illustrated in Figure 17-1. and the animation is tied to the video. the best approach is to position a canvas element on top of a video element.getElementById("play"). function init() { vid = document. var vidTimer. var vid.Putting Video on Canvas Canvas Over Video Canvas Over Video When you want to superimpose canvas graphics or animation on a video. var ctx. 189 . so the video shows through the transparent parts of the canvas. All Rights Reserved. 2011-03-14 | © 2011 Apple Inc. var angle = 0. A simple play/pause button controls the video. var text. var playButton. playButton = document. The example in Listing 17-1 plays an MPEG-4 video stream behind the canvas. Figure 17-1 Video under canvas Listing 17-1 Displaying video behind the canvas <html> <head> <meta name = "viewport" content = "width = device-width"> <title>Canvas Over Video</title> <script type="text/javascript"> var can.

width.ended) vidTimer = setTimeout("animate(). // set origin at center of canvas ctx. // if video not paused or ended.rotate(angle).addEventListener('play'. ctx.0.can.width / 2.addEventListener('ended'. 190 .height / 2). reset to 0 at 2 * Pi angle = angle + 0. false).getContext('2d').strokeStyle = "black".translate(can.can. vid. 50). 0.0).". // clear screen ctx.28) angle = 0. animate().Putting Video on Canvas Canvas Over Video can = document. vid.save(). ctx. ctx.querySelector("video"). if (!vidState. ctx = can.font = "20pt Helvetica".textBaseline = "middle" . All Rights Reserved.fillText ("Animation over video!". ctx.0). if (angle>6. ctx. } 2011-03-14 | © 2011 Apple Inc.textAlign = "center". // increment rotation angle. } // Draw spinning text function animate() { ctx.height).getElementById("can").clearRect(0.restore(). // spin the canvas ctx. ctx. false).strokeText ("Animation over video!". can. vidEnd. repeat in 50 msec var vidState = document.paused && !vidState. setAnimate. 0.1. ctx.fillStyle = "white". // draw white text with a black outline ctx.

play(). vidTimer = setTimeout("animate(). top: 10. 50). play. All Rights Reserved. } else { vid.value = "Play". 191 . top: 10. left: 10." > 2011-03-14 | © 2011 Apple Inc.".value=="Play") { vid.Putting Video on Canvas Canvas Over Video // Start animation when video starts playing function setAnimate() { clearTimeout(vidTimer).pause(). playButton. clearTimeout(vidTimer).value = "Play". } </script> </head> <body onload="init()"> <video src="assets/myMovie.m4v" id="vid" style="position:absolute." > </video> <canvas id="can" height="270" width="480" style="position:absolute. } function playVideo() { if (playButton.value = "Pause". left: 10. } } function vidEnd() { playButton.

"> <input type="button" value="Play" onclick="playVideo()" id="play" style="font:18pt Helvetica"> </p> </body> </html> Tiled Video There are a number of effects you can implement by displaying a video as a collection of independent tiles. You map parts of a video onto tiles by calling drawImage(sourceX.destWidth. destX.Putting Video on Canvas Tiled Video </canvas> <p style="position:absolute. such as explosions. top: 300. All Rights Reserved.sourceY. Figure 17-2 Rotating video tiles 2011-03-14 | © 2011 Apple Inc.sourceHeight. left: 50.destY. particle effects. and moving jigsaw puzzles.destHeight). The example in Listing 17-2 divides a video image into four tiles and rotates each tile independently. 192 .sourceWidth. as illustrated in Figure 17-2. See “Drawing an Image with Region Mapping” (page 44) for details.

var leftOffset = 22. var tWide. var vidTimer.getElementById("play"). var tHigh. var vidState. var imageHeight = 270. var ctx. All Rights Reserved. false).getElementById("can"). var vid.addEventListener('ended'. ctx = can. // Tile width and height (divide image into 4 tiles) 2011-03-14 | © 2011 Apple Inc.getContext('2d'). function init() { vid = document. var angle = -1. var playButton. // Offsets to rotate tiles around centers var xOffset. // Offsets to center tiles on canvas var topOffset = 72. can = document. var imageWidth = 480. 193 .querySelector("video"). vid.getElementById("vid").addEventListener('play'. vidState = document. vid.Putting Video on Canvas Tiled Video Listing 17-2 Breaking video into tiles <html> <head> <meta name = "viewport" content = "width = device-width"> <title>Tiled Video</title> <script type="text/javascript"> var can. vidEnd. var yOffset. setAnimate. false). playButton = document.

tHigh).save(). 194 . ctx. yOffset = imageHeight / -4 .drawImage(vid. ctx. ctx.width.0.tHigh.rotate(dispAngle). ctx.yOffset. // Draw four tiles. tHigh / 2 + topOffset).can. tHigh / 2 + topOffset).max(0.tWide.save().tHigh. ctx. ctx. tHigh * 3 / 2 + topOffset).tHigh).tHigh. // Clear screen ctx. ctx.Putting Video on Canvas Tiled Video tWide = imageWidth / 2. ctx.paused) { // If video ended.xOffset. // Offset from center of tile image to upper left corner xOffset = imageWidth / -4 .rotate(dispAngle). ctx. increment but display without rotation var dispAngle = Math. ctx. animate().restore(). ctx. rotated about the center ctx.0.height). ctx.can. draw image in 4 tiles. // If angle is negative.yOffset. All Rights Reserved.tWide.tWide.0.tWide.xOffset. tHigh = imageHeight / 2.drawImage(vid.tWide.ended) angle = 0.0.xOffset.translate(tWide * 3 / 2 + leftOffset. } // If video not paused.0.tHigh.rotate(dispAngle). // rotating each tile about the center function animate() { if (!vidState.drawImage(vid.angle).tWide.translate(tWide / 2 + leftOffset.yOffset. 2011-03-14 | © 2011 Apple Inc.save().draw image once with no rotation if (vidState.translate(tWide / 2 + leftOffset.tWide.restore().clearRect(0.tHigh). ctx.restore(). ctx.

02. All Rights Reserved. ctx. } } // Set display angle to 0 for fifty cycles function reset() { angle = -1. 2011-03-14 | © 2011 Apple Inc. 195 .tWide.value=="Play") { vid. } function playVideo() { if (playButton.play().Putting Video on Canvas Tiled Video ctx.tHigh.translate(tWide * 3 / 2 + leftOffset. play.rotate(dispAngle). 50).tHigh.tHigh). ctx. ctx.restore(). ctx. 50).ended) vidTimer = setTimeout("animate().28) angle = -1. reset angle // and display without rotation for fifty cycles if (angle>6. vidTimer = setTimeout("animate(). repeat animation in 50 ms if (!vidState. } // Start animation when video starts playing function setAnimate() { clearTimeout(vidTimer). tHigh * 3 / 2 + topOffset).xOffset.value = "Pause".save(). } else { vid.pause(). // If at full circle. // If video not paused (and video not ended).tWide. // Increment angle angle = angle + 0.tWide.drawImage(vid.".yOffset.".

196 .value = "Play". } } function vidEnd() { playButton.m4v" id="vid" style="display:none" > </video> <canvas id="can" height="420" width="522" > </canvas> <hr> <p> <input type="button" value="Play" onclick="playVideo()" id="play" style="font:18pt Helvetica"> <input type="button" value="Reset" onclick="reset()" style="font:18pt Helvetica"> </p> </body> </html> 2011-03-14 | © 2011 Apple Inc.value = "Play".Putting Video on Canvas Tiled Video playButton. All Rights Reserved. } </script> </head> <body onload="init()"> <video src="assets/myMovie.

Putting Video on Canvas Image Processing Image Processing It’s possible to do realtime image processing on video by displaying the video on canvas and manipulating the pixels of the canvas bitmap.y. The example then uses a slider to control the opacity of only the dark pixels (those with a combined RGB value of less than 150). The example in Listing 17-3 displays a video stream on the canvas and finds the combined rgb value for each pixel. border: 1 px solid #404040. The results are illustrated in Figure 17-3. See “Pixel Manipulation” (page 166) for details. which is a pixel array. All Rights Reserved. 197 . } . Figure 17-3 Image processing Listing 17-3 Realtime image processing <html> <head> <title>Realtime Image Processing</title> <style> canvas { border-radius: 25. width.slider { 2011-03-14 | © 2011 Apple Inc. You can drag the slider while the video is playing and the alpha value of the dark areas of each frame changes dynamically. The canvas bitmap is accessed by calling getImageData(x.height) and accessing the imageData object’s data property.

2011-03-14 | © 2011 Apple Inc. } </style> <script type="text/javascript"> var can.knob { position: relative. } . height: 2 px. All Rights Reserved.Putting Video on Canvas Image Processing position: absolute. height = 52. width: 152. border-radius: 25 px. top:30. text-align:center. background-color: #C0C0C0 . left:85.bar { position: relative. var play. left:100. top: 300. var ctx. background-color: #404040 . var vid. width: 152. width:50 px. border: 1 px solid #404040. } . height: 50 px. 198 .

var knobMid. 25).Putting Video on Canvas Image Processing var vidTimer. vidSet. false). can = document. 2011-03-14 | © 2011 Apple Inc.height). } function showVid() { ctx.0).getElementById("vid"). All Rights Reserved. knobMid = knob.can. vidEnd. function init() { vid = document. var pixData.addEventListener('play'. processImage().drawImage(vid. play = document. var mouseIsDown = 0. 199 . } function vidSet() { clearTimeout(vidTimer). ctx = can. var pixels. } function processImage() { // get pixel data for entire canvas pixels=ctx.paused) // Repeat 40 times a second to oversample 30 fps video vidTimer = setTimeout("showVid()".0.addEventListener('ended'.getElementById("can").can.offsetWidth / 2. vidTimer = setTimeout("showVid()". vid. 0. var knobX = 100. 25).querySelector("video").getElementById("play").getImageData(0.width. vid. false).getContext('2d'). if (!document. showVid().

play().value=="Play") { vid. } // put modified data back into image object pixels.log(playing). // set alpha value for dark pixels to knob value if (rgbVal < 150) pixData[i*4 +3]=alphaVal.i++) { // get combined rgb value to determine brightness var rgbVal = pixData[i*4] + pixData[i*4 + 1] + pixData[i*4 + 2].value = "Play". // blit modified image object to screen ctx. All Rights Reserved. play. } function playPauseVideo() { if (play.i<can. } 2011-03-14 | © 2011 Apple Inc.data.55) // for each pixel for (i=0. play. // set alpha value using slider var alphaVal = parseInt(knobX * 2.value = "Play".0).data = pixData. 200 .value = "Pause". } } function vidEnd() { console.Putting Video on Canvas Image Processing pixData=pixels. } else { vid.putImageData(pixels. play.0.width * can.height.pause().

var touchX = e.Putting Video on Canvas Image Processing function mouseDown() { mouseIsDown = 1.touches[0].preventDefault(). knobX = Math.min(knobX.offsetWidth ) { setKnob(mouseX).left = knobX. mouseXY(). } } function setKnob(x) { knobX = x .knobMid.max(knobX. 201 . All Rights Reserved. knobX = Math. } function mouseXY(e) { if (mouseIsDown) { if (!e) var e = event. // slide.offsetWidth). don't scroll e.offsetLeft. if (mouseX >= 0 && mouseX <= slider.knob.slider. } function mouseUp() { mouseIsDown = 0.offsetWidth . } } } function touchXY(e) { if (!e) var e = event.offsetWidth) { setKnob(touchX).slider. 0).offsetLeft. slider.style.pageX . knob. } 2011-03-14 | © 2011 Apple Inc. var mouseX = e.pageX . if (touchX >= 0 && touchX <= slider.

All Rights Reserved. 202 .Putting Video on Canvas Image Processing </script> </head> <body onload="init()" onmouseup="mouseUp()"> <canvas id="can" height="270" width="480" </canvas> <p> <input type="button" value="Play" onclick="playPauseVideo()" id="play" style="font:18 pt Helvetica"> </p> <video src="assets/myMovie.m4v" id="vid" style="display:none"> </video> <div class="slider" id="slider" onmousedown="mouseDown()" onmousemove="mouseXY()" ontouchstart="touchXY()" ontouchmove="touchXY()"> <div class="bar"> </div> <div id="knob" class="knob"> <br> Alpha </div> </div> </body> </html> 2011-03-14 | © 2011 Apple Inc.

and games. animations. Date 2011-03-14 2011-03-08 Notes Revised and expanded code examples. New document explains how to use the HTML5 <canvas> element for graphics. 2011-03-14 | © 2011 Apple Inc.Document Revision History This table describes the changes to Safari HTML5 Canvas Guide . All Rights Reserved. 203 . Modified code listings.

electronic. registered in the United States and other countries. This warranty gives you specific legal rights. with the following exceptions: Any person is hereby authorized to store documentation on a single computer for personal use only and to print copies of documentation for personal use provided that the documentation contains Apple’s copyright notice. Some states do not allow the exclusion or limitation of implied warranties or liability for incidental or consequential damages. The Apple logo is a trademark of Apple Inc. or otherwise. photocopying. or addition to this warranty.S. ITS QUALITY. and other countries and is used under license. ACCURACY. stored in a retrieval system. IOS is a trademark or registered trademark of Cisco in the U. even if advised of the possibility of such damages. Keynote. THE READER. CA 95014 408-996-1010 Apple. and you may also have other rights which vary from state to state. Apple retains all intellectual property rights associated with the technology described in this document. Java is a registered trademark of Oracle and/or its affiliates. EITHER EXPRESS OR IMPLIED. This document is intended to assist application developers to develop applications only for Apple-labeled computers. ORAL OR WRITTEN.” AND YOU. QuickTime. mechanical. THIS DOCUMENT IS PROVIDED “AS IS. the Apple logo. . iPad is a trademark of Apple Inc. are granted with respect to any of the technology described in this document. and Safari are trademarks of Apple Inc. APPLE MAKES NO WARRANTY OR REPRESENTATION. iTunes. INDIRECT. No Apple dealer. 1 Infinite Loop Cupertino. THE WARRANTY AND REMEDIES SET FORTH ABOVE ARE EXCLUSIVE AND IN LIEU OF ALL OTHERS. No licenses. MERCHANTABILITY. INCIDENTAL. Even though Apple has reviewed this document. No part of this publication may be reproduced. agent. AS A RESULT. express or implied. © 2011 Apple Inc. All rights reserved. iPod. without prior written permission of Apple Inc. OR CONSEQUENTIAL DAMAGES RESULTING FROM ANY DEFECT OR INACCURACY IN THIS DOCUMENT. OR FITNESS FOR A PARTICULAR PURPOSE. available from Linotype Library GmbH. Helvetica is a registered trademark of Heidelberger Druckmaschinen AG.. or employee is authorized to make any modification. iPod touch. ARE ASSUMING THE ENTIRE RISK AS TO ITS QUALITY AND ACCURACY. or transmitted. SPECIAL. iPhone. WITH RESPECT TO THIS DOCUMENT. IN NO EVENT WILL APPLE BE LIABLE FOR DIRECT. EXPRESS OR IMPLIED. extension. so the above limitation or exclusion may not apply to you. in any form or by any means. Apple Inc. Mac.Apple Inc. recording..

You're Reading a Free Preview

Download
scribd
/*********** DO NOT ALTER ANYTHING BELOW THIS LINE ! ************/ var s_code=s.t();if(s_code)document.write(s_code)//-->