Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 939 lines (713 sloc) 54.722 kb
ce99b34 line endings fiddling
Mark Pilgrim authored
1 <!DOCTYPE html>
2 <meta charset=utf-8>
29e9fdc updated copyright info and unified title strategy
Mark Pilgrim authored
3 <title>Canvas - Dive Into HTML5</title>
cf86921 more on IE9 support
Mark Pilgrim authored
4 <!--[if lt IE 9]><script src=j/excanvas.min.js></script><![endif]-->
6f9d06b switch to Git, update feed links and build scripts
Mark Pilgrim authored
5 <link rel=icon href=favicon.ico>
6 <link rel=alternate type=application/atom+xml href=https://github.com/diveintomark/diveintohtml5/commits/master.atom>
ce99b34 line endings fiddling
Mark Pilgrim authored
7 <link rel=stylesheet href=screen.css>
8 <style>
9 body{counter-reset:h1 4}
10 </style>
11 <link rel=stylesheet media='only screen and (max-device-width: 480px)' href=mobile.css>
e9302c2 added prefetch links
Mark Pilgrim authored
12 <link rel=prefetch href=index.html>
ca25a05 reverted stupid idea
Mark Pilgrim authored
13 <p>You are here: <a href=index.html>Home</a> <span class=u>&#8227;</span> <a href=table-of-contents.html#canvas>Dive Into <abbr>HTML5</abbr></a> <span class=u>&#8227;</span>
ce99b34 line endings fiddling
Mark Pilgrim authored
14 <h1><br>Let&#8217;s Call It A<br>Draw(ing Surface)</h1>
15 <p id=toc>&nbsp;
16 <p class=a>&#x2767;
17
18 <h2 id=divingin>Diving In</h2>
19
20 <p class=f><img src=i/aoc-h.png alt=H width=107 height=105>TML 5 defines <a href=http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html>the &lt;canvas> element</a> as &#8220;a resolution-dependent bitmap canvas which can be used for rendering graphs, game graphics, or other visual images on the fly.&#8221; A <dfn>canvas</dfn> is a rectangle in your page where you can use JavaScript to draw anything you want.</p>
21
22 <table class=bc>
23 <caption>Basic &lt;canvas> support</caption>
24 <thead>
cf86921 more on IE9 support
Mark Pilgrim authored
25 <tr><th title="Internet Explorer">IE<th title="Mozilla Firefox">Firefox<th title="Apple Safari">Safari<th title="Google Chrome">Chrome<th>Opera<th>iPhone<th>Android
ce99b34 line endings fiddling
Mark Pilgrim authored
26 </thead>
27 <tbody>
cf86921 more on IE9 support
Mark Pilgrim authored
28 <tr><td>7.0+<sup>*</sup><td>3.0+<td>3.0+<td>3.0+<td>10.0+<td>1.0+<td>1.0+
ce99b34 line endings fiddling
Mark Pilgrim authored
29 </tbody>
30 <tfoot>
cf86921 more on IE9 support
Mark Pilgrim authored
31 <tr><td colspan=7 style="text-align:left">* Internet Explorer 7 and 8 require the third-party <a href=http://code.google.com/p/explorercanvas/>explorercanvas</a> library. Internet Explorer 9 supports <code>&lt;canvas></code> natively.
ce99b34 line endings fiddling
Mark Pilgrim authored
32 </tfoot>
33 </table>
34
35 <p class=clear>So what does a canvas look like? Nothing, really. A <code>&lt;canvas></code> element has no content and no border of its own.</p>
36
37 <canvas width=300 height=225 class=clear style="float:left"></canvas>
38 <p class="legend right" style="margin-top:4em"><span class=arrow>&nbsp;&#x219c;</span> Invisible canvas
39
40 <p class=clear>The markup looks like this:
41
42 <pre><code>&lt;canvas width="300" height="225">&lt;/canvas></code></pre>
43
44 <p>Let&#8217;s add a dotted border so we can see what we&#8217;re dealing with.</p>
45
46 <canvas width=300 height=225 class=clear style="border:1px dotted;float:left"></canvas>
47 <p class="legend right" style="margin-top:4em"><span class=arrow>&nbsp;&#x219c;</span> Canvas with border
48
597a7c6 word fiddling (this is all matt's fault: http://matt.might.net/articl…
Mark Pilgrim authored
49 <p class=clear>You can have more than one <code>&lt;canvas></code> element on the same page. Each canvas will show up in the <abbr>DOM</abbr>, and each canvas maintains its own state. If you give each canvas an <code>id</code> attribute, you can access them just like any other element.
ce99b34 line endings fiddling
Mark Pilgrim authored
50
51 <p>Let&#8217;s expand that markup to include an <code>id</code> attribute:
52
53 <pre><code>&lt;canvas id="a" width="300" height="225">&lt;/canvas></code></pre>
54
55 <p>Now you can easily find that <code>&lt;canvas></code> element in the <abbr>DOM</abbr>.
56
57 <pre><code>var a_canvas = document.getElementById("a");</code></pre>
58
59 <p class=a>&#x2767;
60
61 <h2 id=shapes>Simple Shapes</h2>
62
63 <table class=bc>
64 <thead>
cf86921 more on IE9 support
Mark Pilgrim authored
65 <tr><th title="Internet Explorer">IE<th title="Mozilla Firefox">Firefox<th title="Apple Safari">Safari<th title="Google Chrome">Chrome<th>Opera<th>iPhone<th>Android
ce99b34 line endings fiddling
Mark Pilgrim authored
66 </thead>
67 <tbody>
cf86921 more on IE9 support
Mark Pilgrim authored
68 <tr><td>7.0+<sup>*</sup><td>3.0+<td>3.0+<td>3.0+<td>10.0+<td>1.0+<td>1.0+
ce99b34 line endings fiddling
Mark Pilgrim authored
69 </tbody>
70 <tfoot>
cf86921 more on IE9 support
Mark Pilgrim authored
71 <tr><td colspan=7 style="text-align:left">* Internet Explorer 7 and 8 require the third-party <a href=http://code.google.com/p/explorercanvas/>explorercanvas</a> library. Internet Explorer 9 supports <code>&lt;canvas></code> shapes natively.
ce99b34 line endings fiddling
Mark Pilgrim authored
72 </tfoot>
73 </table>
74
75 <p>Every canvas starts out blank. That&#8217;s boring! Let&#8217;s draw something.</p>
76
77 <canvas id=b width=300 height=225 style="border:1px dotted;float:left" onclick="draw_b();return false"></canvas>
78 <p class="legend right" style="margin-top:4em"><span class=arrow>&nbsp;&#x21dc;</span> <a href="#" onclick="draw_b();return false">Click to draw on this canvas</a></p>
79
80 <p class=clear>The <code>onclick</code> handler called this function:
81
82 <pre><code>function draw_b() {
83 var b_canvas = document.getElementById("b");
84 var b_context = b_canvas.getContext("2d");
85 b_context.fillRect(50, 25, 150, 100);
86 }</code></pre>
87
88 <p>The 1<sup>st</sup> line of the function is nothing special; it just finds the <code>&lt;canvas></code> element in the <abbr>DOM</abbr>.
89
90 <p class="legend left" style="margin-top:2em">And then there&#8217;s this <span class=arrow>&#x21dd;</span>&nbsp;</p>
91 <pre><code>function draw_b() {
92 var b_canvas = document.getElementById("b");
93 <mark> var b_context = b_canvas.getContext("2d");</mark>
94 b_context.fillRect(50, 25, 150, 100);
95 }</code></pre>
96
97 <p class=ss><img src=i/openclipart.org_media_files_johnny_automatic_4145.png width=312 height=300 alt="man drawing in front of a mirror">
98
99 <p>Every canvas has a drawing <dfn>context</dfn>, which is where all the fun stuff happens. Once you&#8217;ve found a <code>&lt;canvas></code> element in the <abbr>DOM</abbr> (by using <code>document.getElementById()</code> or any other method you like), you call its <code>getContext()</code> method. You <strong>must</strong> pass the string <code>"2d"</code> to the <code>getContext()</code> method.
100
101 <blockquote class=note>
102 <p><span>&#x261E;</span>Q: Is there a 3-D canvas?<br>
103 A: Not yet. Individual vendors have experimented with their own three-dimensional canvas <abbr>API</abbr>s, but none of them have been standardized. The <abbr>HTML5</abbr> specification notes, &#8220;A future version of this specification will probably define a 3d context.&#8221;
104 </blockquote>
105
8b9cbdc copyedits
Mark Pilgrim authored
106 <p>So, you have a <code>&lt;canvas></code> element, and you have its drawing context. The drawing context is where all the drawing methods and properties are defined. There&#8217;s a whole group of properties and methods devoted to drawing rectangles:
ce99b34 line endings fiddling
Mark Pilgrim authored
107
108 <ul>
d529fd5 redo browser compatibility tables so columns are browser families and…
Mark Pilgrim authored
109 <li>The <code>fillStyle</code> property can be a <abbr>CSS</abbr> color, a pattern, or a gradient. (More on gradients shortly.) The default <code>fillStyle</code> is solid black, but you can set it to whatever you like. Each drawing context remembers its own properties as long as the page is open, unless you do something to reset it.
ce99b34 line endings fiddling
Mark Pilgrim authored
110 <li><code>fillRect(x, y, width, height)</code> draws a rectangle filled with the current fill style.
8b9cbdc copyedits
Mark Pilgrim authored
111 <li>The <code>strokeStyle</code> property is like <code>fillStyle</code> &mdash; it can be a CSS color, a pattern, or a gradient.
ce99b34 line endings fiddling
Mark Pilgrim authored
112 <li><code>strokeRect(x, y, width, height)</code> draws an rectangle with the current stroke style. <code>strokeRect</code> doesn&#8217;t fill in the middle; it just draws the edges.
113 <li><code>clearRect(x, y, width, height)</code> clears the pixels in the specified rectangle.
114 </ul>
115
116 <div class="pf clear" id=reset>
117 <h4>Ask Professor Markup</h4>
118 <div class=inner>
119 <blockquote class=note>
120 <p><span>&#x261E;</span>Q: Can I &#8220;reset&#8221; a canvas?<br>
121 A: Yes. Setting the width or height of a <code>&lt;canvas></code> element will erase its contents and reset all the properties of its drawing context to their default values. You don&#8217;t even need to <em>change</em> the width; you can simply set it to its current value, like this:
122 <pre><code>var b_canvas = document.getElementById("b");
123 <mark>b_canvas.width = b_canvas.width;</mark></code></pre>
124 </blockquote>
125 </div>
126 </div>
127
128 <p>Getting back to that code sample in the previous example&hellip;
129 <p class="legend left" style="margin-top:2em">Draw a rectangle <span class=arrow>&#x21dd;&nbsp;</span></p>
130 <pre><code>var b_canvas = document.getElementById("b");
131 var b_context = b_canvas.getContext("2d");
132 <mark>b_context.fillRect(50, 25, 150, 100);</mark></code></pre>
133
134 <p class=clear>Calling the <code>fillRect()</code> method draws the rectangle and fills it with the current fill style, which is black until you change it. The rectangle is bounded by its upper-left corner (50,&nbsp;25), its width (150), and its height (100). To get a better picture of how that works, let&#8217;s look at the canvas coordinate system.
135
136 <p class=a>&#x2767;
137
138 <h2 id=coordinates>Canvas Coordinates</h2>
139
140 <p>The canvas is a two-dimensional grid. The coordinate (0, 0) is at the upper-left corner of the canvas. Along the X-axis, values increase towards the right edge of the canvas. Along the Y-axis, values increase towards the bottom edge of the canvas.
141
142 <p class="legend top" style="width:500px;text-align:center">Canvas coordinates diagram <span class=arrow>&#x21b7;</span><br></p>
143 <canvas id=c width=500 height=375></canvas>
144
145 <p>That coordinate diagram was drawn with a <code>&lt;canvas></code> element. It comprises
146
147 <ul>
148 <li>a set of off-white vertical lines
149 <li>a set of off-white horizontal lines
150 <li>two black horizontal lines
151 <li>two small black diagonal lines that form an arrow
152 <li>two black vertical lines
153 <li>two small black diagonal lines that form another arrow
154 <li>the letter &#8220;x&#8221;
155 <li>the letter &#8220;y&#8221;
156 <li>the text &#8220;(0, 0)&#8221; near the upper-left corner
157 <li>the text &#8220;(500, 375)&#8221; near the lower-right corner
158 <li>a dot in the upper-left corner, and another in the lower-right corner
159 </ul>
160
161 <p>First, we need to define the <code>&lt;canvas></code> element itself. The <code>&lt;canvas></code> element defines the <code>width</code> and <code>height</code>, and the <code>id</code> so we can find it later.
162
163 <pre><code>&lt;canvas id="c" width="500" height="375">&lt;/canvas></code></pre>
164
165 <p>Then we need a script to find the <code>&lt;canvas></code> element in the DOM and get its drawing context.
166
167 <pre><code>var c_canvas = document.getElementById("c");
168 var context = c_canvas.getContext("2d");</code></pre>
169
170 <p>Now we can start drawing lines.
171
172 <p class=a>&#x2767;
173
174 <h2 id=paths>Paths</h2>
175
176 <table class=bc>
177 <thead>
cf86921 more on IE9 support
Mark Pilgrim authored
178 <tr><th title="Internet Explorer">IE<th title="Mozilla Firefox">Firefox<th title="Apple Safari">Safari<th title="Google Chrome">Chrome<th>Opera<th>iPhone<th>Android
ce99b34 line endings fiddling
Mark Pilgrim authored
179 </thead>
180 <tbody>
cf86921 more on IE9 support
Mark Pilgrim authored
181 <tr><td>7.0+<sup>*</sup><td>3.0+<td>3.0+<td>3.0+<td>10.0+<td>1.0+<td>1.0+
ce99b34 line endings fiddling
Mark Pilgrim authored
182 </tbody>
183 <tfoot>
cf86921 more on IE9 support
Mark Pilgrim authored
184 <tr><td colspan=7 style="text-align:left">* Internet Explorer 7 and 8 require the third-party <a href=http://code.google.com/p/explorercanvas/>explorercanvas</a> library. Internet Explorer 9 supports <code>&lt;canvas></code> paths natively.
ce99b34 line endings fiddling
Mark Pilgrim authored
185 </tfoot>
186 </table>
187
188 <p style="float:left;margin:1.75em 1.75em 1.75em 0"><img src=i/openclipart.org_media_files_johnny_automatic_7563.png alt="gerbil sitting on a chair with a quill and ink jar" width=167 height=347>
189
8b9cbdc copyedits
Mark Pilgrim authored
190 <p>Imagine you&#8217;re drawing a picture in ink. You don&#8217;t want to just dive in and start drawing with ink, because you might make a mistake. Instead, you sketch the lines and curves with a pencil, and once you&#8217;re happy with it, you trace over your sketch in ink.
ce99b34 line endings fiddling
Mark Pilgrim authored
191
192 <p>Each canvas has a <dfn>path</dfn>. Defining the path is like drawing with a pencil. You can draw whatever you like, but it won&#8217;t be part of the finished product until you pick up the quill and trace over your path in ink.
193
8b9cbdc copyedits
Mark Pilgrim authored
194 <p>To draw straight lines in pencil, you use the following two methods:
ce99b34 line endings fiddling
Mark Pilgrim authored
195
196 <ol style="list-style-position:inside">
8b9cbdc copyedits
Mark Pilgrim authored
197 <li><code>moveTo(x, y)</code> moves the pencil to the specified starting point.
198 <li><code>lineTo(x, y)</code> draws a line to the specified ending point.
ce99b34 line endings fiddling
Mark Pilgrim authored
199 </ol>
200
201 <p>The more you call <code>moveTo()</code> and <code>lineTo()</code>, the bigger the path gets. These are &#8220;pencil&#8221; methods &mdash; you can call them as often as you like, but you won&#8217;t see anything on the canvas until you call one of the &#8220;ink&#8221; methods.
202
8b9cbdc copyedits
Mark Pilgrim authored
203 <p>Let&#8217;s begin by drawing the off-white grid.
ce99b34 line endings fiddling
Mark Pilgrim authored
204
205 <pre style="float:left"><code>for (var x = 0.5; x &lt; 500; x += 10) {
206 context.moveTo(x, 0);
207 <mark>context.lineTo(x, 375);</mark>
208 }</code></pre>
209 <p class="legend right" style="margin-top:4em"><span class=arrow>&nbsp;&#x21dc;</span> Draw vertical lines</p>
210
211 <pre style="clear:left;float:left"><code>for (var y = 0.5; y &lt; 375; y += 10) {
212 context.moveTo(0, y);
213 <mark>context.lineTo(500, y);</mark>
214 }</code></pre>
215 <p class="legend right" style="margin-top:4em"><span class=arrow>&nbsp;&#x21dc;</span> Draw horizontal lines</p>
216
217 <p class=clear>Those were all &#8220;pencil&#8221; methods. Nothing has actually been drawn on the canvas yet. We need an &#8220;ink&#8221; method to make it permanent.
218
219 <pre><code>context.strokeStyle = "#eee";
220 <mark>context.stroke();</mark></code></pre>
221
8b9cbdc copyedits
Mark Pilgrim authored
222 <p><code>stroke()</code> is one of the &#8220;ink&#8221; methods. It takes the complex path you defined with all those <code>moveTo()</code> and <code>lineTo()</code> calls, and actually draws it on the canvas. The <code>strokeStyle</code> controls the color of the lines. This is the result:</p>
ce99b34 line endings fiddling
Mark Pilgrim authored
223
224 <canvas id=c2 width=500 height=375></canvas>
225
226 <div class="pf clear" id=pixel-madness>
227 <h4>Ask Professor Markup</h4>
228 <div class=inner>
229 <blockquote class=note>
230 <p><span>&#x261E;</span>Q: Why did you start <var>x</var> and <var>y</var> at <code>0.5</code>? Why not <code>0</code>?<br>
231 A: Imagine each pixel as a large square. The whole-number coordinates (0, 1, 2&hellip;) are the edges of the squares. If you draw a one-unit-wide line between whole-number coordinates, it will overlap opposite sides of the pixel square, and the resulting line will be drawn two pixels wide. To draw a line that is only one pixel wide, you need to shift the coordinates by 0.5 perpendicular to the line's direction.
a4f2522 added half-pixel diagrams [thanks J.J.]
Mark Pilgrim authored
232 <p>For example, if you try to draw a line from <code>(1,&nbsp;0)</code> to <code>(1,&nbsp;3)</code>, the browser will draw a line covering 0.5 screen pixels on either side of <code>x=1</code>. The screen can&#8217;t display half a pixel, so it expands the line to cover a total of two pixels:
233 <p><img src=i/canvas-half-pixels-1.jpg alt="A line from (1,0) to (1,3) is drawn 2 pixels wide" width=406 height=314>
234 <p>But, if you try to draw a line from <code>(1.5,&nbsp;0)</code> to <code>(1.5,&nbsp;3)</code>, the browser will draw a line covering 0.5 screen pixels on either side of <code>x=1.5</code>, which results in a true 1-pixel-wide line:
235 <p><img src=i/canvas-half-pixels-2.jpg alt="A line from (1.5,0) to (1.5,3) is draw 1 pixel wide" width=404 height=323>
236 <p><em>Thanks to Jason Johnson for providing these diagrams.</em>
ce99b34 line endings fiddling
Mark Pilgrim authored
237 </blockquote>
238 </div>
239 </div>
240
d529fd5 redo browser compatibility tables so columns are browser families and…
Mark Pilgrim authored
241 <p>Now let&#8217;s draw the horizontal arrow. All the lines and curves on a path are drawn in the same color (or gradient &mdash; yes, we&#8217;ll get to those soon). We want to draw the arrow in a different color ink &mdash; black instead of off-white &mdash; so we need to start a new path.
ce99b34 line endings fiddling
Mark Pilgrim authored
242
243 <p class="legend top" style="margin-left:2em">A new path <span class=arrow>&#x21b7;</span><br></p>
244 <pre><code><mark>context.beginPath();</mark>
245 context.moveTo(0, 40);
246 context.lineTo(240, 40);
247 context.moveTo(260, 40);
248 context.lineTo(500, 40);
249 context.moveTo(495, 35);
250 context.lineTo(500, 40);
251 context.lineTo(495, 45);</code></pre>
252
253 <p>The vertical arrow looks much the same. Since the vertical arrow is the same color as the horizontal arrow, we do <strong>not</strong> need to start another new path. The two arrows will be part of the same path.
254
255 <pre style="float:left"><code>context.moveTo(60, 0);
256 context.lineTo(60, 153);
257 context.moveTo(60, 173);
258 context.lineTo(60, 375);
259 context.moveTo(65, 370);
260 context.lineTo(60, 375);
261 context.lineTo(55, 370);</code></pre>
262 <p class="legend right" style="margin-top:4em"><span class=arrow>&nbsp;&#x219c;</span> Not a new path</p>
263
8b9cbdc copyedits
Mark Pilgrim authored
264 <p class=clear>I said these arrows were going to be black, but the <code>strokeStyle</code> is still off-white. (The <code>fillStyle</code> and <code>strokeStyle</code> don&#8217;t get reset when you start a new path.) That&#8217;s OK, because we&#8217;ve just run a series of &#8220;pencil&#8221; methods. But before we draw it for real, in &#8220;ink,&#8221; we need to set the <code>strokeStyle</code> to black. Otherwise, these two arrows will be off-white, and we&#8217;ll hardly be able to see them! The following lines change the color to black and draw the lines on the canvas:
ce99b34 line endings fiddling
Mark Pilgrim authored
265
266 <pre><code>context.strokeStyle = "#000";
267 context.stroke();</code></pre>
268
8b9cbdc copyedits
Mark Pilgrim authored
269 <p>This is the result:</p>
ce99b34 line endings fiddling
Mark Pilgrim authored
270
271 <canvas id=c3 width=500 height=375></canvas>
272
273 <p class=a>&#x2767;
274
275 <h2 id=text>Text</h2>
276
277 <table class=bc>
278 <thead>
cf86921 more on IE9 support
Mark Pilgrim authored
279 <tr><th title="Internet Explorer">IE<th title="Mozilla Firefox">Firefox<th title="Apple Safari">Safari<th title="Google Chrome">Chrome<th>Opera<th>iPhone<th>Android
ce99b34 line endings fiddling
Mark Pilgrim authored
280 </thead>
281 <tbody>
cf86921 more on IE9 support
Mark Pilgrim authored
282 <tr><td>7.0+<sup>*</sup><td>3.0+<sup>&dagger;</sup><td>3.0+<td>3.0+<td>10.0+<td>1.0+<td>1.0+
ce99b34 line endings fiddling
Mark Pilgrim authored
283 </tbody>
284 <tfoot>
48c5ac7 alignment fiddling
Mark Pilgrim authored
285 <tr><td colspan=7 style="text-align:left">* Internet Explorer 7 and 8 require the third-party <a href=http://code.google.com/p/explorercanvas/>explorercanvas</a> library. Internet Explorer 9 supports canvas text natively.
286 <tr><td colspan=7 style="text-align:left">&dagger; Mozilla Firefox 3.0 requires a compatibility shim.
ce99b34 line endings fiddling
Mark Pilgrim authored
287 </tfoot>
288 </table>
289
8b9cbdc copyedits
Mark Pilgrim authored
290 <p>In addition to drawing <a href=#paths>lines on a canvas</a>, you can also draw text on a canvas. Unlike text on the surrounding web page, there is no box model. That means none of the familiar CSS layout techniques are available: no floats, no margins, no padding, no word wrapping. (Maybe you think that&#8217;s a good thing!) You can set a few font attributes, then you pick a point on the canvas and draw your text there.
ce99b34 line endings fiddling
Mark Pilgrim authored
291
292 <p>The following font attributes are available on the <a href=#shapes>drawing context</a>:
293
294 <ul>
295 <li><code>font</code> can be anything you would put in a <abbr>CSS</abbr> <code>font</code> rule. That includes font style, font variant, font weight, font size, line height, and font family.
296 <li><code>textAlign</code> controls text alignment. It is similar (but not identical) to a <abbr>CSS</abbr> <code>text-align</code> rule. Possible values are <code>start</code>, <code>end</code>, <code>left</code>, <code>right</code>, and <code>center</code>.
297 <li><code>textBaseline</code> controls where the text is drawn relative to the starting point. Possible values are <code>top</code>, <code>hanging</code>, <code>middle</code>, <code>alphabetic</code>, <code>ideographic</code>, or <code>bottom</code>.
298 </ul>
299
8b9cbdc copyedits
Mark Pilgrim authored
300 <p><code>textBaseline</code> is tricky, because text is tricky (English text isn&#8217;t, but you can draw any Unicode character you like on a canvas, and Unicode is tricky). The <abbr>HTML5</abbr> specification <a href=http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-textbaseline>explains the different text baselines</a>:
ce99b34 line endings fiddling
Mark Pilgrim authored
301
302 <blockquote cite=http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-textbaseline>
303 <p>The top of the em square is roughly at the top of the glyphs in a font, the hanging baseline is where some glyphs like <span class=u>आ</span> are anchored, the middle is half-way between the top of the em square and the bottom of the em square, the alphabetic baseline is where characters like <span class=u>Á</span>, <span class=u>ÿ</span>, <span class=u>f</span>, and <span class=u>Ω</span> are anchored, the ideographic baseline is where glyphs like <span class=u>私</span> and <span class=u>達</span> are anchored, and the bottom of the em square is roughly at the bottom of the glyphs in a font. The top and bottom of the bounding box can be far from these baselines, due to glyphs extending far outside the em square.</p>
304 <p><img src=i/baselines.png alt="diagram of different values of the textBaseline property" width=680 height=227>
305 </blockquote>
306
307 <p>For simple alphabets like English, you can safely stick with <code>top</code>, <code>middle</code>, or <code>bottom</code> for the <code>textBaseline</code> property.
308
309 <p>Let&#8217;s draw some text! Text drawn inside the canvas inherits the font size and style of the <code>&lt;canvas></code> element itself, but you can override this by setting the <code>font</code> property on the drawing context.
310
311 <pre style="float:left"><code><mark>context.font = "bold 12px sans-serif";</mark>
312 context.fillText("x", 248, 43);
313 context.fillText("y", 58, 165);</code></pre>
314 <p class="legend right"><span class=arrow>&nbsp;&#x219c;</span> Change the font style</p>
315
316 <p class=clear>The <code>fillText()</code> method draws the actual text.</p>
317
318 <pre style="float:left"><code>context.font = "bold 12px sans-serif";
319 <mark>context.fillText("x", 248, 43);</mark>
320 context.fillText("y", 58, 165);</code></pre>
321 <p class="legend right" style="margin-top:2.6em"><span class=arrow>&nbsp;&#x21dc;</span> Draw the text</p>
322
323 <div class="pf clear" id=relative-font-size>
324 <h4>Ask Professor Markup</h4>
325 <div class=inner>
326 <blockquote class=note>
327 <p><span>&#x261E;</span>Q: Can I use relative font sizes to draw text on a canvas?<br>
328 A: Yes. Like every other <abbr>HTML</abbr> element on your page, the <code>&lt;canvas></code> element itself has a computed font size based on your page&#8217;s CSS rules. If you set the <code>context.font</code> property to a relative font size like <code>1.5em</code> or <code>150%</code>, your browser multiplies this by the computed font size of the <code>&lt;canvas></code> element itself.
329 </blockquote>
330 </div>
331 </div>
332
8b9cbdc copyedits
Mark Pilgrim authored
333 <p>For the text in the upper-left corner, let&#8217;s say I want the top of the text to be at <code>y=5</code>. But I&#8217;m lazy &mdash; I don&#8217;t want to measure the height of the text and calculate the baseline. Instead, I can set <code>textBaseline</code> to <code>top</code> and pass in the upper-left coordinate of the text&#8217;s bounding box.
ce99b34 line endings fiddling
Mark Pilgrim authored
334
335 <pre><code>context.textBaseline = "top";
336 context.fillText("( 0 , 0 )", <mark>8, 5</mark>);</code></pre>
337
8b9cbdc copyedits
Mark Pilgrim authored
338 <p>Now for the text in the lower-right corner. Let&#8217;s say I want the bottom-right corner of the text to be at coordinates <code>(492,370)</code> &mdash; just a few pixels away from the bottom-right corner of the canvas &mdash; but I don&#8217;t want to measure the width or height of the text. I can set <code>textAlign</code> to <code>right</code> and <code>textBaseline</code> to <code>bottom</code>, then call <code>fillText()</code> with the bottom-right coordinates of the text&#8217;s bounding box.
ce99b34 line endings fiddling
Mark Pilgrim authored
339
340 <pre><code>context.textAlign = "right";
341 context.textBaseline = "bottom";
342 context.fillText("( 500 , 375 )", <mark>492, 370</mark>);</code></pre>
343
344 <p>And this is the result:</p>
345
346 <canvas id=c4 width=500 height=375></canvas>
347
8b9cbdc copyedits
Mark Pilgrim authored
348 <p>Oops! We forgot the dots in the corners. We&#8217;ll see how to draw circles a little later. For now, I&#8217;ll cheat a little and <a href=#shapes>draw them as rectangles</a>.
ce99b34 line endings fiddling
Mark Pilgrim authored
349
350 <pre style="float:left"><code>context.fillRect(0, 0, 3, 3);
351 context.fillRect(497, 372, 3, 3);</code></pre>
352 <p class="legend right"><span class=arrow>&nbsp;&#x21dc;</span> Draw two &#8220;dots&#8221;</p>
353
8b9cbdc copyedits
Mark Pilgrim authored
354 <p>And that&#8217;s all she wrote! Here is the final product:</p>
ce99b34 line endings fiddling
Mark Pilgrim authored
355
356 <canvas id=c5 width=500 height=375 class=clear></canvas>
357
358 <p class=a>&#x2767;
359
360 <h2 id=gradients>Gradients</h2>
361
362 <table class=bc>
363 <thead>
cf86921 more on IE9 support
Mark Pilgrim authored
364 <tr><th><th title="Internet Explorer">IE<th title="Mozilla Firefox">Firefox<th title="Apple Safari">Safari<th title="Google Chrome">Chrome<th>Opera<th>iPhone<th>Android
ce99b34 line endings fiddling
Mark Pilgrim authored
365 </thead>
366 <tbody>
d529fd5 redo browser compatibility tables so columns are browser families and…
Mark Pilgrim authored
367 <tr>
cf86921 more on IE9 support
Mark Pilgrim authored
368 <tr><th>linear gradients<td>7.0+<sup>*</sup><td>3.0+<td>3.0+<td>3.0+<td>10.0+<td>1.0+<td>1.0+
369 <tr><th>radial gradients<td>9.0+<td>3.0+<td>3.0+<td>3.0+<td>10.0+<td>1.0+<td>1.0+
ce99b34 line endings fiddling
Mark Pilgrim authored
370 </tbody>
371 <tfoot>
cf86921 more on IE9 support
Mark Pilgrim authored
372 <tr><td colspan=8 style="text-align:left">* Internet Explorer 7 and 8 require the third-party <a href=http://code.google.com/p/explorercanvas/>explorercanvas</a> library. Internet Explorer 9 supports <code>&lt;canvas></code> gradients natively.
ce99b34 line endings fiddling
Mark Pilgrim authored
373 </tfoot>
374 </table>
375
3465673 copyedits
Mark Pilgrim authored
376 <p>Earlier in this chapter, you learned how to draw <a href=#shapes>a rectangle filled with a solid color</a>, then <a href=#paths>a line stroked with a solid color</a>. But shapes and lines aren&#8217;t limited to solid colors. You can do all kinds of magic with gradients. Let&#8217;s look at an example.</p>
ce99b34 line endings fiddling
Mark Pilgrim authored
377
378 <canvas id=d width=300 height=225></canvas>
379
380 <p>The markup looks the same as any other canvas.
381
382 <pre><code>&lt;canvas id="d" width="300" height="225">&lt;/canvas></code></pre>
383
384 <p>First, we need to find the <code>&lt;canvas></code> element and its drawing context.
385
386 <pre><code>var d_canvas = document.getElementById("d");
387 var context = d_canvas.getContext("2d");</code></pre>
388
389 <p>Once we have the drawing context, we can start to define a gradient. A <dfn>gradient</dfn> is a smooth transition between two or more colors. The canvas drawing context supports two types of gradients:
390
391 <ol>
392 <li><code>createLinearGradient(x0, y0, x1, y1)</code> paints along a line from (x0, y0) to (x1, y1).
393 <li><code>createRadialGradient(x0, y0, r0, x1, y1, r1)</code> paints along a cone between two circles. The first three parameters represent the start circle, with origin (x0, y0) and radius r0. The last three parameters represent the end circle, with origin (x1, y1) and radius r1.
394 </ol>
395
396 <p>Let&#8217;s make a linear gradient. Gradients can be any size, but I&#8217;ll make this gradient be 300 pixels wide, like the canvas.
397
398 <p class="legend top" style="margin-left:2.5em">Create a gradient object <span class=arrow>&#x21b7;</span><br></p>
399 <pre><code>var my_gradient = <mark>context.createLinearGradient(0, 0, 300, 0);</mark></code></pre>
400
401 <p>Because the <code>y</code> values (the 2<sup>nd</sup> and 4<sup>th</sup> parameters) are both 0, this gradient will shade evenly from left to right.
402
403 <p>Once we have a gradient object, we can define the gradient&#8217;s colors. A gradient has two or more <dfn>color stops</dfn>. Color stops can be anywhere along the gradient. To add a color stop, you need to specify its position along the gradient. Gradient positions can be anywhere between 0 to 1.
404
405 <p>Let&#8217;s define a gradient that shades from black to white.
406
407 <pre><code>my_gradient.addColorStop(0, "black");
408 my_gradient.addColorStop(1, "white");</code></pre>
409
410 <p>Defining a gradient doesn&#8217;t draw anything on the canvas. It&#8217;s just an object tucked away in memory somewhere. To draw a gradient, you set your <code>fillStyle</code> to the gradient and draw a shape, like a rectangle or a line.
411
412 <p class="legend top">Fill style is a gradient <span class=arrow>&#x21b7;</span><br></p>
413 <pre><code><mark>context.fillStyle = my_gradient;</mark>
414 context.fillRect(0, 0, 300, 225);</code></pre>
415
416 <p>And this is the result:</p>
417
418 <canvas id=d2 width=300 height=225></canvas>
419
420 <p>Suppose you want a gradient that shades from top to bottom. When you create the gradient object, keep the <code>x</code> values (1<sup>st</sup> and 3<sup>rd</sup> parameters) constant, and make the <code>y</code> values (2<sup>nd</sup> and 4<sup>th</sup> parameters) range from 0 to the height of the canvas.
421
422 <p class="legend top" style="margin-left:6.5em">x values are 0, y values vary <span class=arrow>&#x21b7;</span><br></p>
423 <pre><code>var my_gradient = context.createLinearGradient(<mark>0, 0, 0, 225</mark>);
424 my_gradient.addColorStop(0, "black");
425 my_gradient.addColorStop(1, "white");
426 context.fillStyle = my_gradient;
427 context.fillRect(0, 0, 300, 225);</code></pre>
428
429 <p>And this is the result:</p>
430
431 <canvas id=d3 width=300 height=225></canvas>
432
433 <p>You can also create gradients along a diagonal.
434
435 <p class="legend top" style="margin-left:8.5em">both x and y values vary <span class=arrow>&#x21b7;</span><br></p>
436 <pre><code>var my_gradient = context.createLinearGradient(<mark>0, 0, 300, 225</mark>);
437 my_gradient.addColorStop(0, "black");
438 my_gradient.addColorStop(1, "white");
439 context.fillStyle = my_gradient;
440 context.fillRect(0, 0, 300, 225);</code></pre>
441
442 <p>And this is the result:</p>
443
444 <canvas id=d4 width=300 height=225></canvas>
445
446 <p class=a>&#x2767;
447
448 <h2 id=images>Images</h2>
449
450 <table class=bc>
451 <thead>
cf86921 more on IE9 support
Mark Pilgrim authored
452 <tr><th title="Internet Explorer">IE<th title="Mozilla Firefox">Firefox<th title="Apple Safari">Safari<th title="Google Chrome">Chrome<th>Opera<th>iPhone<th>Android
ce99b34 line endings fiddling
Mark Pilgrim authored
453 </thead>
454 <tbody>
cf86921 more on IE9 support
Mark Pilgrim authored
455 <tr><td>7.0+<sup>*</sup><td>3.0+<td>3.0+<td>3.0+<td>10.0+<td>1.0+<td>1.0+
ce99b34 line endings fiddling
Mark Pilgrim authored
456 </tbody>
457 <tfoot>
cf86921 more on IE9 support
Mark Pilgrim authored
458 <tr><td colspan=7 style="text-align:left">* Internet Explorer 7 and 8 require the third-party <a href=http://code.google.com/p/explorercanvas/>explorercanvas</a> library. Internet Explorer 9 supports <code>&lt;canvas></code> images natively.
ce99b34 line endings fiddling
Mark Pilgrim authored
459 </tfoot>
460 </table>
461
462 <p>Here is a cat:
463
464 <p style="float:left"><img src=i/openclipart.org_media_files_johnny_automatic_1360.png alt="sleeping cat" width=177 height=113 id=cat>
465 <p class="legend right" style="margin-top:4em"><span class=arrow>&nbsp;&#x21dc;</span> An &lt;img> element</p>
466
467 <p class=clear>Here is the same cat, drawn on a canvas:
468
469 <div style="float:right">
470 <p class="legend left" style="margin-top:2em">A &lt;canvas> element <span class=arrow>&#x21dd;&nbsp;</span></p>
471 <canvas id=e width=177 height=113></canvas>
472 </div>
473
597a7c6 word fiddling (this is all matt's fault: http://matt.might.net/articl…
Mark Pilgrim authored
474 <p class=clear>The canvas drawing context defines a <code>drawImage()</code> method for drawing an image on a canvas. The method can take three, five, or nine arguments.
ce99b34 line endings fiddling
Mark Pilgrim authored
475
476 <ul>
477 <li><code>drawImage(image, dx, dy)</code> takes an image and draws it on the canvas. The given coordinates <code>(dx, dy)</code> will be the upper-left corner of the image. Coordinates <code>(0, 0)</code> would draw the image at the upper-left corner of the canvas.
478 <li><code>drawImage(image, dx, dy, dw, dh)</code> takes an image, scales it to a width of <code>dw</code> and a height of <code>dh</code>, and draws it on the canvas at coordinates <code>(dx, dy)</code>.
479 <li><code>drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh)</code> takes an image, clips it to the rectangle <code>(sx, sy, sw, sh)</code>, scales it to dimensions <code>(dw, dh)</code>, and draws it on the canvas at coordinates <code>(dx, dy)</code>.
480 </ul>
481
482 <p>The <abbr>HTML5</abbr> specification <a href=http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#images>explains the <code>drawImage()</code> parameters</a>:
483
484 <blockquote cite=http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#images>
485 <p>The source rectangle is the rectangle [within the source image] whose corners are the four points <code>(sx, sy)</code>, <code>(sx+sw, sy)</code>, <code>(sx+sw, sy+sh)</code>, <code>(sx, sy+sh)</code>.
486 <p>The destination rectangle is the rectangle [within the canvas] whose corners are the four points <code>(dx, dy)</code>, <code>(dx+dw, dy)</code>, <code>(dx+dw, dy+dh)</code>, <code>(dx, dy+dh)</code>.
487 <p><img src=i/drawImage.png alt="diagram of drawImage parameters" width=327 height=330>
488 </blockquote>
489
490 <p>To draw an image on a canvas, you need an image. The image can be an existing <code>&lt;img></code> element, or you can create an <code>Image()</code> object with JavaScript. Either way, you need to ensure that the image is fully loaded before you can draw it on the canvas.
491
492 <p>If you&#8217;re using an existing <code>&lt;img></code> element, you can safely draw it on the canvas during the <code>window.onload</code> event.
493 <p class="legend top" style="margin-left:6.5em"><span class=arrow>&#x21b6;</span> using an &lt;img> element<br></p>
494 <pre><code>&lt;img <mark>id="cat"</mark> src="images/cat.png" alt="sleeping cat" width="177" height="113">
495 &lt;canvas id="e" width="177" height="113">&lt;/canvas>
496 &lt;script>
497 <mark>window.onload</mark> = function() {
498 var canvas = document.getElementById("e");
499 var context = canvas.getContext("2d");
500 var cat = document.getElementById("cat");
501 <mark>context.drawImage(cat, 0, 0);</mark>
502 };
503 &lt;/script></code></pre>
504
505 <p>If you&#8217;re creating the image object entirely in JavaScript, you can safely draw the image on the canvas during the <code>Image.onload</code> event.
506
507 <p class="legend top" style="margin-left:3.5em">using an Image() object <span class=arrow>&#x21b7;</span><br></p>
508 <pre><code>&lt;canvas id="e" width="177" height="113">&lt;/canvas>
509 &lt;script>
510 var canvas = document.getElementById("e");
511 var context = canvas.getContext("2d");
512 var cat = <mark>new Image()</mark>;
513 cat.src = "images/cat.png";
514 <mark>cat.onload</mark> = function() {
515 context.drawImage(cat, 0, 0);
516 };
517 &lt;/script></code></pre>
518
519 <p>The optional 3<sup>rd</sup> and 4<sup>th</sup> parameters to the <code>drawImage()</code> method control image scaling. This is the same image, scaled to half its width and height and drawn repeatedly at different coordinates within a single canvas.</p>
520
521 <canvas id=multicat width=500 height=375></canvas>
522
523 <p>Here is the script that produces the &#8220;multicat&#8221; effect:
524
525 <pre style="float:left"><code>cat.onload = function() {
526 for (var x = 0, y = 0;
527 x &lt; 500 &amp;&amp; y &lt; 375;
528 x += 50, y += 37) {
529 context.drawImage(cat, x, y, <mark>88, 56</mark>);
530 }
531 };
532 </code></pre>
533 <p class="legend right" style="margin-top:6em"><span class=arrow>&nbsp;&#x21dc;</span> Scale the image</p>
534
535 <p class=clear>All this effort raises a legitimate question: why would you want to draw an image on a canvas in the first place? What does the extra complexity of image-on-a-canvas buy you over an <code>&lt;img></code> element and some <abbr>CSS</abbr> rules? Even the &#8220;multicat&#8221; effect could be replicated with 10 overlapping <code>&lt;img></code> elements.
536
d529fd5 redo browser compatibility tables so columns are browser families and…
Mark Pilgrim authored
537 <p>The simple answer is, for the same reason you might want to <a href=#text>draw text on a canvas</a>. The <a href=#coordinates>canvas coordinates diagram</a> included text, lines, and shapes; the text-on-a-canvas was just one part of a larger work. A more complex diagram could easily use <code>drawImage()</code> to include icons, sprites, or other graphics.
ce99b34 line endings fiddling
Mark Pilgrim authored
538
539 <p class=a>&#x2767;
540
541 <h2 id=ie>What About IE?</h2>
542
37ca17b Several updates for IE9 support. Never thought I'd say *that*.
Mark Pilgrim authored
543 <p>Versions of Internet Explorer before 9.0 do not support the canvas <abbr>API</abbr>. (IE9 does <a href=http://msdn.microsoft.com/en-us/ie/ff468705.aspx#_HTML5_canvas>fully support the canvas <abbr>API</abbr></a>.) However, those older versions of Internet Explorer <em>do</em> support a Microsoft-proprietary technology called <abbr>VML</abbr>, which can do many of the same things as the <code>&lt;canvas></code> element. And thus, <code>excanvas.js</code> was born.
ce99b34 line endings fiddling
Mark Pilgrim authored
544
545 <p><a href=http://code.google.com/p/explorercanvas/>Explorercanvas</a> (<code>excanvas.js</code>) is an open source, Apache-licensed JavaScript library that implements the canvas <abbr>API</abbr> in Internet Explorer. To use it, include the following <code>&lt;script></code> element at the top of your page.
546
547 <pre><code>&lt;!DOCTYPE html>
548 &lt;html>
549 &lt;head>
550 &lt;meta charset="utf-8">
ca25a05 reverted stupid idea
Mark Pilgrim authored
551 &lt;title>Dive Into HTML5&lt;/title>
cf86921 more on IE9 support
Mark Pilgrim authored
552 <mark>&lt;!--[if lt IE 9]>
ce99b34 line endings fiddling
Mark Pilgrim authored
553 &lt;script src="excanvas.js">&lt;/script>
554 &lt;![endif]--></mark>
555 &lt;/head>
556 &lt;body>
557 ...
558 &lt;/body>
559 &lt;/html>
560 </code></pre>
561
cf86921 more on IE9 support
Mark Pilgrim authored
562 <p>The <code>&lt;!--[if lt IE 9]></code> and <code>&lt;![endif]--></code> bits are <a href="http://msdn.microsoft.com/en-us/library/ms537512(VS.85).aspx">conditional comments</a>. Internet Explorer interprets them like an <code>if</code> statement: &#8220;if the current browser is a version of Internet Explorer prior to (but not including) version 9, then execute this block.&#8221; Every other browser will treat the entire block as an <abbr>HTML</abbr> comment. The net result is that Internet Explorer 7 and 8 will download the <code>excanvas.js</code> script and execute it, but other browsers will ignore the script altogether (not download it, not execute it, not anything). This makes your page load faster in browsers that implement the canvas <abbr>API</abbr> natively.
ce99b34 line endings fiddling
Mark Pilgrim authored
563
564 <p>Once you include the <code>excanvas.js</code> in the <code>&lt;head></code> of your page, you don&#8217;t need to do anything else to accomodate Internet Explorer. Just include <code>&lt;canvas></code> elements in your markup, or create them dynamically with JavaScript. Follow the instructions in this chapter to get the drawing context of a <code>&lt;canvas></code> element, and you can draw shapes, text, and patterns.
565
566 <p>Well&hellip; not quite. There are a few limitations:
567
568 <ol>
569 <li><a href=#gradients>Gradients</a> can only be linear. <a href=https://developer.mozilla.org/En/Canvas_tutorial/Applying_styles_and_colors#A_createRadialGradient_example>Radial gradients</a> are not supported.
d529fd5 redo browser compatibility tables so columns are browser families and…
Mark Pilgrim authored
570 <li>Patterns must be repeating in both directions.
ce99b34 line endings fiddling
Mark Pilgrim authored
571 <li><a href=http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#clipping-region>Clipping regions</a> are not supported.
572 <li>Non-uniform <a href=http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-scale>scaling</a> does not correctly scale strokes.
d529fd5 redo browser compatibility tables so columns are browser families and…
Mark Pilgrim authored
573 <li>It&#8217;s slow. This should not come as a raging shock to anyone, since Internet Explorer's JavaScript parser is slower than other browsers to begin with. Once you start drawing complex shapes via a JavaScript library that translates commands to a completely different technology, things are going to get bogged down. You won&#8217;t notice the performance degradation in simple examples like drawing a few lines and transforming an image, but you&#8217;ll see it right away once you start doing canvas-based animation and other crazy stuff.
ce99b34 line endings fiddling
Mark Pilgrim authored
574 </ol>
575
4b79f40 finished halma tutorial
Mark Pilgrim authored
576 <p>There is one more caveat about using <code>excanvas.js</code>, and it&#8217;s a problem that I ran into while creating the examples in this chapter. ExplorerCanvas initializes its own faux-canvas interface automatically whenever you include the <code>excanvas.js</code> script in your <abbr>HTML</abbr> page. But that doesn&#8217;t mean that Internet Explorer is ready to use it immediately. In certain situations, you can run into a race condition where the faux-canvas interface is <em>almost</em>, but not quite, ready to use. The primary symptom of this state is that Internet Explorer will complain that &#8220;<samp>object doesn&#8217;t support this property or method</samp>&#8221; whenever you try to do anything with a <code>&lt;canvas></code> element, such as get its drawing context.
d529fd5 redo browser compatibility tables so columns are browser families and…
Mark Pilgrim authored
577
6ee7fa4 some more code inline for live example
Mark Pilgrim authored
578 <p>The easiest solution to this is to defer all of your canvas-related manipulation until after the <code>onload</code> event fires. This may be a while &mdash; if your page has a lot of images or videos, they will delay the <code>onload</code> event &mdash; but it will give ExplorerCanvas time to work its magic.
d529fd5 redo browser compatibility tables so columns are browser families and…
Mark Pilgrim authored
579
580 <p class=a>&#x2767;
581
385eebf incomplete changes to support inline Halma game in canvas chapter
Mark Pilgrim authored
582 <h2 id=halma>A Complete, Live Example</h2>
d529fd5 redo browser compatibility tables so columns are browser families and…
Mark Pilgrim authored
583
4b79f40 finished halma tutorial
Mark Pilgrim authored
584 <p>Halma is a centuries-old board game. Many variations exist. In this example, I&#8217;ve created a solitaire version of Halma with 9 pieces on a 9 &times; 9 board. In the beginning of the game, the pieces form a 3 &times; 3 square in the bottom-left corner of the board. The object of the game is to move all the pieces so they form a 3 &times; 3 square in the upper-right corner of the board, in the least number of moves.
d529fd5 redo browser compatibility tables so columns are browser families and…
Mark Pilgrim authored
585
4b79f40 finished halma tutorial
Mark Pilgrim authored
586 <p>There are two types of legal moves in Halma:
385eebf incomplete changes to support inline Halma game in canvas chapter
Mark Pilgrim authored
587
4b79f40 finished halma tutorial
Mark Pilgrim authored
588 <ul>
589 <li>Take a piece and move it to any adjacent empty square. An &#8220;empty&#8221; square is one that does not currently have a piece in it. An &#8220;adjacent&#8221; square is immediately north, south, east, west, northwest, northeast, southwest, or southeast of the piece&#8217;s current position. (The board does not wrap around from one side to the other. If a piece is in the left-most column, it can not move west, northwest, or southwest. If a piece is in the bottom-most row, it can not move south, southeast, or southwest.)
590 <li>Take a piece and hop over an adjacent piece, and possibly repeat. That is, if you hop over an adjacent piece, then hop over <em>another</em> piece adjacent to your new position, that counts as a single move. In fact, any number of hops still counts as a single move. (Since the goal is to minimize the total number of moves, doing well in Halma involves constructing, and then using, long chains of staggered pieces so that other pieces can hop over them in long sequences.)
591 </ul>
6ee7fa4 some more code inline for live example
Mark Pilgrim authored
592
4b79f40 finished halma tutorial
Mark Pilgrim authored
593 <p>Here is the game itself. You can also <a href=examples/canvas-halma.html>play it on a separate page</a> if you want to poke at it with your browser&#8217;s developer tools.</p>
6ee7fa4 some more code inline for live example
Mark Pilgrim authored
594
4b79f40 finished halma tutorial
Mark Pilgrim authored
595 <canvas id=halmacanvas></canvas>
6ee7fa4 some more code inline for live example
Mark Pilgrim authored
596
4b79f40 finished halma tutorial
Mark Pilgrim authored
597 <p style="margin-top:0;font-style:normal" class="legend">Moves: <span id=halmamovecount>0</span>
6ee7fa4 some more code inline for live example
Mark Pilgrim authored
598
4b79f40 finished halma tutorial
Mark Pilgrim authored
599 <p>How does it work? I&#8217;m so glad you asked. I won&#8217;t show <em>all</em> the code here. (You can see it at <a href=examples/halma.js>diveintohtml5.org/examples/halma.js</a>.) I&#8217;ll skip over most of the gameplay code itself, but I want to highlight a few parts of the code that deal with actually drawing on the canvas and responding to mouse clicks on the canvas element.
6ee7fa4 some more code inline for live example
Mark Pilgrim authored
600
4b79f40 finished halma tutorial
Mark Pilgrim authored
601 <p>During page load, we initialize the game by setting the dimensions of the <code>&lt;canvas></code> itself and storing a reference to its drawing context.
6ee7fa4 some more code inline for live example
Mark Pilgrim authored
602
4b79f40 finished halma tutorial
Mark Pilgrim authored
603 <pre><code>gCanvasElement.width = kPixelWidth;
604 gCanvasElement.height = kPixelHeight;
605 gDrawingContext = gCanvasElement.getContext("2d");</code></pre>
6ee7fa4 some more code inline for live example
Mark Pilgrim authored
606
4b79f40 finished halma tutorial
Mark Pilgrim authored
607 <p>Then we do something you haven&#8217;t seen yet: we add an event listener to the <code>&lt;canvas></code> element to listen for click events.
608
609 <pre><code>gCanvasElement.<mark>addEventListener</mark>(<mark>"click"</mark>, halmaOnClick, false);</code></pre>
610
611 <p>The <code>halmaOnClick()</code> function gets called when the user clicks anywhere within the canvas. Its argument is a <code>MouseEvent</code> object that contains information about where the user clicked.
612
613 <pre><code>function halmaOnClick(e) {
614 var cell = <mark>getCursorPosition(e)</mark>;
615
616 // the rest of this is just gameplay logic
1fdcfb9 validation fiddling
Mark Pilgrim authored
617 for (var i = 0; i &lt; gNumPieces; i++) {
4b79f40 finished halma tutorial
Mark Pilgrim authored
618 if ((gPieces[i].row == cell.row) &&
619 (gPieces[i].column == cell.column)) {
620 clickOnPiece(i);
621 return;
622 }
6ee7fa4 some more code inline for live example
Mark Pilgrim authored
623 }
4b79f40 finished halma tutorial
Mark Pilgrim authored
624 clickOnEmptyCell(cell);
6ee7fa4 some more code inline for live example
Mark Pilgrim authored
625 }</code></pre>
626
4b79f40 finished halma tutorial
Mark Pilgrim authored
627 <p>The next step is to take the <code>MouseEvent</code> object and calculate which square on the Halma board just got clicked. The Halma board takes up the entire canvas, so every click is <em>somewhere</em> on the board. We just need to figure out where. This is tricky, because mouse events are implemented differently in just about every browser.
6ee7fa4 some more code inline for live example
Mark Pilgrim authored
628
d529fd5 redo browser compatibility tables so columns are browser families and…
Mark Pilgrim authored
629 <pre><code>function getCursorPosition(e) {
630 var x;
631 var y;
17b3853 fix subtle bug with pageX/pageY
Mark Pilgrim authored
632 if (e.pageX != undefined && e.pageY != undefined) {
d529fd5 redo browser compatibility tables so columns are browser families and…
Mark Pilgrim authored
633 x = e.pageX;
634 y = e.pageY;
635 }
636 else {
4b79f40 finished halma tutorial
Mark Pilgrim authored
637 x = e.clientX + document.body.scrollLeft +
638 document.documentElement.scrollLeft;
639 y = e.clientY + document.body.scrollTop +
640 document.documentElement.scrollTop;
641 }</code></pre>
642
643 <p>At this point, we have <code>x</code> and <code>y</code> coordinates that are relative to the document (that is, the entire <abbr>HTML</abbr> page). That&#8217;s not quite useful yet. We want coordinates relative to the canvas.
644
645 <pre><code> x -= gCanvasElement.offsetLeft;
646 y -= gCanvasElement.offsetTop;</code></pre>
647
648 <p>Now we have <code>x</code> and <code>y</code> coordinates that are <a href=#coordinates>relative to the canvas</a>. That is, if <code>x</code> is 0 and <code>y</code> is 0 at this point, we know that the user just clicked the top-left pixel of the canvas.
649
650 <p>From here, we can calculate which Halma square the user clicked, and then act accordingly.
651
b283af8 fix Halma cell tracking code
Mark Pilgrim authored
652 <pre><code> var cell = new Cell(Math.floor(y/kPieceHeight),
653 Math.floor(x/kPieceWidth));
d529fd5 redo browser compatibility tables so columns are browser families and…
Mark Pilgrim authored
654 return cell;
4b79f40 finished halma tutorial
Mark Pilgrim authored
655 }</code></pre>
656
657 <p>Whew! Mouse events are tough. But you can use the same logic (in fact, this exact code) in all of your own canvas-based applications. Remember: mouse click &rarr; document-relative coordinates &rarr; canvas-relative coordinates &rarr; application-specific code.
658
3465673 copyedits
Mark Pilgrim authored
659 <p>OK, let&#8217;s look at the main drawing routine. Because the graphics are so simple, I&#8217;ve chosen to clear and redraw the board in its entirety every time anything changes within the game. This is not strictly necessary. The canvas drawing context will retain whatever you have previously drawn on it, even if the user scrolls the canvas out of view or changes to another tab and then comes back later. If you&#8217;re developing a canvas-based application with more complicated graphics (such as an arcade game), you can optimize performance by tracking which regions of the canvas are &#8220;dirty&#8221; and redrawing just the dirty regions. But that is outside the scope of this book.
4b79f40 finished halma tutorial
Mark Pilgrim authored
660
661 <pre><code>gDrawingContext.clearRect(0, 0, kPixelWidth, kPixelHeight);</code></pre>
662
597a7c6 word fiddling (this is all matt's fault: http://matt.might.net/articl…
Mark Pilgrim authored
663 <p>The board-drawing routine should look familiar. It&#8217;s similar to how we drew the <a href=#coordinates>canvas coordinates diagram</a> earlier in this chapter.
4b79f40 finished halma tutorial
Mark Pilgrim authored
664
091ebce some more marks
Mark Pilgrim authored
665 <pre><code>gDrawingContext.<mark>beginPath()</mark>;
4b79f40 finished halma tutorial
Mark Pilgrim authored
666
667 /* vertical lines */
c01adaa validation fiddling
Mark Pilgrim authored
668 for (var x = 0; x &lt;= kPixelWidth; x += kPieceWidth) {
091ebce some more marks
Mark Pilgrim authored
669 gDrawingContext.<mark>moveTo</mark>(0.5 + x, 0);
670 gDrawingContext.<mark>lineTo</mark>(0.5 + x, kPixelHeight);
d529fd5 redo browser compatibility tables so columns are browser families and…
Mark Pilgrim authored
671 }
672
4b79f40 finished halma tutorial
Mark Pilgrim authored
673 /* horizontal lines */
c01adaa validation fiddling
Mark Pilgrim authored
674 for (var y = 0; y &lt;= kPixelHeight; y += kPieceHeight) {
091ebce some more marks
Mark Pilgrim authored
675 gDrawingContext.<mark>moveTo</mark>(0, 0.5 + y);
676 gDrawingContext.<mark>lineTo</mark>(kPixelWidth, 0.5 + y);
4b79f40 finished halma tutorial
Mark Pilgrim authored
677 }
678
679 /* draw it! */
091ebce some more marks
Mark Pilgrim authored
680 gDrawingContext.<mark>strokeStyle</mark> = "#ccc";
681 gDrawingContext.<mark>stroke()</mark>;</code></pre>
4b79f40 finished halma tutorial
Mark Pilgrim authored
682
3465673 copyedits
Mark Pilgrim authored
683 <p>The real fun begins when we go to draw each of the individual pieces. A piece is a circle, something we haven&#8217;t drawn before. Furthermore, if the user selects a piece in anticipation of moving it, we want to draw that piece as a filled-in circle. Here, the argument <code>p</code> represents a piece, which has <code>row</code> and <code>column</code> properties that denote the piece&#8217;s current location on the board. We use some in-game constants to translate <code>(column, row)</code> into canvas-relative <code>(x, y)</code> coordinates, then draw a circle, then (if the piece is selected) fill in the circle with a solid color.
4b79f40 finished halma tutorial
Mark Pilgrim authored
684
685 <pre><code>function drawPiece(p, selected) {
686 var column = p.column;
687 var row = p.row;
091ebce some more marks
Mark Pilgrim authored
688 var <mark>x</mark> = (column * kPieceWidth) + (kPieceWidth/2);
689 var <mark>y</mark> = (row * kPieceHeight) + (kPieceHeight/2);
4b79f40 finished halma tutorial
Mark Pilgrim authored
690 var radius = (kPieceWidth/2) - (kPieceWidth/10);</code></pre>
691
a96d5e2 add missing argument to arc() call, as required by the spec and firefox
Mark Pilgrim authored
692 <p>That&#8217;s the end of the game-specific logic. Now we have <code>(x, y)</code> coordinates, relative to the canvas, for the center of the circle we want to draw. There is no <code>circle()</code> method in the canvas <abbr>API</abbr>, but there is an <code>arc()</code> method. And really, what is a circle but an arc that goes all the way around? Do you remember your basic geometry? The <code>arc()</code> method takes a center point <code>(x, y)</code>, a radius, a start and end angle (in radians), and a direction flag (<code>false</code> for clockwise, <code>true</code> for counter-clockwise). You can use the <code>Math</code> module that&#8217;s built into JavaScript to calculate radians.
4b79f40 finished halma tutorial
Mark Pilgrim authored
693
694 <pre><code>gDrawingContext.beginPath();
a96d5e2 add missing argument to arc() call, as required by the spec and firefox
Mark Pilgrim authored
695 gDrawingContext.<mark>arc</mark>(x, y, radius, 0, <mark>Math.PI * 2</mark>, false);
4b79f40 finished halma tutorial
Mark Pilgrim authored
696 gDrawingContext.closePath();</code></pre>
697
698 <p>But wait! Nothing has been drawn yet. Like <code>moveTo()</code> and <code>lineTo</code>, the <code>arc()</code> method is <a href=#paths>a &#8220;pencil&#8221; method</a>. To actually draw the circle, we need to set the <code>strokeStyle</code> and call <code>stroke()</code> to trace it in &#8220;ink.&#8221;
699
091ebce some more marks
Mark Pilgrim authored
700 <pre><code>gDrawingContext.<mark>strokeStyle</mark> = "#000";
701 gDrawingContext.<mark>stroke()</mark>;</code></pre>
4b79f40 finished halma tutorial
Mark Pilgrim authored
702
703 <p>What if the piece is selected? We can re-use the same path we created to draw the outline of the piece, to fill in the circle with a solid color.
704
705 <pre><code>if (selected) {
091ebce some more marks
Mark Pilgrim authored
706 gDrawingContext.<mark>fillStyle</mark> = "#000";
707 gDrawingContext.<mark>fill()</mark>;
d529fd5 redo browser compatibility tables so columns are browser families and…
Mark Pilgrim authored
708 }</code></pre>
709
091ebce some more marks
Mark Pilgrim authored
710 <p>And that&#8217;s&hellip; well, that&#8217;s pretty much it. The rest of the program is game-specific logic &mdash; distinguishing between valid and invalid moves, keeping track of the number of moves, detecting whether the game is over. With 9 circles, a few straight lines, and 1 <code>onclick</code> handler, we&#8217;ve created an entire game in <code>&lt;canvas></code>. Huzzah!
ce99b34 line endings fiddling
Mark Pilgrim authored
711
712 <p class=a>&#x2767;
713
714 <h2 id=further-reading>Further Reading</h2>
715
716 <ul>
717 <li><a href=https://developer.mozilla.org/en/Canvas_tutorial>Canvas tutorial</a> on Mozilla Developer Center
3465673 copyedits
Mark Pilgrim authored
718 <li><a href=http://dev.opera.com/articles/view/html-5-canvas-the-basics/><abbr>HTML5</abbr> <code>canvas</code> &mdash; the basics</a>, by Mihai Sucan
719 <li><a href=http://www.canvasdemos.com/>CanvasDemos.com</a>: demos, tools, and tutorials for the <abbr>HTML</abbr> <code>canvas</code> element
720 <li><a href=http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html>The <code>canvas</code> element</a> in the <abbr>HTML5</abbr> draft standard
37ca17b Several updates for IE9 support. Never thought I'd say *that*.
Mark Pilgrim authored
721 <li><a href=http://msdn.microsoft.com/en-us/ie/ff468705.aspx#_HTML5_canvas>Internet Explorer 9 Guide for Developers: HTML5 <code>canvas</code> element</a>
ce99b34 line endings fiddling
Mark Pilgrim authored
722 </ul>
723
724 <p class=a>&#x2767;
725
a312f69 navigation
Mark Pilgrim authored
726 <p>This has been &#8220;Let&#8217;s Call It A Draw(ing Surface).&#8221; The <a href=table-of-contents.html>full table of contents</a> has more if you&#8217;d like to keep reading.
727
ce99b34 line endings fiddling
Mark Pilgrim authored
728 <div class=pf>
729 <h4>Did You Know?</h4>
730 <div class=moneybags>
7ff039a now available, apparently
Mark Pilgrim authored
731 <blockquote><p>In association with Google Press, O&#8217;Reilly is distributing this book in a variety of formats, including paper, ePub, Mobi, and <abbr>DRM</abbr>-free <abbr>PDF</abbr>. The paid edition is called &#8220;HTML5: Up &amp; Running,&#8221; and it is available now. This chapter is included in the paid edition.
732 <p>If you liked this chapter and want to show your appreciation, you can <a href="http://www.amazon.com/HTML5-Up-Running-Mark-Pilgrim/dp/0596806027?ie=UTF8&amp;tag=diveintomark-20&amp;creativeASIN=0596806027">buy &#8220;HTML5: Up &amp; Running&#8221; with this affiliate link</a> or <a href=http://oreilly.com/catalog/9780596806033>buy an electronic edition directly from O&#8217;Reilly</a>. You&#8217;ll get a book, and I&#8217;ll get a buck. I do not currently accept direct donations.
ce99b34 line endings fiddling
Mark Pilgrim authored
733 </blockquote>
734 </div>
735 </div>
736
29e9fdc updated copyright info and unified title strategy
Mark Pilgrim authored
737 <p class=c>Copyright MMIX&ndash;MMXI <a href=about.html>Mark Pilgrim</a>
ce99b34 line endings fiddling
Mark Pilgrim authored
738
739 <form action=http://www.google.com/cse><div><input type=hidden name=cx value=014021643941856155761:6jgee_nxreo><input type=hidden name=ie value=UTF-8><input type=search name=q size=25 placeholder="powered by Google&trade;">&nbsp;<input type=submit name=sa value=Search></div></form>
740
741 <script src=j/jquery.js></script>
742 <script src=j/canvastext-fx3.js></script>
743 <script src=j/dih5.js></script>
4b79f40 finished halma tutorial
Mark Pilgrim authored
744 <script src=examples/halma.js></script>
ce99b34 line endings fiddling
Mark Pilgrim authored
745 <script>
746 function draw_b() {
747 try {
748 var b_canvas = document.getElementById("b");
749 var b_context = b_canvas.getContext("2d");
750 b_context.fillRect(50, 25, 150, 100);
751 } catch(err) {}
752 }
753
754 function reset_b() {
755 try {
756 var b_canvas = document.getElementById("b");
757 b_canvas.width = b_canvas.width;
758 } catch(err) {}
759 }
760
761 function draw_grid(ctx) {
762 try {
763 /* vertical lines */
a70ed25 adventures in escaping
Mark Pilgrim authored
764 for (var x = 0.5; x < 500; x += 10) {
ce99b34 line endings fiddling
Mark Pilgrim authored
765 ctx.moveTo(x, 0);
766 ctx.lineTo(x, 375);
767 }
768
769 /* horizontal lines */
a70ed25 adventures in escaping
Mark Pilgrim authored
770 for (var y = 0.5; y < 375; y += 10) {
ce99b34 line endings fiddling
Mark Pilgrim authored
771 ctx.moveTo(0, y);
772 ctx.lineTo(500, y);
773 }
774
775 /* draw it! */
776 ctx.strokeStyle = "#eee";
777 ctx.stroke();
778 } catch(err) {}
779 }
780
781 function draw_arrows(ctx) {
782 try {
783 /* x-axis */
784 ctx.beginPath();
785 ctx.moveTo(0, 40);
786 ctx.lineTo(240, 40);
787 ctx.moveTo(260, 40);
788 ctx.lineTo(500, 40);
789 ctx.moveTo(495, 35);
790 ctx.lineTo(500, 40);
791 ctx.lineTo(495, 45);
792
793 /* y-axis */
794 ctx.moveTo(60, 0);
795 ctx.lineTo(60, 153);
796 ctx.moveTo(60, 173);
797 ctx.lineTo(60, 375);
798 ctx.moveTo(65, 370);
799 ctx.lineTo(60, 375);
800 ctx.lineTo(55, 370);
801
802 /* draw it! */
803 ctx.strokeStyle = "#000";
804 ctx.stroke();
805 } catch(err) {}
806 }
807
808 function draw_labels(ctx) {
809 try {
810 ctx.font = "bold 12px sans-serif";
811 ctx.fillText("x", 248, 43);
812 ctx.fillText("y", 58, 165);
813 } catch(err) {}
814
815 try {
816 ctx.textBaseline = "top";
817 ctx.fillText("( 0 , 0 )", 8, 5);
818 } catch(err) {}
819
820 try {
821 ctx.textAlign = "right";
822 ctx.textBaseline = "bottom";
823 ctx.fillText("( 500 , 375 )", 492, 370);
824 } catch(err) {}
825 }
826
827 function draw_dots(ctx) {
828 try {
829 ctx.fillRect(0, 0, 3, 3);
830 ctx.fillRect(497, 372, 3, 3);
831 } catch(err) {}
832 }
833
834 function draw_gradients() {
835 try {
836 var d = document.getElementById("d");
837 var context = d.getContext("2d");
838 var my_gradient = context.createLinearGradient(0, 0, 300, 0);
839 my_gradient.addColorStop(0, "black");
840 my_gradient.addColorStop(1, "white");
841 context.fillStyle = my_gradient;
842 context.fillRect(0, 0, 300, 225);
843 } catch(err) {}
844
845 try {
846 var d2 = document.getElementById("d2");
847 var context = d2.getContext("2d");
848 var my_gradient = context.createLinearGradient(0, 0, 300, 0);
849 my_gradient.addColorStop(0, "black");
850 my_gradient.addColorStop(1, "white");
851 context.fillStyle = my_gradient;
852 context.fillRect(0, 0, 300, 225);
853 } catch(err) {}
854
855 try {
856 var d3 = document.getElementById("d3");
857 var context = d3.getContext("2d");
858 var my_gradient = context.createLinearGradient(0, 0, 0, 225);
859 my_gradient.addColorStop(0, "black");
860 my_gradient.addColorStop(1, "white");
861 context.fillStyle = my_gradient;
862 context.fillRect(0, 0, 300, 225);
863 } catch(err) {}
864
865 try {
866 var d4 = document.getElementById("d4");
867 var context = d4.getContext("2d");
868 var my_gradient = context.createLinearGradient(0, 0, 300, 225);
869 my_gradient.addColorStop(0, "black");
870 my_gradient.addColorStop(1, "white");
871 context.fillStyle = my_gradient;
872 context.fillRect(0, 0, 300, 225);
873 } catch(err) {}
874 }
875
876 function draw_images(imagesReady) {
877 var cat_canvas = document.getElementById("e");
878 var cat_context = cat_canvas.getContext("2d");
879 var cat_canvas2 = document.getElementById("multicat");
880 var cat_context2 = cat_canvas2.getContext("2d");
881 var cat_image = document.getElementById("cat");
882 var _draw = function() {
883 cat_context.drawImage(cat_image, 0, 0);
884 for (var x = 0, y = 0; x < 500 && y < 375; x += 50, y += 37) {
885 cat_context2.drawImage(cat_image, x, y, 88, 56);
886 }
887 };
888 if (!!imagesReady) {
889 _draw();
890 } else {
891 window.onload = _draw;
892 }
893 }
894
895 function draw(imagesReady) {
896 var c = document.getElementById("c");
897 var ctx = c.getContext("2d");
898 draw_grid(ctx);
899 draw_arrows(ctx);
900 draw_labels(ctx);
901 draw_dots(ctx);
902
903 var c2 = document.getElementById("c2");
904 ctx = c2.getContext("2d");
905 draw_grid(ctx);
906
907 var c3 = document.getElementById("c3");
908 ctx = c3.getContext("2d");
909 draw_grid(ctx);
910 draw_arrows(ctx);
911
912 var c4 = document.getElementById("c4");
913 ctx = c4.getContext("2d");
914 draw_grid(ctx);
915 draw_arrows(ctx);
916 draw_labels(ctx);
917
918 var c5 = document.getElementById("c5");
919 var ctx = c5.getContext("2d");
920 draw_grid(ctx);
921 draw_arrows(ctx);
922 draw_labels(ctx);
923 draw_dots(ctx);
924
925 draw_gradients();
926 draw_images(imagesReady);
4b79f40 finished halma tutorial
Mark Pilgrim authored
927
928 initGame(document.getElementById("halmacanvas"), document.getElementById("halmamovecount"));
ce99b34 line endings fiddling
Mark Pilgrim authored
929 }
930
931 $(document).ready(function() {
932 if (!(!/*@cc_on!@*/0)) {
933 window.attachEvent('onload', draw);
934 } else {
935 draw(false);
936 }
937 });
938 </script>
Something went wrong with that request. Please try again.