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

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 . All Rights Reserved.

world. All Rights Reserved.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.” 52 Animating text 54 2011-03-14 | © 2011 Apple Inc. 6 .

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. Rotation. 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.

8 .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. 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. All Rights Reserved. 9 .

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

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

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

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

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

All Rights Reserved. 15 . 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.

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

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

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

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

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

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

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

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

width-2. Calling clip() makes the current path the clipping region for the canvas. var ctx. drawMask(). ctx. 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. Note that the black outline around the canvas is drawn prior to the mask. and so is unaffected. The example in Listing 2-2 creates a triangle shape. } function drawMask() { 2011-03-14 | © 2011 Apple Inc. All Rights Reserved.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. ctx = can. Figure 2-3 Clipping mask Listing 2-2 <html> <head> Making a mask <title>Triangular Mask</title> <script type="text/javascript"> var can. ctx.1. lines.height-2).can.getElementById("can"). then calls clip() to make the shape a clipping mask.strokeRect(1. 27 .can. Any shapes.strokeStyle = "black". The rectangle and text are clipped when they fall outside the boundaries of the triangle.getContext("2d"). as shown in Figure 2-3.

height). ctx.can.can.Drawing Lines and Shapes Creating Masks // make a triangle with top at mid canvas ctx. ctx.width / 2. ctx.closePath(). 180).height). All Rights Reserved.lineTo(can.0. ctx.fillStyle = "white".0).width.fillRect(0.lineTo(0. 20.can.fillStyle = "blue".width. ctx.clip(). ctx. ctx. // make the current path the clipping region ctx.can.font = "24pt Helvetica". } </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.beginPath().height). 28 . // rectangle and text are clipped to the triangle ctx.fillText("Triangular Clipping Mask". ctx.moveTo(can.

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

0. ctx = can.addColorStop(0. function init() { can = document.createLinearGradient(10. 80. 'white'). All Rights Reserved. The result is shown in Figure 3-1.getElementById("can").0).fillStyle=grad. The rectangle extends 25 pixels beyond the gradient’s endpoints.fillRect(10. } </script> </head> <body onload="init()" style="background-color:#e0e0e0"> 2011-03-14 | © 2011 Apple Inc. var ctx.0 to the point 50. } function drawGradient() { var grad = ctx. 100.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. drawGradient().getContext("2d").0. ctx. 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. 30 .addColorStop(1.100).10. then fills a rectangle with the gradient. 'black'). ctx. grad.

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

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

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

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

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

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

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. 40 . All Rights Reserved.png"><br> Block: <img id="block" src="block.

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

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

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

scaled to fill the specified region of the canvas. and the x. function init() { can = document.sy.0 scaled up by a factor of 2.dy.0. ctx. y. To draw an image with region mapping. } 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.sWide.drawImage(image.getElementById("sprite"). and height of the destination region in the canvas.getContext("2d"). the x. All Rights Reserved.getElementById("can"). 2011-03-14 | © 2011 Apple Inc.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. passing in the image. and height of the source region within the image. sx.200. } function drawSprite() { ctx.100).sHi.drawImage(sprite. sprite = document. drawSprite(). Only the specified region of the image is displayed. call drawImage(). dx. width. width. 44 .Using Predrawn Images Drawing an Image with Region Mapping sprite = document. as illustrated in Figure 4-1.0. ctx = can. y.dWide.getElementById("sprite").

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

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

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

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

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

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

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

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

4. 3. Save the context. can. Note It’s best to use vector fonts when scaling or rotating text. scaled.height / 2). 2. All Rights Reserved. ctx. 53 . 2011-03-14 | © 2011 Apple Inc. world. ctx. ctx. Clear the canvas.font = "24pt Helvetica". } </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.width / 2 . text can be rotated.fillStyle = "blue". and animated.fillText("Hello. transformed. Draw the next frame.textAlign = "center". The usual sequence for animation applies: 1.".textBaseline = "middle". ctx. because bitmapped fonts can appear jagged when scaled up or rotated. Change the context settings. can.Adding Text Animating Text } function drawText() { ctx.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

including the default mode. 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. a transparent pixel (erase both source and destination to transparent). The result of the composition can be to display the source pixel. or a combination pixel. the destination pixel.Advanced Compositing Compositing Modes Compositing Modes The canvas element has eleven built-in compositing modes. which uses values from the source and destination. 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. 74 . Source and destination pixels can be opaque (alpha = 1).globalCompositeOperation = "copy". transparent (alpha = 0). 2011-03-14 | © 2011 Apple Inc. combined using an algorithm. or translucent (0 < alpha < 1). All Rights Reserved.

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. 75 . Display the destination image wherever the source image is transparent. ● ● ● ● ● ● ● ● ● ● 2011-03-14 | © 2011 Apple Inc. Display a blend wherever the destination is translucent and the source is either translucent or opaque. Display a blend wherever both the destination and source are translucent. Display a blend wherever both the source and destination are translucent. Display transparency where the destination is transparent. with a maximum RGB value of 255 and a maximum alpha value of 1. Display the destination image wherever the source is transparent. Display a blend of source and destination wherever the destination is translucent. destination-atop—Display the destination image wherever both images are opaque. copy—Display the source image wherever it is opaque or translucent. destination-over—Display the destination image wherever the destination image is opaque. Display a blend of source and destination wherever the source is translucent (0 < alpha < 1). Display a blend wherever the source and destination are both translucent. source-atop—Display the source image wherever both images are opaque. Display a blend wherever the destination and source are both translucent. ● source-over—The default value for compositing. destination-in—Display the destination image wherever both the destination image and source image are opaque. Display the source image wherever the destination is transparent. All Rights Reserved. lighter—Display an image in which the RGBa color values of source and destination are summed. xor—Display the result of a bitwise exclusive-OR operation between the source and destination pixels. 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 destination or source are transparent. source-out—Display the source image wherever the source image is opaque and the destination image is transparent. Display transparency wherever either the source or destination are transparent. Display the destination image only where the source image is transparent. Display the source image wherever the destination image is transparent. Display transparency elsewhere. destination-out—Display the destination image wherever the destination image is opaque and the source image is transparent. Display a blend wherever the source is translucent and the destination is either translucent or opaque.

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

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

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

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

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

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

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

6200. 83 . 5800. var ctx. All Rights Reserved. as shown in Figure 10-2.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. // data sets -. 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. 300 ]. var maxVal. var dataValue = [ 11000. var numSamples. var minVal. "Chimp". "Dolphin". 2011-03-14 | © 2011 Apple Inc.set literally or obtain from a file or .asp call var dataName = [ "Human". var y. var yScalar. "Cat" ].

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

i++) { calcY(dataValue[i]).fillStyle="green".scale(xScalar.shadowColor = 'rgba(128.i<4.128. ctx. // draw bars for (i=0. 85 . } </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. 0. } } function calcY(value) { y = can.y .y to match data ctx.margin).shadowOffsetX = 20. dataValue[i]).i<4. All Rights Reserved. } // set a color and a shadow ctx.Creating Charts and Graphs Bar Graphs for (i=0. ctx. // translate to bottom of graph and scale x.can.5. xScalar * (i+1).value * yScalar.translate(0.fillText(dataName[i]. 0.height . ctx.i++) { ctx.5)' ctx.fillRect(i+1.128.height .margin). 0. ctx.-1 * yScalar).shadowOffsetY = 1.

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

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

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

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

Creating Charts and Graphs Pie Charts var labY = midY + Math.restore(). All Rights Reserved.shadowOffsetY= -1.fillStyle = fillColor[i]. labX. labY).12.fillText(dataName[i]. 90 . labY + 25). ctx. // update beginning angle for next wedge oldAngle = oldAngle + wedge.fillText("$" + dataValue[i]. // print name and value with black shadow ctx. ctx. ctx. ctx.sin(labAngle) * radius * 1. ctx. ctx.save().shadowColor = "black".shadowOffsetX = 1.3 . } } </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. ctx. labX.

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

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

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

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

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

drawSinWave(index). especially interactive data. </EM> <H1>Frequency Addition</H1> <div id="main" style="border: 5px inset #80e080."> <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. labelWaves(). width:480px. width: 200. All Rights Reserved." > <div id="waves" style="margin: 5px.Creating Charts and Graphs Interactive Data Visualization and Animation phase[index] = thePhase. } </script> </head> <body onload="init()"> <P> <EM> The canvas element is well-suited to display scientific or numeric data. 96 .

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

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

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

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

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

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

10. filled with a gradient.255. 106 .translate(x.0.0. 'rgba(0. grad. All Rights Reserved. grad.0.addColorStop(0.addColorStop(1. var grad.0. 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. This allows you to make the ball any color you like and add a lighting effect at the same time. 'rgba(255.8)').y).75). } 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). 2011-03-14 | © 2011 Apple Inc.Animating the Canvas Adding a Gradient Adding a Gradient You can overlay runtime-generated shapes on predrawn images.PI * 2. ctx. Let’s take the previous example and overlay the soccer ball with a shape. function draw() { ctx.255.255.0.save().3)'). var twoPi = Math. function makeGradient() { grad = ctx.createRadialGradient(0.0. as shown in Figure 11-3.

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

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

rotate(rot).restore(). and large. if (counter==4) { counter=0.width. setTimeout("flap()". ctx. often conspicuously marked wings. 109 .Animating the Canvas Intermittent Animation function flap() { var wait = 40. } draw(). can.can. wait=wait + Math. characterized by clubbed antennae. } function draw() { ctx. a slender body. rot=rot+.centerY). ctx.height / 2). counter++. } </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.random() * 1500.clearRect(0.centerX. ctx.can.width / 2. wait).translate(can. broad. All Rights Reserved. ctx.height).0. ctx.01.drawImage(sprite[counter].save().

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

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

drawBack(). } function animate() { x = x .Animating the Canvas Panning Background ctx = can. 112 .1. if (x<= -1 * imgWidth) x = x + imgWidth.can.getContext("2d"). x + imgWidth.width)) ctx. } </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.0). setTimeout("animate()". animate(). x.drawImage(back.png"> </body> </html> 2011-03-14 | © 2011 Apple Inc. if (x < -1 * (imgWidth . All Rights Reserved.0).drawImage(back.mac. } function drawBack() { ctx.25). x = 0.com/qt4web/landscape.

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

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

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

so it does not interfere with image processing. allowing you to use a background image and clear small areas of the canvas quickly. In Safari and other WebKit-based browsers. add a border. assign it a background color or image.Modifying the Canvas with CSS Because the canvas is an HTML element. you can use CSS styles to modify its position.y. without redrawing the background image. revealing the CSS background. 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 CSS to create animated graphics that roam freely across the webpage. 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. you can use WebKit transitions to smoothly animate changes in CSS properties. A CSS background does not appear in the canvas bitmap. 116 . as illustrated in Figure 12-1. All Rights Reserved. Because the canvas can have a transparent background.height) method clears a section of the canvas. The clearRect(x. and so on. width.

fillRect(25. } </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.fillStyle = "black". ctx.55). ctx. 2011-03-14 | © 2011 Apple Inc.100).getElementById("can"). border: 10px inset brown.font = "24pt Helvetica". 30. or rollover event on another webpage element.getContext("2d"). var ctx = can. All Rights Reserved.com/qt4web/cork.25. ctx. ctx. click.Modifying the Canvas with CSS A Pop-Up Canvas—Animating Position and Opacity <style> canvas { background-image:url('http://homepage.mac.120. 117 .fillStyle = "white".png').fillText("Notice:". } </style> <script type="text/javascript"> function init() { var can = document. ctx.

com/qt4web/cork.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. In non-WebKit-based browsers. 2011-03-14 | © 2011 Apple Inc. 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 canvas is given a CSS background image and a border. When the event occurs that triggers the pop-up. 118 . any changes are automatically animated in Safari and other WebKit-based browsers. All CSS properties are set as webkit-transition properties for the canvas element. If you set the position and opacity properties as -webkit-transition properties.png'). A mouseup event anywhere on the page changes the canvas’s class name to hide it again. left. use CSS to change the canvas top. and animates a bouncing ball. All Rights Reserved. Touching or clicking and holding a particular text element on the page changes the class name of the canvas. the changes take place immediately and the canvas simply appears in the specified position. The CSS class definition makes the canvas visible and positions it on the page.mac. 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. or opacity properties to make the canvas appear at the appropriate place on the page.

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

beginPath(). ctx. setTimeout("animate()". y = y + ydir. ctx. if (x < 25) xdir = 1. 25.clearRect(0. ctx. } function unpop() { can. All Rights Reserved.fill(). ctx. 0. 120 .2 * Math. ctx. if (y > 175) ydir = -1. } </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.arc(x. } function pop() { can. if (x > 375) xdir = -1.PI).200). 25).0.closePath(). 400.y.className="can-pop".className="can-hide".Modifying the Canvas with CSS A Pop-Up Canvas—Animating Position and Opacity function animate() { x = x + xdir.

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

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

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

canY = canY . draw at 0. offset to center ctx. rotate.restore().save(). ctx. // use CSS style to move canvas can. ctx.rotate(rot).centerY). } function moveCan() { canX = canX + 1.centerX.style.0.style.height / 2).2.left = canX.width.0. ctx. if (canY < -80) canY = -80.can.translate(can.clearRect(0. can.top = canY. } </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. ctx.Modifying the Canvas with CSS Free Range Canvas function draw() { // draw rotated: translate to x.height).drawImage(sprite[counter]. 124 . if (canX > 400) canX = 400. can.width / 2. ctx. All Rights Reserved.can.y.

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

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

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

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

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=" . All Rights Reserved. as illustrated in Figure 13-2. 129 . The example in Listing 13-2 positions a pair of buttons and a selector on top of the canvas.</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. The inputs cover any graphics or animation drawn on the canvas. Figure 13-2 Controls on canvas 2011-03-14 | © 2011 Apple Inc." onclick="decr()"> <select id="hundred" onchange="setHundred()"> <option value=0> -.

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

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

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

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

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

255. // draw text at center.Adding Mouse and Touch Controls to Canvas Responding to Mouse and Touch Events on Canvas // large. Clicking the mouse on a bubble.fillRect(canX -5.can.canY -5. } </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.textBaseline="middle".width. ctx. pops the bubble. ctx.10). as illustrated in Figure 13-4. if (!mouseIsDown) str = str + " up". ctx.0.64)". 2011-03-14 | © 2011 Apple Inc. 10. can. // plot cursor ctx. if (mouseIsDown) str = str + " down".width /2.textAlign="center".10). ctx.height / 2. can.font="24pt Helvetica". or touching a bubble with a finger on iOS. This example tracks up to four simultaneous touch events. can. max length to fit on canvas ctx.width . var str = canX + ". ctx.height). can.clearRect(0.fillStyle="white".fillText(str. " + canY. 135 . All Rights Reserved. centered. bright green text ctx. allowing the user to pop up to four bubbles at a time on iOS-based devices.fillStyle="rgb(64.

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

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

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

139 . or to track the mouse or finger coordinates at all. 2011-03-14 | © 2011 Apple Inc. then position the control on top of the canvas using CSS.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. create an HTML div or img element and use CSS to style the element and position it on top of the canvas. That way. Consequently. It can get complicated. By creating the control as an element in HTML. you need to track all touches on the canvas. and you can respond to touches on the control itself. There’s no need to compare multiple touches with multiple controls. you can make the control a target for mouse and touch events. Your control can still be a graphic image—or multiple images—alpha channels in images are automatically composited onto the underlying canvas. and compare the coordinates of each touch to each control you draw. the control is just a graphic. 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. Safari sorts the touches for you. A better approach is to build the control using HTML and CSS. Adding a Custom Button To add a custom button to the canvas. but there is a faster and better way to implement most custom controls. not a targetable element. If your custom control is part of the canvas itself. All Rights Reserved.

in case the user clicks your button. and add a listener function for touchend and mouseup events to detect the button being released. then moves the mouse pointer off your button before releasing the mouse button. and positions it on the canvas. The results are illustrated in Figure 13-5. Similarly. for example). The canvas also changes. When the touch ends or the mouse button is released.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. styles it as a button. You might want to take an action when the button is pressed or when it is released. such as onmousedown and ontouchstart. add a listener function for touchcancel events to the page as a whole. 2011-03-14 | © 2011 Apple Inc. The example in Listing 13-5 creates a div element. in this case to blue.myButton { position: relative. or both. 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. When the button is clicked or touched. Add a state variable to track whether the button is pressed or released. 140 . in case the touch is canceled for some reason (such as an incoming phone call. Add a listener function for mouseup events to the page as a whole. 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 button style reverts to the unpressed state and the canvas changes to black. the button state changes and a different style is applied to the button.

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

0.height). release it and do something on the canvas if (but1press) { but1press = 0.can.width. All Rights Reserved. ctx.can. can.fillStyle="white".fillStyle="beige". } } </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.className="myButton". // revert button style but1.0. } function release1() { // button 1 may or may not be pressed when mouse button comes up // or touch ends. // do something on the canvas ctx. ctx.height).Adding Mouse and Touch Controls to Canvas Creating Custom Canvas Controls // do something on the canvas ctx. // if button is pressed.fillRect(0. 142 . can.

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

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

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

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

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

png" > </body> </html> 2011-03-14 | © 2011 Apple Inc.mac. 148 . All Rights Reserved.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.

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

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

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

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

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

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

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

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

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

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

left:10. } </style> <script type="text/javascript"> var can. var index = 0. top: 380. var audio. var slide = []. 2011-03-14 | © 2011 Apple Inc.getElementById("audio"). var maxSlides = 4. top:10.com/qt4web/". var image.mac. } audio { position: absolute. 159 . function init() { audio = document. var voice = []. left:160.Fill the iOS screen /--> <meta name="viewport" content="width=600" /> <style> canvas { position:absolute. var ctx. var base = "http://homepage. All Rights Reserved.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> <!-.

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

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

var yVec. 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.Adding Sound to Canvas Animations Adding Sound Effects The example in Listing 14-4 includes an audio element and an Enable Sounds button. var ctx. 162 . The example is illustrated in Figure 14-4. var xVec. All Rights Reserved. 2011-03-14 | © 2011 Apple Inc. When sounds are enabled. var x. var y. var rot = 0. var direc. var gravity = 1. var ball. the ball makes different sounds when it bounces off the bottom or the side.

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

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

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

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

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

green. var pix. green. var red = []. 168 . 2011-03-14 | © 2011 Apple Inc. and alpha values from the pixel data. 0 and sets the alpha values for those pixels to 0. The result is illustrated in figure Figure 15-1.0).getElementById("can"). Figure 15-1 Green clear and restore Listing 15-1 Manipulating pixels <html> <head> <title>Pixel Manipulation</title> <script type="text/javascript"> var can. // fill screen with red. var blue = []. var alpha = []. var green = []. var theImage. function init() { can = document. All Rights Reserved. blue. 255. var ctx.getContext("2d").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.fillStyle="red". 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. ctx = can. the example searches for pixels with the RGB value 0. and blue stripes ctx.

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

All Rights Reserved. 170 .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.

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

var targetY = new Array. var targets = 4. var ctx. 1. With a little work.5. 1. var shipY = 140. var xIncr = 1. var target = [1. var imgWidth = 1498. 2011-03-14 | © 2011 Apple Inc. 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. All Rights Reserved. 1]. var targetX = new Array. var phase=0.Creating Games Space Arcade Game All the elements of a side-scrolling arcade game are present. var back. 1. var targetSpeed = 1. 172 . you can modify the skeleton to support any number of arcade shoot-em-ups. var xBack=0. var shipX = 10.1.

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

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

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

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

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

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

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

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

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

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

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

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

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

jpg" style="display:none"> <img id="sprite" src="lem. All Rights Reserved.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.png" style="display:none"> <audio id="sound" src="noise.png" style="display:none"> <img id="flag" src="flag.png" style="display:none"> <img id="flames" src="flames. 186 .

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

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

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

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

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

"> <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.destWidth.destHeight). You map parts of a video onto tiles by calling drawImage(sourceX. Figure 17-2 Rotating video tiles 2011-03-14 | © 2011 Apple Inc.sourceY. and moving jigsaw puzzles. 192 .sourceWidth. The example in Listing 17-2 divides a video image into four tiles and rotates each tile independently. as illustrated in Figure 17-2. such as explosions. top: 300. See “Drawing an Image with Region Mapping” (page 44) for details. particle effects. All Rights Reserved. destX.sourceHeight. left: 50.Putting Video on Canvas Tiled Video </canvas> <p style="position:absolute.destY.

var angle = -1. // Offsets to rotate tiles around centers var xOffset. // Tile width and height (divide image into 4 tiles) 2011-03-14 | © 2011 Apple Inc. false). var vid. var imageHeight = 270. can = document.getContext('2d'). false).addEventListener('ended'. var vidState. var playButton. vidState = document.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. // Offsets to center tiles on canvas var topOffset = 72. var vidTimer. 193 . var ctx. All Rights Reserved.getElementById("can"). var tWide. var tHigh. playButton = document. var imageWidth = 480.addEventListener('play'. vid. var leftOffset = 22. var yOffset. vidEnd.querySelector("video"). ctx = can.getElementById("vid"). vid. function init() { vid = document. setAnimate.getElementById("play").

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

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

Putting Video on Canvas Tiled Video playButton. 196 . } </script> </head> <body onload="init()"> <video src="assets/myMovie.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".value = "Play". All Rights Reserved. } } function vidEnd() { playButton.

197 . The example in Listing 17-3 displays a video stream on the canvas and finds the combined rgb value for each pixel. which is a pixel array. border: 1 px solid #404040. Figure 17-3 Image processing Listing 17-3 Realtime image processing <html> <head> <title>Realtime Image Processing</title> <style> canvas { border-radius: 25. } . The canvas bitmap is accessed by calling getImageData(x. See “Pixel Manipulation” (page 166) for details. width. All Rights Reserved.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.slider { 2011-03-14 | © 2011 Apple Inc. The results are illustrated in Figure 17-3. You can drag the slider while the video is playing and the alpha value of the dark areas of each frame changes dynamically.height) and accessing the imageData object’s data property. 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).y.

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

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

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

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

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. All Rights Reserved.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. 202 .

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

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

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)//-->