STAY HUNGRY , STAY FOOLISH.

求知若饥,虚心若愚。

       浏览:

阅读之HTML5移动Web开发实战详解

第一章 移动互联网的浪潮之巅

HTML是用来承载信息的载体,那么HTML5是用来承载更多更强大信息的载体。
Web标准的精髓不仅仅在于内容结构、表现和行为的分离,更在于提供结构化互联网信息的工具。

Web标准就是降低开发复杂度,简化代码的规范。

Web语义化指让机器理解内容。

爬虫程序是一种自动访问互联网页面并收集信息以供人们检索的程序。

HTML5有八个类别:

  1. 图像和特效
    SVG、Canvas、WebGL

  2. 通信
    Web Socket、Server-Sent Events

  3. 设备访问
    Orientation API、Geolocation API

  4. 多媒体
    Video、Audio

  5. 离线存储
    HTML5 App Cache、Local Storage、Indexed DB、File API

  6. 性能和集成
    Web Worker

  7. 语义网
    语义化标签 header、footer等

  8. 呈现
    CSS3


第二章 HTML5基础

2.1 HTML的核心要素:标签


2.2 HTML的语义来源:元素名称、元素属性


2.3 HTML5的元素和属性:

2.3.1 全局属性

常用属性:id、class、style、lang、translate、dir、title、accesskey、tabindex
contenteditable、contextmenu、draggable、dropzone、hidden、spellcheck、data-*


事件处理属性(默认前面加on):
Window事件:
load、unload
afterprint、beforeprint、beforeunload、error、haschange、message、offline、online、page hide、pageshow、pop state、redo、resize、storage、undo

Form事件:
blur、change、focus、reset、select、submit
contextmenu、formchange、forminput、input、invalid

Keyboard事件:
keydown、keypress、keyup

Mouse事件:
click、dbclick、drop、mousedown、mousemove、mouseup、mouseover、mousemouseout
drag、dragstart、dragenter、dragend、dragover、dragleave、mousewheel、scroll

Media事件:
abort、waiting
canplay、canplaythrough、durationchange、emptied、ended、error、loadeddata、loadedmetadata、loadstart、pause、play、playing、progress、ratechange、readystatechange、seeked、seeking、stalled、suspend、timeupdate、volumchange


2.3.2 HTML5与它的全局变量

1)contentEditable属性:元素内容是否可编辑
2)contextmenu属性:设置右键菜单属性
3)draggable属性:元素是否可拖动
dropzone属性:元素是否可放置拖拽的元素
4)hidden属性:设置元素不再与页面相关
5)spellcheck属性:是否对元素进行拼写和语法检查
6)data-*属性:自定义属性来存储数据


2.3.3 元素分类与内容模型(Content Model)

HTMLL元素所能表达的内容的描述及与这些元素如何互相作用的描述叫内容模型。
简单说就是不能按照之前用CSS的样式来把HTML元素划分成块级元素、内联元素,而是按照内容模型来划分。
内容模式大致分成七类:

  • Metadata
  • Flow
  • Sectioning
  • Heading
  • Phrasing
  • Embedded
  • Interactive

HTML中的每个元素都可以是上面分类的零个或多个。
1)元数据式内容(Metadata content)
指head元素之间的元素,如:base、command、link、meta、noscript、script、style、title

2)流式内容(Flow content)
指文档大部分元素,如:div、p

3)章节式内容(Sectioning Content)
指用于定义标题与页脚范围之间的元素,如:article、aside、nav、section

4)标题式内容(Heading Content)
指定义标题的元素,如:h1~h6,hgroup

5)段落式内容(Phrasing Content)
指段落内的元素,如:a、span、abbr

6)嵌入式内容(Embedded Content)
指当前文档引用到其他资源的元素,如:audio、canvas、embed、iframe、img、object、svg、video

7)交互式内容(Interactive Content)
指与用户发生交互的元素,如:form、input


2.3.4 文档元数据

1)title:文档的标题或名称,标签之间是纯文本
2)base:文档的基地址及链接打开方式
3)link:引用文档其他资源
4)meta:提供页面元信息


2.3.5 区块内容

该类标签如下:

  • body
  • article
  • section
  • nav
  • aside
  • h1~h2、hgroup
  • header、footer
  • address

1)article:用来定义内容可单独发布或重用的文档、页面、应用等。如:一篇帖子、文章、评论甚至一个页面交互组件

2)section:用来定义需要出现在文档的提纲中的章节或区块。如:一篇文章的章节

3)nav:用来定义导航区块。如:导航栏

4)aside:用来定义当前已有内容的相关内容。如:一篇文章的相关背景、广告、引述、评论等

5)h1~h6:用来定义区块的标题,h1级别最高
hgroup:用来定义多个级别的标题,如:标题和副标题

6)header:用来定义头部的内容,footer:用来定义底部的内容。如:目录标题、搜索框、logo、友情链接等
与前面的article、section、nav、aside相比,header和footer不具备产生大纲视图的特性。

7)address:用来定义它最近的父级article或body元素里内容相关的联系信息。


2.3.6 分组内容

p:段落
hr:水平线
pre:已排版的内容
blockquote:引用来自其他来源的内容
ol:有序列表
ul:无序列表
li:列表项
dl:定义列表
dt:定义的项目
dd:定义的描述
div: 块级元素
figure:定义插图注解、图表、照片、代码列表等
figcaption:figure元素的标题


2.3.7 文本内容

a:超链接
em:强调
strong:内容重要性
small:旁注,如免责声明、使用条款、版权信息等
s:有误文本,如商品标价
cite:作品标题的引用,如书影音画等
q:短引用,如某人的一句话
dfn:定义的实例,通常用来定义术语
abbr:缩写词,可以配合dfn定义术语
code:计算机代码
var:定义变量
samp:计算机程序的输出
kbd:用户输入(按键),如:Enter
sub:下标文本
sup:上标文本
i:斜体
b:粗体
u:下划线
bdo:定义文本显示的方向
span:无语义
br:换行
wbr:规定在文本中的何处适合添加换行符
data:为元素赋予机器可读的数据
time:data标签的时间格式版本
mark:标记或高亮文本
ruby,rt,rp:注音标示
bdi:定义文本的文本方向,如:双向文本排版


2.3.8 修改记录

ins,del:表示文档的增删改记录。


2.3.9 嵌入内容

1)img:图片

2)iframe:窗体

3)embed:Flash插件、视频

4)object:视频

5)vidoe:视频、audio:音频

6)canvas:游戏、3D/2D效果

7)mathml:数学公式

8)svg:渲染


2.3.10 表格数据

table:表格
caption:表格标题
colgroup:对表格中的列进行组合
col:表示列
tbody:一段表格主体
thead:表格表头
tfoot:表格的页脚
tr:表格中的行
td:表格中的单元格
th:表格表头中的单元格


2.3.11 HTML5表单

HTML5给表单input输入框提供了很多类型及更丰富的属性。


2.3.12 input元素和其属性

input的type值可以为:email、tel、url、serach、color、number、range、date等。

required属性:该字段必须要填写才能提交
autocomplete属性:填写该文本时会出现记录以前填写时的内容
placeholder属性:文本框提示
pattern属性:该文本输入的内容需要通过该正则验证后才能提交
min属性:最小值 type值为number时
max属性:最大值 type值为number时
step属性:调整数字的步长 type值为number时
list属性:配合datalist元素,可实现自动提示功能
autofocus属性:自动聚焦到搜索框内


后头直接省略,理由:浏览器对HTML5表单的兼容性还有很长一段路要走。


第三章 初探CSS3

3.2.1 特殊性

比如多个CSS样式操作同一个元素的背景色,其最终样式取决于特殊性的加权结果。组合特殊性值越大,该样式对元素起作用。基本规则如下:

  • 1.内联样式,及使用style属性的 +1
  • 2.使用元素或伪元素选择器(:before) +1
  • 3.使用类或伪类选择器(:hover) +1
  • 4.使用ID选择器 +1

3.6.1 透视 perspective

/* 透视深度*/
-webkit-perspective: 250px;
/* preseve-3d指定元素的子元素在3d空间内定位 /
-webkit-transform-style: preserve-3d;
/
指定用户从哪个方向看过来的 */
-webkit-perspective-origin: -100% -50%;


3.7响应式设计基础

响应式设计可以理解成”PC端和移动端共享一套代码”,其主要包含的内容有:

  1. 使用流式布局或栅格布局以适应不同屏幕。
  2. 使用CSS3媒体查询技术对不同尺寸的屏幕进行样式微调。
  3. 使用流式图片

基本概念:
CSS像素与设备像素
设备像素(device pixel):屏幕上最小的发色单元。
CSS像素(CSS pixel):定义的样式尺寸大小。

PPI与设备像素比
PPI:设备像素密度。
设备像素比(devicePixelRatio):设备上物理像素和设备独立像素的比例。
设备像素比可用来判断设备是否是retina设备。

视口(viewport)
视口:表示浏览器窗口的可视区域。
移动设备浏览器定义了两个视口:

  1. 布局视口:默认是980px,决定桌面版网站的CSS
  2. 可见视口:决定移动版网站的CSS

meta的name属性为viewport时,content属性值可以为:

  • width:布局视口宽度
  • height:布局视口高度
  • initial-scale:初始缩放比例(0~10.0)
  • minimum-scale:最小缩放比例
  • maximum-scale:最大缩放比例
  • user-scalable:用户是否可缩放,默认yes

3.7.2 媒体查询(media queries)

语法:
@media :<sMedia> { sRules }
取值:
<sMedia>:指定设备名称。
{sRules}:样式表定义。

media_query: [only | not]? [ and <expression> ]*

expression: ( <media_feature> [: <value>]? )

media_type: all | aural | braille | handheld | print | projection | screen | tty | tv | embossed

media_feature(”min-“或”max-“): width | height | device-width | device-height | color | color-index | resolution | orientation | scan | grid | device-aspect-ratio | monochrome

在JavaScript中,可使用matchMedia()检查媒体查询是否成功,即看其matches属性是true还是false。


3.7.3 响应式栅格系统

HTML代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
<div class="container">
<div class="row">
<div class="header">header</div>
</div>
<div class="row">
<div class="col1"> nav</div>
<div class="col2"> main </div>
<div class="col1"> aside </div>
</div>
<div class="row">
<div class="footer">footer</div>
</div>
</div>

CSS代码如下:

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
/* 普通屏幕使用 960的宽度 */
.row {
width: 960px;
}
.row:after {
clear: left;
content: '';
display: table; /* 清除行中浮动 */
}
[class^="col"] {
float: left;
}
.col1 {
width: 25%;
}
.col2 {
width: 50%;
}
.col3 {
width: 75%;
}

/* 屏幕设备宽度大于 1200px 时row宽度固定为 1170px */
@media (min-width: 1200px) {
.row {
width: 1170px;
}
}

/* 对于平板电脑每行宽度为 724px */
@media (min-width: 768px) and (max-width: 979px) {
.row {
width: 724px;
}
}

/* 横屏的手机或者竖屏的平板 */
@media (max-width: 767px) {
.row {
width: 100%;
}
}
/* 竖屏的手机 */
@media (max-width: 480px) {
/* 这里是可能一些微调 */
}

3.7.4 移动优先理念

对于一个新产品,先设计移动版,然后才是桌面版。
这样的好处是:

  1. 产品功能会更简洁
  2. 移动设备可以用的功能电脑上同样可以用
  3. 能写出性能更好的程序
  4. 可加新功能,因为加功能比删功能更容易

3.7.5 另一种思路:后端模块输出的优化

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
<head>
<link rel="stylesheet" href="common.css">
<% if (!is_mobile()) {%>
<link rel="stylesheet" href="desktop.css">
<% } else { %>
<link rel="stylesheet" href="mobile.css">
<% } %>
<script>
function is_mobile(){
var ua=navigator.userAgent.toLowerCase();
if (/iphone|ipad|ipod/.test(ua)){
reutrn true;
}else if(/android/.test(ua)){
return true;
}else{
retur false;
}
}
</script>
</head>
<body>
<div class="header"></div>
<div class="main">
<div class="main-content"></div>
<% if (!is_mobile()) {%>
<div class="side"></div>
<% } %>
</div>
<div class="footer"></div>
</body>

3.7.6 其他细节

触摸和非触摸

1
document.documentElement.className+=('ontouchstart' in window) ? 'touch' : 'no-touch'
1
2
3
4
html .no-touch .item:hover{
cursor:pointer;
background-color:#ff9;
}

retina屏幕的图片使用

1
2
3
4
5
6
7
8
.icon{
background-image:url(icon.png);
}
@media all and (max-width:320px) and (-webkit-min-device-pixel-ratio:2){
.icon{
background-size:50% 50%;
}
}

第四章 从网页到应用

4.2 本地存储升级

cookie是如何存储的?
用户浏览器第一次向服务器发生请求时,服务器会返回set-Cookie:xxx,浏览器会记下xxx,当浏览器再次请求服务器时会带上Cookie:xxx。
简单说,cookie会随着HTTP请求发送到服务器端。

cookie是如何设置的?

1
document.cookie="name=value"

cookie的大小和数量限制?
IE6及以下 每个域最多20个
IE7及以上 每个域最多50个
Firefox 每个域最多50个
Google、Safari 没明确限制,但是cookie超过了HTTP头部大小的限制时,服务器将无法处理

cookie的大小限制在4096B

cookie不适合在本地存储数据,本地存储大量数据请使用Web Storage。


4.2.2 web storage

web storage不会随着HTTP请求发送到服务器端,使用它不会影响网站的性能。

浏览器不允许开发者实例化Storage对象,已经有两个实例化好的对象,分别是localStorage和sessionStorage。
localStorage会持久化数据,当关闭浏览器再打开网站,依然可以访问这个域的数据,而使用sessionStorage不能。

web storage使用:

1
2
3
4
5
6
7
8
9
10
11
12
//设值
localStorage['ww']=JSON.stringify({name:"ww",age:22});
localStorage.setItem('zl',JSON.stringify({name:'zl',age:22}));
//获取
localStorage.length;
localStorage.key('0');
JSON.parse(localStorage['ww']);
JSON.parse(localStorage.getItem('zl'));
//删除
delete localStorage['ww'];
localStorage.removeItem('zl');
localStorage.clear();//清除所有

4.3离线应用

4.3.1 缓存和应用缓存

应用缓存就是将服务器的资源文件缓存至本地,有以下三大优点:

  • 加速应用启动速度
  • 离线访问页面
  • 节省服务器资源,减少请求

4.3.2 应用缓存的基本用法

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
<script>
function getAppCacheStatus() {
var appCache = window.applicationCache;
// status 是一个整数,appCache上定义了一系列常量表示缓存的状态
switch (appCache.status) {
// UNCACHED === 0,未缓存状态,表示应用缓存对象还没有初始化完成。
case appCache.UNCACHED:
return 'UNCACHED';
break;
// IDLE === 1,空闲状态,应用缓存此时未处于更新过程中。
case appCache.IDLE:
return 'IDLE';
break;
// CHECKING === 2,检查状态,清单已经获取完毕并检查更新。
case appCache.CHECKING:
return 'CHECKING';
break;
// DOWNLOADING === 3,下载资源并准备加入到缓存中,这是由于清单文件变化引起的。
case appCache.DOWNLOADING:
return 'DOWNLOADING';
break;
// UPDATEREADY === 4,更新就绪状态,一个新版本的应用缓存可以使用。
// 该状态有一个对应的事件 updateready,当下载完毕一个更新,并且还未使用 swapCache() 方法激活更新时,该事件触发,而不会是 cached 事件。
case appCache.UPDATEREADY:
return 'UPDATEREADY';
break;
// OBSOLETE === 5,废弃状态,应用缓存现在被废弃。
case appCache.OBSOLETE:
return 'OBSOLETE';
break;
default:
return 'UKNOWN CACHE STATUS';
break;
};
}
</script>

<script>
// load 事件后再进行监听。
window.addEventListener('load', function(e) {

window.applicationCache.addEventListener('updateready', function(e) {
if (getAppCacheStatus() === 'UPDATEREADY') {
// 此时浏览器已经下载好了需要被缓存的文件
// 调用swapCache()方法以填充
window.applicationCache.swapCache();
// 在重新加载页面之前,最好提示用户
if (confirm('本程序有更新,是否刷新?')) {
window.location.reload();
}
} else {
// 此时manifest文件无更新。
}
}, false);
}, false);
</script>

<script>
function handleCacheEvent(e) {
//...
}

function handleCacheError(e) {
alert('Error: Cache failed to update!');
};

// manifest第一次加载缓存时会触发
appCache.addEventListener('cached', handleCacheEvent, false);

// 正在检查更新,这个事件永远是第一个触发的。
appCache.addEventListener('checking', handleCacheEvent, false);

// 有更新,浏览器正在下载资源文件
appCache.addEventListener('downloading', handleCacheEvent, false);

// 当这些情况出现时会触发error事件:
// 1. manifest文件返回404或者410状态时
// 2. 下载资源失败时
// 3. 正在下载资源文件时却发现manifest文件更新了时
appCache.addEventListener('error', handleCacheError, false);

// 第一次下载manifest文件之后触发
appCache.addEventListener('noupdate', handleCacheEvent, false);

// manifest文件返回404或者410状态,此时缓存将会被删除
appCache.addEventListener('obsolete', handleCacheEvent, false);

// 每一个资源文件在获取时都会触发一次progress事件
appCache.addEventListener('progress', handleCacheEvent, false);

// 最近一次manifest资源被重新下载时触发
appCache.addEventListener('updateready', handleCacheEvent, false);
</script>

第五章 指尖下的浏览器

touch事件:

  • touchstart:开始触摸时
  • touchmove:触摸移动时
  • touchend :触摸结束时

触摸事件对象e包含的属性有:

  • touches:所有屏幕上所有手指动作的列表,是一个TouchList类型的对象,对象里有多个Touch对象
  • targetTouches:当前DOM元素上所有手指动作列表
  • changedTouches:当前改变的手指动作列表

每一根手指都会产生一个Touch对象,Touch对象包含的属性有:
identifier:一个数字,标识唯一
target:动作目标的DOM元素
clientX、clientY:触摸点相对于浏览器视口的位置
pageX、pageY:触摸点相对于页面的位置
screenX、screenY:触摸点相对于屏幕的位置


第六章 地理定位

百度地图我的位置:

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
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
<style type="text/css">
body, html,#allmap {width: 280px;height: 200px;overflow: hidden;margin:0;font-family:"微软雅黑";}
</style>
<script src="http://cdn.bootcss.com/jquery/2.1.4/jquery.js"></script>
<title>添加动画标注点</title>
</head>
<body>
<div id="allmap"></div>
</body>
</html>
<script type="text/javascript">
//百度地图API功能
window.onload=function() {
var script = document.createElement("script");
script.type = "text/javascript";
script.src = "http://api.map.baidu.com/api?v=2.0&ak=X5w1ZLsQvWwxRwNv5nonp4MX&callback=init";
document.body.appendChild(script);
}
function init() {
var map = new BMap.Map("allmap");
var point = new BMap.Point(104.083963,30.623718);
map.centerAndZoom(point,18);
var marker = new BMap.Marker(point);//创建标注
map.addOverlay(marker);//将标注添加到地图中
map.addEventListener("tilesloaded",function(){
setTimeout(function(){
$(".BMap_cpyCtrl").remove();
},500);
});
}
</script>

第七章 Web Worker

Web Worker技术是为了解决JavaScript因运行在浏览器单线程环境里,无法创建线程问题。
使用Web Worker:

1
var myWorker=new Worker("xxx.js");

在xxx.js里可以做一些耗时操作,通过messaage事件和postMessage方法进行通信。
xxxx.js里

1
2
3
4
5
var num=0;
for(var i=0;i<100;i++){
num+=i;
}
postMessage(num);

调用:

1
2
3
4
var myWorker=new Worker("xxx.js");
myWorker.addEventListener('message',function(e){
console.log(e.data);//4950
},false);

Worker技术虽然给客户端进行大量提供了便利,但对于移动设备而言,过分依赖Worker并不是一个好主意。


第八章 通信基础

XRH2:XML HttpRequest Level2,提供一种利用JavaScript与服务器端通信的方式。
Ajax基本步骤:

1
2
3
4
5
6
7
8
9
10
11
12
var xhr=new XmlHttpRequest();
xhr.open('GET','/img.jpg',true);
xhr.responseType='blob';//text,arraybuffer,json,document
xhr.onload = function(e) {
if (this.status == 200) {
var url=window.URL.createObjectURL(this.response);
var img=new Image();
img.src=url;
document.body.appendChild(img)
}
};
xhr.send();

第九章 实时Web技术

使用Socket.IO实现在线实时聊天室
index.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
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<link href='style.css' rel='stylesheet'/>
</head>
<body>
<h1>Socket.IO Chat Demo</h1>
<div class="wrap" class="chatroom">
<div class="nickname">
<form class="set-nickname">
<label for="nick">输入昵称后进入聊天室</label>
<input class="nick" name="nick" type="text" placeholder="昵称" />
<button type="submit">进入</button>
<p class="nickname-err">该昵称已经有人使用</p>
</form>
</div>
<div class="messages">
<div class="nicknames">
<span>当前在线: </span>
</div>
<div class="lines"></div>
</div>
<form class="send-message">
<span class="to"> 发送给<b>所有人</b></span>
<input class="message" type="text"/ placeholder="在这里输入消息发送" />
<button>发送</button>
</form>
</div>
<script src='http://code.jquery.com/jquery-latest.min.js'></script>
<script src='socket-io.js'></script>
<script src='index.js'></script>
</body>
</html>

style.css:

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
body {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
background: #eee;
}

h1 {
text-align: center;
font-size: 40px;
color: rgba(0, 0, 0, .8);
text-shadow: 0 1px 1px #fff;
}

.wrap {
max-width: 900px;
position: relative;
margin: auto;
border: 1px solid #ddd;
border-radius: 10px;
background: #f3f3f3;
box-shadow: 0 0 25px rgba(0, 0, 0, .07), inset 0 1px 11px rgb(255, 255, 255);
}
/* 按钮和表单样式 */
input[type="text"] {
border: 1px solid #ccc;
padding: 12px;
width: 250px;
font-size: 14px;
color: #777;
border-radius: 5px;
box-shadow: inset 0 1px 3px rgba(0, 0, 0, .2);
margin-bottom: 10px;
}
input[type="text"]:focus {
border-color: #999;
outline: 0;
}
button {
margin: 0;
display: inline-block;
text-decoration: none;
background: #00b5d6;
border: 1px solid #00a5c3;
border-radius: 7px;
color: #fff;
box-shadow: inset 0 1px 1px rgba(255, 255, 255, .6),
inset 0 0 10px #008da7;
font: 600 1.3em/1.7em "helvetica neue", helvetica, arial, sans-serif;
text-align: center;
text-shadow: 0 1px 1px #006679;
cursor: pointer;
}
button:hover,
button:active,
button:focus {
background: #009cb8;
box-shadow: inset 0 1px 1px rgba(255, 255, 255, .7),
inset 0 0 10px #007287;
}

/* 布局样式 */
.nickname {
text-align: center;
font: 15px;
color: rgba(0, 0, 0, .5);
display: block;
}

label {
display: block;
margin: 20px 0;
font-size: 18px;
}

.nickname .nickname-err {
color: #8b0000;
font-size: 12px;
visibility: hidden;
}
.nickname {
/*display: none;*/
}
.messages {
border-radius: 10px;
overflow: hidden;
display: none;
}
.send-message {
display: none;
}

.messages em {
text-shadow: 0 1px 0 #fff;
color: #999;
}
.messages p {
margin: 0;
color: rgba(0, 0, 0, .5);
font: 13px Helvetica, Arial;
padding: 5px 10px;
}
.messages p b {
display: inline-block;
padding-right: 10px;
color: rgba(0, 0, 0, .8);
}
.messages p:nth-child(even) {
background: #fafafa;
}
.messages .nicknames {
padding: 10px;
font-size: 13px;
}
.messages .nicknames span {
color: #000;
font-weight: bold;
}
.messages .nicknames b {
display: inline-block;
color: #fff;
background: #4FA72C;
padding: 3px 6px;
margin-right: 5px;
border-radius: 5px;
text-shadow: 0 1px 0 rgba(0, 0, 0, .2);
cursor: pointer;
}
.messages .lines {
height: 250px;
border-top: 1px solid #ddd;
background: #fff;
overflow: auto;
overflow-x: hidden;
overflow-y: auto;
}
.send-message {
padding: 10px;
position: relative;
}
.send-message input:focus {
outline: 0;
}

.send-message button {
width: 110px;
}

/* 由于我们的应用比较简单因此将只针对三种宽度的设备进行优化 */

/* 较宽屏幕 */
@media (min-width: 960px) {
.nicknames {
float: right;
width: 160px;
}
.nicknames b,
.nicknames span {
display: block !important;
margin-bottom: 4px;
text-align: center;
}
}

/* 横屏的手机或者平板 */
@media (max-width: 959px) {
h1 {
font-size: 30px;
}
}

/* 竖屏的手机 */
@media (max-width: 320px) {
.to {
display: none;
}
.send-message button {
width: auto;
}
.message {
width: 190px !important;
}
h1 {
font-size: 20px;
}
}

重点及核心代码
前端代码:
index.js

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
// 在 DOMReady 后再开始执行实际代码
$(function() {
var $chatroom = $('.chat'),
$lines = $('.lines'),
$nickname = $('.nickname'),
$setNickname = $('.set-nickname'),
$nicknames = $('.nicknames'),
$messages = $('.messages'),
$message = $('.message'),
$nick = $('.nick'),
$sendMessage = $('.send-message'),
$to = $('.to'),
$nicknameErr = $('.nickname-err'), toUser = null, myself = null
// 如果不传递url参数,Socket.IO会自动探测地址。
// 通常是生成的类似 /socket.io/1/?t=1371223173600 这样的地址
var socket = io.connect()
socket.on('announcement', function(msg) {
$lines.append($('<p>').append($('<em>').text(msg)))
})
socket.on('nicknames', function(nicknames) {
$nicknames.empty().append($('<span>当前在线: </span>'))
$.each(nicknames, function (key, val) {
$nicknames.append($('<b>').text(val))
})
})
function message(from, msg, opt_to) {
var label
if (opt_to) {
label = $('<b>').text(from + '对' + opt_to + '说:')
} else {
label = $('<b>').text(from + ':')
}
$lines.append($('<p>').append(label, msg))
}
socket.on('user:pub', message)
socket.on('user.private', message)
socket.on('reconnect', function() {
$lines.remove()
message('<i>系统消息</i>', '重连了!')
})
socket.on('reconnecting', function() {
message('<i>系统消息</i>', '尝试重连中…')
})
socket.on('error', function(e) {
message('<i>系统消息</i>', e ? e : '未知错误!')
})
function clear() {
$message.val('').focus()
}
$setNickname.submit(function(e) {
socket.emit('nickname', $nick.val(), function(set) {
if (!set) {
clear()
myself = $nick.val()
$nickname.hide()
$messages.show()
$sendMessage.show()
return
}
$nicknameErr.css('visibility', 'visible')
})
return false
})
$sendMessage.submit(function() {
if (toUser) {
message('我对' + toUser + '说', $message.val())
socket.emit('user:private', $message.val(), toUser)
} else {
message('我', $message.val())
socket.emit('user:pub', $message.val())
}
clear()
$lines.scrollTop(10000000)
return false
})
$nicknames.on('click', 'b', function (e) {
toUser = $(e.target).text()
if (toUser === myself) {
$to.find('b').text('所有人')
toUser = null
return
}
$to.find('b').text(toUser)
})
})

后端js代码:
server.js

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
var http = require('http')
var fs = require('fs')
// 使用connect中间层来处理静态文件请求
// 详情参考:https://github.com/senchalabs/connect
var connect = require('connect')
var app = connect.createServer(
// 挂载当前文件所在目录
connect.static(__dirname)
).listen(8080)
var sio = require('socket.io')
var io = sio.listen(app),
// 我们的程序没有引入后端存储层,因此在程序运行期间直接将所有用户保存在内存里面
nicknames = {}, onlines = {}
io.sockets.on('connection', function(socket) {
// 在设计事件时,用冒号分割以分组事件类型是一种易读的好做法
socket.on('user:pub', function(msg) {
socket.broadcast.emit('user:pub', socket.nickname, msg)
})
socket.on('user:private', function (msg, to) {
if(onlines[to]) {
onlines[to].emit('user.private', socket.nickname, msg, to)
}
})
socket.on('nickname', function(nick, fn) {
// fn用于确认是否登录聊天室成功了,true表示有相同昵称的用户已经进入
if (nicknames[nick]) {
fn(true)
} else {
fn(false)
nicknames[nick] = socket.nickname = nick
onlines[nick] = socket
socket.broadcast.emit('announcement', nick + ' 已连接')
io.sockets.emit('nicknames', nicknames)
}
})
socket.on('disconnect', function() {
if (!socket.nickname) {
return
}
delete nicknames[socket.nickname];
delete onlines[socket.nickname]
// 广播“我”已经离开聊天室了,并更新在线列表
socket.broadcast.emit('announcement', socket.nickname + ' 断开连接了')
socket.broadcast.emit('nicknames', nicknames)
})
})

第十章 感官世界

感知方向和动作:
deviceorientation事件:在设备有明显的方向变化时触发
alpha:右手坐标系y变的夹角度数
beta:右手坐标系x变的夹角度数
gamma:右手坐标系z变的夹角度数

orientationchange事件:检测设备横竖屏幕

devicemotion事件:在设备进行自由落体时触发,很少用

compassneedscalibration事件:在需要校准设备时触发,很少用

随着手机的旋转、倾斜,浏览器里面的图片也跟着旋转、倾斜:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<style>
div {
/* 不要忘了给容器加上透视深度 */
-webkit-perspective: 250px;
}
</style>
<div style="text-align:center;padding-top:50px;">
<img src="../iphone.png" id="iphone" alt="" width="200">
</div>

<script>
var iphone = document.getElementById('iphone')
window.addEventListener('deviceorientation', function(e) {
// 如果你左右倾斜手机,屏幕上的手机也会左右摇(e.gamma),如果前后倾斜,屏幕上的手机则会相对屏幕前后转动(-e.beta)
iphone.style.webkitTransform = "rotate(" + e.gamma + "deg) rotate3d(1,0,0, " + (e.beta * -1) + "deg)"
}, true)
</script>

判断是横屏还是竖屏:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<script>
window.addEventListener('orientationchange', function() {
var displayStr = "Orientation : "
switch (window.orientation) {
case 0:
// 竖屏
displayStr += "Portrait"
break
case -90:
// 向右横屏
displayStr += "Landscape (right, screen turned clockwise)"
break
case 90:
// 向左横屏
displayStr += "Landscape (left, screen turned counterclockwise)"
break
case 180:
// 竖屏(倒着)
displayStr += "Portrait (upside-down portrait)"
break
}
console.log(displayStr)
}, false)
</script>

音视频捕获:

1
navigator.getUserMedia(type,successCallback,errorCallback)//获取音频和视频

获取媒体流实现截屏:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<video></video>
<img>
<canvas></canvas>
<script>
var video = document.querySelector('video')
var canvas = document.querySelector('canvas')
var ctx = canvas.getContext('2d')
var localMediaStream = null
function snapshot() {
if (localMediaStream) {
// drawImage方法可以直接绘制video的当前帧
ctx.drawImage(video, 0, 0)
// 将canvas当前绘制的内容转换成DataURL
document.querySelector('img').src = canvas.toDataURL('image/webp')
}
}
// 点击拍照
video.addEventListener('click', snapshot, false)
navigator.webkitGetUserMedia({video:true,audio:true}, function(stream) {
video.src = window.URL.createObjectURL(stream)
localMediaStream = stream
},function(){})
</script>

第十一章 history与导航

使用history.js实现页面无刷新更改URL的需求:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<script src="plugins/native.history.js"></script>
<script>
(function(window,undefined){
// History对象是history.js提供的唯一对象,它拥有和window.history 几乎一样的API
History.Adapter.bind(window,'statechange',function(){
var state = History.getState()
console.log(state)
})
History.pushState({state:1}, 'title 1', '?state=1') // log: {state:1}, 'title 1', '?state=1'
History.pushState({state:2}, 'title 2', '?state=2') // log: {state:2}, 'title 2', '?state=2'
History.replaceState({state:3}, 'title 3', '?state=3') // log: {state:3}, 'title 3', '?state=3'
History.pushState(null, null, '?state=4') // log: {}, '', '?state=4'
History.back() // log: {state:3}, 'title 3', '?state=3'
History.back() // log: {state:1}, 'title 1', '?state=1'
History.back() // log: {}, 'Home Page', '?'
History.go(2) // log: {state:3}, 'title 3', '?state=3'
})(window)
</script>

到目前为止,关于HTML5相关的基础已经介绍的差不多了,对自己而言,那些东西可能只是基础中的基础,了解下原理和语法就行了。
HTML5现在感觉还在处于发展期,很多规范和接口还没有完全统一,真正想在HTML5有所作为,肯定离不开框架的使用,而且如果你只想用HTML5一手遮天的做项目那是绝对不可能的,根据需求而定是很重要的选择哦。