forked from subelsky/html5tutorial
-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathtutorial.html
530 lines (361 loc) · 47.6 KB
/
tutorial.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF8"/>
<title>HTML5: Больше, чем модная фишка</title>
<style type="text/css">
body {
font-family: Georgia, Times, serif;
font-size: 1em;
line-height: 1.6em;
}
h1.title {
text-align: center;
font-size: 3em;
border: none;
margin-top: 0.5em;
width:
}
h1 {
font-family: Ubuntu, "Trebuchet MS", Tachoma, Arial, sans;
margin-top: 2em;
margin-bottom: 0.5em;
color: #039;
border-top: 1px solid #039;
padding-top: 10px;
}
h2 {
color: #039;
}
</style>
</head>
<body>
<h1 class="title">HTML5: Больше, чем модная фишка</h1>
<h1>ЗАДАЧИ</h1>
<p>Цель этого практикума - показать, как новые возможности HTML5 могут быть использованы в современных веб-приложениях без участия дополнительных зависимостей и противоречий с уже существующим кодом. Курс рассчитан на то, чтобы дать вам достаточно знаний для самостоятельного использования новых возможностей в своих приложениях.</p>
<h1>ПРЕДВАРИТЕЛЬНЫЕ ТРЕБОВАНИЯ</h1>
<ul>
<li><p>Все упражнения проверялись в последних версиях браузера Chrome, но они должны работать и в последних версиях Safari и Firefox. Некоторые упражнения работают в Firefox не так, как задумано, из-за ограничений браузера.
Я не проверял работоспособность в IE или Opera, но большая часть кода должна работать и там.</p></li>
<li><p>Ваш браузер должен иметь консоль JavaScript. Такая консоль уже имеется в Safari и Chrome. Если вы используете Firefox, не забудьте установить <a href="http://getfirebug.com">Firebug</a>.</p></li>
<li><p>Вам понадобится текстовый редактор (Vi, Emacs, Textmate, Notepad, etc.) для внесения изменений в файлы проекта.</p></li>
</ul>
<h1>УСТАНОВКА</h1>
<ol>
<li><p>Скопируйте папку <code>start</code> из этого проекта в какое-нибудь другое место. Эта папка содержит все необходимые зависимости и послужит основой для игры, которую мы будем делать.</p></li>
<li><p>Изучите файл index.html в скопированной директории <code>start</code>. Он представляет собой упрощенный шаблон, основанный на <a href="http://html5boilerplate.com/">HTML5 Boilerplate</a>. Здесь вы можете найти <a href="https://github.com/paulirish/html5-boilerplate/blob/master/index.html">полный шаблон</a>. Это отличный способ изучить особенности HTML5.</p>
<p> Обратите внимание на DOCTYPE, с которого начинается этот файл. Это все, что нужно, чтобы указать браузеру, что вы используете последнюю, самую современную версию HTML.
Кроме того, в шаблоне используются новые семантические тэги <code><header></code> и <code><footer></code>. Их можно использовать вместо менее семантической разметки вроде <code><div id="header"></div></code>.</p></li>
<li><p>Откройте index.html в вашем браузере. Затем откройте консоль JavaScript (в Chrome это делается через меню Вид > Инструменты > Консоль Javascript; в Safari достаточно нажать option-Apple-C). В консоли должно быть сообщение "Ваша консоль работает". Если вы не видите это сообщение, попробуйте установить Firebug или открыть файл в другом браузере. Без работающей консоли следовать этому руководству крайне затруднительно.</p>
<p> Это сообщение было отправлено в консоль в файле <code>js/tutorial.js</code>. Я буду использовать этот файл для всего кода приложения.</p></li>
</ol>
<h1>УПРАЖНЕНИЕ 1</h1>
<h2>Определение возможностей</h2>
<ol>
<li><p>Изучите файл <code>js/libs/modernizr</code>. Это библиотека <a href="http://www.modernizr.com/">Modernizr</a>, которая выполняет определение возможностей браузера и помогает применять стили к новым семантическим элементам HTML5. Пока рано полагаться на то, что браузеры полностью поддерживают новые возможности, поэтому крайне важно проводить определение возможностей браузера и выдавать осмысленные сообщения об ошибках в случае необходимости.</p></li>
<li><p>Чтобы подключить Modernizr, откройте текстовый редактор и добавьте в тэг <code><HEAD></code> файла <code>index.html</code> следующую строку:</p>
<p> <code><script src="js/libs/modernizr-1.7.js"></script></code></p>
<p> Затем добавьте класс <code>no-js</code> к тэгу <code><HTML></code> во второй строке файла index.html.</p></li>
<li><p>Откройте <code>index.html</code> в браузере и посмотрите на тэг <code><HTML></code>. Обратите внимание на то, что класс <code>no-js</code> был заменен на ряд объявлений CSS. Эти объявления можно использовать, чтобы показывать сообщения об ошибках или для стилизации контента, предъявлемого при недоступности некоторого функционала.</p></li>
<li><p>Откройте консоль JavaScript и изучите объект Modernizr. Вы сможете выполнить все упражнения этого учебника, если все нижеприведенные свойства будут иметь значение true:</p>
<ul>
<li>Modernizr.canvas</li>
<li>Modernizr.websockets</li>
<li>Modernizr.audio</li>
<li>Modernizr.geolocation</li>
<li>Modernizr.localstorage</li>
</ul>
<p> Если какие-либо из этих свойств не имеют значения "true", установите браузер <a href="http://www.google.com/chrome">Chrome</a>.</p></li>
</ol>
<h2>Дополнительное задание</h2>
<p>Используя только CSS и HTML и полагаясь на классы <code>touch</code> и <code>no-touch</code> элемента <code><HEAD></code>, выведите соотвествующее сообщение о том, поддерживает ли устройство пользователя тач-интерфейс. Если у вас установлен симулятор мобильной ОС (например, iOS Simulator), проверьте оба случая.</p>
<h1>УПРАЖНЕНИЕ 2</h1>
<h2>Основы рисования на canvas</h2>
<ol>
<li><p>Добавьте тэг <code><canvas id="main" width="400" height="400"></canvas></code> сразу после открывающего тэга <code><body></code> в файле <code>index.html</code>.</p></li>
<li><p>Чтобы нарисовать на canvas прямоугольник, добавьте в js/tutorial.js следующий код:
<pre>var canvas = document.getElementById("main");
var context = canvas.getContext("2d");
context.fillRect(0,0,20,20);</pre></p>
<p> Рисование на canvas осуществляется в контексте canvas, а не на самом объекте canvas. В настоящее время доступен только контекст "2d". В будущем возможно появление 3D-контекста.</p></li>
<li><p>Откройте <code>index.html</code> в браузере. Вы должны увидеть черный прямоугольник.</p></li>
<li><p>Вместо <code>fillRect</code> вызовите <code>strokeRect</code> и обновите страницу в браузере. Теперь прямоугольник не закрашен.</p></li>
<li><p>Задайте свойству <code>fillStyle</code> контекста какой-нибудь цвет CSS, например, красный - "red", нарисуйте закрашенный прямоугольник и обновите страницу. Вы увидите красный прямоугольник.</p>
<p> <pre>context.fillStyle = "red";</pre></p></li>
<li><p>Чтобы отобразить текст, добавьте в <code>tutorial.js</code> следующий код и обновите страницу.</p>
<p> <pre>context.font = "bold 24px sans-serif";
context.fillStyle = "blue";
context.fillText("HTML5",100,100);</pre></p></li>
</ol>
<h2>Дополнительное задание</h2>
<p>Попробуйте залить прямоугольник градиентом. Для этого сначала вам нужно создать градиент (что объяснено здесь: <a href="http://diveintohtml5.org/canvas.html#gradients">Dive Into HTML5</a>), а затем установить этот градиент значением свойства fillStyle.</p>
<h1>УПРАЖНЕНИЕ 3</h1>
<h2>Работа с изображениями на canvas</h2>
<ol>
<li><p>Скопируйте файл <code>media/water.jpg</code> из папки <code>media</code> практикума в папку <code>media</code> вашего проекта. (Это изображение распростряняется на условиях лицензии <a href="http://www.flickr.com/photos/50183640@N05/5616041841/">Creative Commons-licensed</a>)</p></li>
<li><p>Выведите это изображение на canvas, добавив следующий код в <code>tutorial.js</code>:</p>
<p> <pre>var img = new Image();
img.src = "media/water.jpg";
img.onload = function() {
context.drawImage(img,0,110);
};</pre></p></li>
<li><p>Обновите страницу в браузере, чтобы увидеть картинку.</p></li>
<li><p>Добавьте два дополнительных параметра после 3 уже имеющихся, ширину и высоту области вывода для картинки:</p>
<p> <pre>context.drawImage(img,0,110,200,100);</pre></p></li>
<li><p>Обновите страницу. Размер картинки будет другим.</p></li>
<li><p>Для нашей простой игры нам понадобится файл со спрайтами <code>media/characters.gif</code>. Скопируйте его в папку <code>media</code> вашего проекта. (Я получил этот файл, лицензированный на условиях CC, от David E. Gervais, участника проекта <a href="http://pousse.rapiere.free.fr/tome/">TomeTik</a>)</p></li>
<li><p>Чтобы вырезать одного персонажа из файла и отобразить его на canvas, вам придется вызвать <code>drawImage</code> с 9 аргументами:</p>
<p> <pre>drawImage(image,sx,sy,sw,sh,dx,dy,dw,dh);</pre></p>
<p> Назначение каждого из этих агрументов объяснено на картинке:
<img src="http://images.whatwg.org/drawImage.png" alt="drawImage arguments" /></p>
<p> Каждый спрайт имеет ширину и высоту в 32 пикселя. Таким образом, если вы хотите вырезать второго персонажа в верхнем ряду, да-да, этого, в красном капюшоне, вам нужно вызвать <code>drawImage</code> с такими параметрами:</p>
<ul>
<li>sx = 33</li>
<li>sy = 0</li>
<li>sw = 32</li>
<li>sh = 32</li>
</ul>
<p> Добавьте dx, dy, dw и dh по вкусу, затем обновите страницу. Если что-то не получается, посмотрите решение в папке <code>ex3</code>.</p></li>
</ol>
<h1>УПРАЖНЕНИЕ 4</h1>
<h2>Простая анимация</h2>
<ol>
<li><p>Скопируйте <code>js/libs/jquery-1.5.2.js</code> в папку <code>js/libs</code> вашего проекта. jQuery понадобится нам для обработки событий клавиатуры и некоторых других задач.</p></li>
<li><p>Чтобы подключить jQuery, добавьте следующий тэг в конец файла index.html, перед загрузкой файла <code>tutorial.js</code>:</p>
<p> <code><script src="js/libs/jquery-1.5.2.js"></script></code></p></li>
<li><p>Закомментируйте большую часть кода, отвечающего за рисование. В этом упражнении мы начнем с чистого листа. Оставшийся в <code>tutorial.js</code> код должен выглядеть примерно так:</p>
<p> <pre>var canvas = document.getElementById("main");
var context = canvas.getContext("2d");
var characters = new Image();
characters.src = "media/characters.gif";</pre></p></li>
<li><p>Задайте значения переменных <code>x</code> и <code>y</code> равными нулю. В этих переменных будет храниться текущая позиция игрока.</p></li>
<li><p>Задайте переменные для хранения ширины и высоты canvas. В дальнейшем мы будем менять размеры canvas, поэтому имеет смысл не задавать эти переменные константами.</p>
<p> <pre>var height = $(canvas).height();</pre>
<pre>var width = $(canvas).width();</pre></p></li>
<li><p>Задайте обработчик для обработки события <code>onload</code> объекта <code>characters</code>. После загрузки изображения персонажа клавиатурные события будут обрабатываться функцией, которую мы напишем на следующем шаге:</p>
<p> <pre>$(window).keyup(move);</pre></p></li>
<li><p>Напишите функцию <code>move</code>, которая изменяет позицию персонажа на экране в зависимости от нажатой кнопки. На предыдущем шаге мы задали обрабочик клавиатурных событий. Этот обработчик должен принимать объект <code>event</code>, передаваемый jQuery. Этот объект имеет свойство <code>which</code>, указывающий на то, какая клавиша была нажата.</p>
<p> Коды клавиш:</p>
<ul>
<li>Вверх = 38</li>
<li>Вниз = 40</li>
<li>Влево = 37</li>
<li>Вправо = 39</li>
</ul>
<p> Увеличивайте значение <code>x</code> или <code>y</code> на 10, в зависимости от того, какая клавиша была нажата. Проверяйте значения <code>x</code> и <code>y</code> на предмет выхода за границы canvas (значения не должны быть меньше нуля или больше размеров canvas).</p>
<p> Если вы запутались, посмотрите как реализована эта функция в файле <code>ex4/js/tutorial.js</code>.</p></li>
<li><p>В конце тела функции <code>move</code> вызовите <code>context.drawImage</code>, чтобы вырезать изображение из файла со спрайтами и отобразить его на canvas в координатах, заданных <code>x</code> и <code>y</code>.</p>
<p> Если вы запутались, посмотрите, как это сделано в файле <code>ex4/js/tutorial.js</code>.</p></li>
<li><p>Обновите страницу и опробуйте анимацию. Персонаж должен неторопливо перемещаться по экрану при нажатии на клавиши со стрелками.</p></li>
<li><p>Персонаж оставляет за собой некрасивый след. Исправить это можно путем очистки экрана перед каждым выводом персонажа. Добавьте следующий код перед вызовом <code>drawImage</code>:</p>
<p> <pre>context.clearRect(0,0,width,height);</pre></p></li>
<li><p>Если хотите, заставьте персонажа перемещаться на большее расстояние при каждом нажатии клавиши.</p></li>
</ol>
<h2>Дополнительное задание</h2>
<p>Попробуйте создать цикл анимации, который осуществлял бы вывод на экран несколько раз в секунду. Это поможет отделить обработку событий клавиатуры от отображения. Для запуска цикла вам понадобится что-то вроде:</p>
<p><code>setInterval(runLoopFunction,interval);</code></p>
<p>Где <code>runLoopFunction</code> - имя вашей функции, а <code>interval</code> - количество миллисекунд, которые браузер должен выдерживать между вызовами функции.</p>
<h1>УПРАЖНЕНИЕ 5</h1>
<h2>Работаем с формами</h2>
<ol>
<li><p>Если мы сделаем нашу игру многопользовательской, нам нужно будет знать, как зовут игроков. Давайте добавим поле, которое принимает имя пользователя и использует новые возможности HTML5. Добавьте в файл <code>index.html</code> следующую строку:</p>
<p> <code><input id="username" placeholder="Ваше имя"></code></p></li>
<li><p>Обновите страницу. Если вы не видите текста "Ваше имя" в поле, проверьте значение <code>Modernizr.input.placeholder</code> в консоли JavaScript.</p></li>
<li><p>Добавьте к полю ввода аттрибут <code>autofocus</code> и обновите страницу. Если браузер поддерживает такую возможность, поле автоматически получит фокус.</p></li>
<li><p>Опробуем в деле новый элемент формы, ползунок, с помощью которого мы можем изменять размеры персонажа. Добавьте в <code>index.html</code> следующий код (1):</p>
<p> <code><input id="size" type="range" min="4" max="320" step="8" value="32"></code></p></li>
<li><p>Добавьте обработчик событий ползунка в файл <code>tutorial.js</code>. Обработчик должен изменять размер изображения, выводимого вызовом <code>drawImage</code>. Задать обработчик можно так:</p>
<p> <code>$('#size').change(function() { ... });</code></p>
<p> Получить значение ползунка можно с помощью <code>$('#size').val()</code></p></li>
</ol>
<p>(1) Firefox не отображает этот элемент. Выполните задание в Safari или Chrome.</p>
<h2>Дополнительное задание</h2>
<p>Попробуйте использовать другие элементы формы, описанные в книге <a href="http://diveintohtml5.org/forms.html">Dive Into HTML5</a>. В HTMl5 есть даже элемент для выбора цвета!</p>
<h1>УПРАЖНЕНИЕ 6</h1>
<h2>Локальное хранилище</h2>
<p><strong>Примечание</strong>: Это упражнение работает некорректно в Firefox из-за бага в обработке ссылок file://. Если вы хотите, чтобы все заработало и в Firefox, придется отдавать веб-страницу с сервера. <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=507361">Подробности</a></p>
<ol>
<li><p>Сохраните значение в локальном хранилище из консоли JavaScript:</p>
<p> <pre>localStorage.setItem('shaz','bot')</pre></p></li>
<li><p>Закройте страницу, затем откройте ее в новом окне и введите в консоли JavaScript такую команду:</p>
<p> <pre>localStorage.getItem('shaz')</pre></p>
<p> Если ваш браузер поддерживает локальные хранилища, функция должна вернуть значение <code>shaz</code>.</p></li>
<li><p>Попробуйте проделать то же самое с объектом <code>sessionStorage</code>. Чем <code>sessionStorage</code> отличается от <code>localStorage</code>? Что происходит, если вы просто обновляете страницу? Одинаково ли ведут себя объекты?</p></li>
<li><p>Вызовите <code>localStorage.clear()</code> и попробуйте еще раз найти элемент 'shaz'.</p></li>
<li><p>Добавьте обработчик события <code>change</code> для поля ввода имени пользователя, которое мы создали в упражнении 5:</p>
<p> <pre>$('#username').change(function() { ... });</pre></p></li>
<li><p>В обработчике сохраните имя пользователя с помощью <code>localStorage</code>. Чтобы вызвать событие <code>change</code> вам, возможно, нужно будет снять фокус с поля ввода. Чтобы получить значение поля ввода, используйте <code>$("#username").val()</code>.</p></li>
<li><p>Добавьте в <code>tutorial.js</code> код для получения имени пользователя из <code>localStorage</code> (которое мы сохранили на предыдущем шаге) при загрузке страницы. Если имя пользователя найдено в хранилище, восстановите ранее сохраненное имя пользователя в поле ввода, например так: <code>$("#username").val(nameStr)</code>.</p></li>
<li><p>Обновите страницу. Введите имя пользователя, снимите фокус с поля (чтобы убедиться, что событие <code>change</code> вызвано), затем снова обновите страницу. На этот раз поле ввода должно содержать имя пользователя вместо текста по умолчанию.</p></li>
<li><p>Попробуйте сохранять и извлекать из локального хранилища числа и хэши. Сохраняет ли локальное хранилище тип?</p></li>
</ol>
<p><em>Консоль JavaScript может обладать функцией просмотра локального хранилища и хранилища сессии. В инструменте для разработчиков движка WebKit содержимое хранилищ можно посмотреть на соответствующей вкладке.</em></p>
<h2>Дополнительное задание</h2>
<ol>
<li><p>Добавьте к объекту <code>window</code> обработчик события <code>storage</code>, чтобы отслеживать добавление новых элементов в локальное хранилище. Если не знаете, с чего начать, начните отсюда: <a href="http://diveintohtml5.org/storage.html#storage-event">Dive Into HTML5</a>.</p></li>
<li><p>Что произойдет, если попытаться обратиться к элементу, которые не был сохранен в хранилище?</p></li>
</ol>
<h1>УПРАЖНЕНИЕ 7</h1>
<h2>Очистка canvas</h2>
<ol>
<li><p>Прежде, чем добавить в нашу "игру" возможности мультиплеера, поработаем над ее внешним видом. Установим для canvas темный фон. Добавьте этот код в тэг <code><head></code> файла <code>index.html</code>:</p>
<pre><code> <style>
canvas {
background-color: black;
}
input { display: block; }
</style>
</code></pre></li>
<li><p>Если хотите, можно увеличить ширину и высоту canvas.</p></li>
<li><p>Увеличьте скорость перемещения персонажа так, чтобы он перемещался на 5 или 10 пикселей на одно нажатие клавиши.</p></li>
</ol>
<h1>УПРАЖНЕНИЕ 8</h1>
<h2>Web Sockets</h2>
<p><strong>Примечание</strong>: WebSockets не работают в Firefox. Вы можете исправить это (частично) с помощью <a href="http://socket.io/">socket.io</a>.</p>
<ol>
<li><p>Подключим websocket-сервер для обмена информацией с другими игроками. Добавьте в <code>tutorial.js</code> код, приведенный ниже. Я заметил, что если этот код выполняется до загрузки <code>characters.gif</code>, это иногда приводит к ошибкам. Возможно, имеет смысл вставить этот код в обработчик события <code>onload</code> картинки. Если вам непонятно, о чем речь, загляните в решение для упражнения 8.</p>
<p> <pre>var ws = new WebSocket("ws://exp.subelsky.com:8011");
ws.onmessage = handleMessage;
function handleMessage(event) {
console.info(event.data);
}</pre></p></li>
<li><p>Обновите страницу. Каждые 10 секунд с сервера должно приходить сообщение "ping". Обратите внимание, что это сообщение - JSON-строка, поэтому прежде чем работать с ней, ее надо десериализовать.</p></li>
<li><p>Откройте <code>server/server.rb</code>, чтобы понять, к чему мы подключаемся.</p></li>
<li><p>Измените функцию <code>handleMessage</code>, добавив разбор JSON-строки. Если ваш браузер не имеет встроенной поддержки JSON, подключите к проекту скрипт <code>js/libs/json2.js</code>.</p>
<p> <pre>var msg = JSON.parse(event.data);
console.info(msg);</pre></p></li>
<li><p>Обновите страницу, подождите 10 секунд и проверьте консоль. Вы должны увидеть десериализованный JSON-объект.</p></li>
<li><p>Измените функцию <code>move</code>, добавив в нее отправку JavaScript-объекта в websocket каждый раз при перемещении персонажа. Воспользуйтесь методом <code>websocket.send</code>:</p>
<p> <pre>ws.send(JSON.stringify({ name: name, x: x, y: y, type: "move" }));</pre></p></li>
<li><p>Посмотрите логи сервера, выводимые на экран. Каждый раз при нажатии клавиши перемещения в логе должны появляется сообщения о перемещении персонажа.</p></li>
</ol>
<p>Если вы подумываете о создании приложения с использованием websocket, обязательно посмотрите проект <a href="http://pusherapp.com/">Pusher</a>. Возможно, вам не придется писать собственный сервер.</p>
<h2>Дополнительное задание</h2>
<p>Сервер передает события передвижения всему классу <em>(практикум проводился на конференции, компьютеры студентов были объединены в сеть - прим. пер.)</em>. Чтобы отобразить перемещения других студентов на экране, нужно отследить их имена и координаты x и y (реализовать это лучше с помощью кэша, ключами которого будут имена пользователей, а значениями - координаты). Измените вывод <code>handleMessage</code> так, чтобы он давал информацию о нескольких игроках, показывая другую картинку для чужих персонажей.</p>
<p>Мы не проверяем имена пользователей на уникальность, поэтому постарайтесь сразу договориться с другими студентами о разных именах.</p>
<h1>УПРАЖНЕНИЕ 9</h1>
<h2>Работа с мультимедия (и пользовательскими аттрибутами)</h2>
<p>Добавим в игру звуковые эффекты и воспользуемся пользовательскими аттрибутами HTML5 для упрощения управления.</p>
<ol>
<li><p>Добавьте в тело <code>index.html</code> такой список:</p>
<pre><code> <ul>
<li><a href="#" data-soundname='bubble'>Бульк</a></li>
<li><a href="#" data-soundname='ray_gun'>Пыщь</a></li>
</ul>
</code></pre></li>
<li><p>С помощью jQuery добавьте обработчик события <code>click</code> для тэга <code><a></code>. Определить, какой звук требуется проиграть, можно спомощью значения пользовательского аттрибута события, как это показано ниже. Здесь я использовал кросс-браузерное решение, а также вариант, предложенный в <a href="http://dev.w3.org/html5/spec/elements.html#embedding-custom-non-visible-data">спецификации HTML5</a>, который, похоже, поддерживает только Chrome.</p>
<pre><code> $('a').click(function(evt) {
// не слишком элегантно, зато работает во всех браузерах
$('#'+evt.target.getAttribute('data-soundname'))[0].play();
// спецификация HTML5 предлагает изящный API, но работает это, по-видимому, только в Chrome
// $('#'+evt.target.dataset.soundname)[0].play();
});
</code></pre>
<p> Не стоит путать пользовательские аттрибуты с микроданными - они не предназначены для внешней обработки. Подробне о микроданных можно почитать в <a href="http://diveintohtml5.org/extensibility.html">Dive Into HTML5</a>.</p></li>
<li><p>Обновите страницу. Щелкните каждую ссылку, чтобы убедиться, что свойство <code>dataset</code> доступно и получает нужное значение.</p></li>
<li><p>Воспроизведение звука и видео в HTML5 подразумевает возню с кодеками. Обычно контент делают доступным в нескольких форматах. Я добавил звуковые файлы в четырех разных форматах. Скопируйте звуковые файлы из папки <code>media</code> в ваш проект. Если вы отдаете эти файлы с сервера (а не через file://), возможно, вам придется повозиться с MIME, так как HTML5 не будет воспроизводить файлы, отданные с неверным MIME типом. Подробнее см. <a href="http://diveintohtml5.org/video.html#video-mime-types">MIME Types</a>.</p>
<p> Следующий код должен работать для большинства пользователей. Спецификация предприсывает браузеру воспроизводить первый источник, который он способен воспроизвести.</p>
<pre><code> <div style="display:hidden">
<audio id="bubble" preload>
<source src="media/bubble.ogg">
<source src="media/bubble.mp3">
<source src="media/bubble.wav">
</audio>
<audio id="ray_gun" preload>
<source src="media/ray_gun.ogg">
<source src="media/ray_gun.mp3">
<source src="media/ray_gun.wav">
</audio>
</div>
</code></pre>
<p> Я решил добавить звуковые файлы прямо в страницу. В этом случае, если HTML5-аудио не сработает, браузер обработает их как обычно. А вообще аудио-объекты можно создавать также, как и картинки:</p>
<pre><code> var audio = new Audio;
audio.src = "http://...";
</code></pre></li>
<li><p>Обновите страницу. Попробуйте проиграть оба файла в консоли:</p>
<p> <pre>$('#bubble')[0].play()
$('#ray_gun')[0].play()</pre></p></li>
<li><p>Измените ссылку так, чтобы указанные файлы воспроизводились автоматически. Воспользуйтесь кодом из предыдущего шага.</p></li>
<li><p>Чтобы посмотреть, как выглядят элементы управления звуком по умолчанию, уберите стиль <code>display:hidden</code> для <code><div></code> и добавьте аттрибут <code>controls</code> после <code>preload</code>. Обновите страницу.</p></li>
<li><p>Вставка видео происходит также. Для обеспечения совместимости придется предоставить несколько форматов видео. Скопируйте файлы <code>short.mov</code>, <code>short.mp4</code>, <code>short.ogv</code> и <code>short.webm</code> из папки <code>media</code> в папку <code>media</code> вашего проекта.</p>
<p> Видео было сконвертировано из файла QuickTime с помощью <code>ffmpeg2theora</code>, <code>ffmpeg</code> и <code>HandBrakeCLI</code>. Настройки конвертации были подсмотрены в <a href="http://diveintohtml5.org/video.html">Dive Into HTML5</a>.</p></li>
<li><p>Добавьте в конец <code>index.html</code>:</p>
<pre><code> <video width="320" height="240" preload controls>
<source src="media/short.ogg" type='video/ogg; codecs="theora, vorbis"' />
<source src="media/short.mp4" />
<source src="media/short.mov" />
</video>
</code></pre></li>
<li><p>Обновите страницу. Один из видеофайлов должен отобразиться в вашем браузере.</p>
<p>Потрясающий пример использования canvas для манипулирования изображением из видео можно найти <a href="http://html5demos.com/video-canvas">здесь</a>. В этом <a href="http://html5demos.com/video">демо</a> показано использование событий вставленного медиаконтента для отображения таймера.</p></li>
</ol>
<h2>Дополнительное задание</h2>
<p>Если воспроизведение мультимедия на поддерживается браузером, воспользуйтесь флеш-плеером <a href="http://flowplayer.org/">FlowPlayer</a> (для этого нужно добавить тэг <code><object></code> после тэгов <code><source></code>. Подробно эта техника освещена в статье <a href="http://camendesign.com/code/video_for_everybody">Video for Everybody</a>).</p>
<h1>УПРАЖНЕНИЕ 10</h1>
<h2>Геолокация</h2>
<ol>
<li><p>Введите эту команду в консоль JavaScript:</p>
<p> <pre>navigator.geolocation.getCurrentPosition(function(loc) { console.info(loc.coords) }, function(err) { console.error(err) })</pre></p></li>
<li><p>Посмотрите на объект локации в консоле. Если поискать эти координаты на Google Maps, то результат окажется довольно точным! Интегрировать геолокацию с Google Maps очень легко, и это позволит показывать положение пользователя на карте. К сожалению, из-за особенностей аутентификации Google Maps API сделать это с localhost не получится.
<a href="http://html5demos.com/geo">Здесь</a> расположено простое демо - обязательно изучите исходный код страницы.</p>
<p> Первая функция обратного вызова вызывается, если браузер может определить свое положения, вторая - если не может. На моей машине вторая функция вызывается в Safari, если компьютер подключен к сети через Ethernet.</p></li>
</ol>
<h2>Дополнительное задание</h2>
<p>Посмотрите на <a href="https://simplegeo.com/docs/tutorials/javascript">SimpleGeo</a> примеры того, что можно сделать, если знать примерные координаты пользователя.</p>
<h1>УПРАЖНЕНИЕ 11</h1>
<h2>Web Workers</h2>
<ol>
<li><p>Изучите файл <code>js/worker.js</code> и скопируйте его в свой проект. Это простой алгоритм перебора, призванный найти все множители данного целого числа.</p></li>
<li><p>Обновите страницу и введите в консоль JavaScript следующие команды:</p>
<p> <pre>var worker = new Worker('js/worker.js');
worker.addEventListener('message', function(e) {
console.info(e.data);
},false);
worker.postMessage(100);</pre></p>
<p> <strong>Если вы используете Chrome</strong>, при открытии <code>index.html</code> как file:// будет вызвано исключение безопасности. Отключить его можно, запустив Chrome из консоли с особым ключом.
На OS X это выглядит так:</p>
<p> <pre>open -n -a 'Google Chrome.app' --args --allow-file-access-from-files</pre></p>
<p> А можно просто воспользоваться другим браузером.</p></li>
<li><p>Worker должен отправить в консоль сообщение. Попробуйте передадать в <code>worker.postMessage</code> большее число. Достаточно большое число (например, 1 000 000) приведет к длительным вычислениям. Обратите внимание, что страница останется отзывчивой при выполнении такого фонового задания.</p></li>
</ol>
<p>Вот <a href="http://nerget.com/rayjs-mt/rayjs.html">более сложный пример использования webworker</a>.</p>
<h1>УПРАЖНЕНИЕ 12</h1>
<h2>Оффлайновые приложения</h2>
<ol>
<li><p>Изучите файл <code>index.html</code> в папке <code>manifest</code>. Это сокращенная версия решения упражнения 11. Обратите внимание на тэг <code><html></code>, который в этой версии содержит ссылку на <code>demo.manifest.</code></p></li>
<li><p>Изучите <code>demo.manifest</code>.</p></li>
<li><p>Если у вас установлен сниффер, натравите его на порт 80. Если сниффера нет, убедитесь, что консоль JavaScript отслеживает сетевой траффик.</p></li>
<li><p>Зайдите на <a href="http://files.subelsky.com/manifestdemo/index.html">http://files.subelsky.com/manifestdemo/index.html</a></p></li>
<li><p>Посмотрите в сниффере или консоли JavaScript, какие файлы скачиваются. Обратите внимание на MIME-тип файла <code>demo.manifest</code> (<code>text/cache-manifest</code>).</p></li>
<li><p>Теперь обновите страницу. Если все заработает как надо, траффик будет состоять из единственного запроса проверки файла <code>demo.manifest</code>, который даже не будет скачан, т.к. он не изменялся (о чем свидетельствует статус <code>304</code> HTTP-ответа).</p>
<p> Используя эту технику, можно "установить" HTML5-приложение на смартфон.</p></li>
</ol>
<h2>Дополнительное задание</h2>
<p>Запустите <code>manifest/index.html</code> на локальном сервере. Папка с приложением должна отдаваться с веб-сервера (а не через файл <code>file://</code>), а файл с манифестом должен иметь правильный MIME-тип. В Apache это достигается следующей директивой в <code>httpd.conf</code>:</p>
<pre><code>AddType text/cache-manifest .manifest
</code></pre>
<h1>СЛИШКОМ ПРОСТО?</h1>
<p>Вот еще несколько возможностей HTML5, о который стоит знать, если этот практикум вам не подошел, или если вы считаете, что пока эти технологии слишком нестабильны для практического применения:</p>
<ul>
<li><p><a href="http://diveintohtml5.org/extensibility.html">Microdata</a></p></li>
<li><p><a href="http://www.queness.com/post/7459/8-stunning-javascript-webgl-demonstrations">WebGL</a></p></li>
<li><p><a href="http://html5demos.com/history/">HTML5 History API</a></p></li>
<li><p><a href="http://html5doctor.com/methods-of-communication/">Альтернатива websockets</a></p></li>
<li><p><a href="https://github.com/Modernizr/Modernizr/wiki/HTML5-Cross-browser-Polyfills">Polyfills, или как добавить кросс-браузерную поддержку HTML5</a>, также посмотрите <a href="http://html5doctor.com/how-to-get-all-the-browsers-playing-ball/">Uber</a></p></li>
<li><p><a href="http://www.css3.com/">Возможности CSS3: закругленные углы, 2D-преобразования и т.д.</a></p></li>
</ul>
<h1>ПОЛЕЗНЫЕ ССЫЛКИ</h1>
<ul>
<li><p><a href="http://diveintohtml5.org/">Dive Into HTML5</a></p></li>
<li><p><a href="http://html5doctor.com/">HTML5 Doctor</a></p></li>
<li><p><a href="http://html5demos.com/">HTML5 Demos</a></p></li>
<li><p><a href="http://www.html5rocks.com/">HTML5 Rocks</a></p></li>
<li><p><a href="https://developer.mozilla.org/en/Canvas_tutorial">Mozilla Canvas Tutorials</a></p></li>
<li><p><a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/">WHATWG Living Standard</a></p></li>
</ul>
<h1>БЛАГОДАРНОСТИ</h1>
<p>Автор благодарит <a href="http://jumpstartlab.com/">Jeff Casimir</a> за помощь в подготовке этого практикума и <a href="http://diveintomark.org/">Mark Pilgrim</a> за написание книги Dive Into HTML5, которая стала большим подспорьем. Разумеется, все ошибки в этом практикуме на совести самого автора!</p>
<h1>ОБРАТНАЯ СВЯЗЬ</h1>
<p>Спасибо за то, что заинтересовались этим практикумом. Связаться со мной можно тут: <a href="mailto:mike@subelsky.com">mike@subelsky.com</a>, или в твиттере: <a href="http://twitter.com/subelsky">@subelsky</a>. Я обожаю разговоры о HTML5, поэтому не стесняйтесь связываться со мной, если у вас возникли вопросы, или вы хотите обсудить интересные задачи.</p>
<p>Большинству приемов, которые обсуждаются в этом практикуме, я научился во время работы над игрой для программистов, использующей возможности HTML5. Игра эта называется EXP, и нам нужны бета-тестеры. Заявку на участие можно оставить здесь: <a href="http://exp.subelsky.com/">exp.subelsky.com</a>.</p>
</body>
</html>