HTML5新特性浅谈
发表于2016/10/17 21:25:58 7809人阅读javascript
分类: 前端php
转载请注明出处:css
http://blog.csdn.net/gane_cheng/article/details/52819118html
http://www.ganecheng.tech/blog/52819118.html (浏览效果更好)前端
2014年10月29日,W3C宣布,通过接近8年的艰苦努力,HTML5标准规范终于制定完成。html5
HTML5将会取代1999年制定的HTML 4.0一、XHTML 1.0标准,以期能在互联网应用迅速发展的时候,使网络标准达到符合当代的网络需求,为桌面和移动平台带来无缝衔接的丰富内容。java
做为2010年入坑IT的程序员来讲,能够说一步一步见证着HTML5的发展。这些年为了兼容IE6放弃了不少HTML5的新特性。可是今时不一样以往,移动设备的流行,自然支持HTML5,以及桌面端IE最终被用户和微软唾弃,更多支持HTML5浏览器的受欢迎,我要从新研究一下HTML5带来的这些新特性。node
HTML5 的新特性
① 语义特性(Semantic)linux
HTML5赋予网页更好的意义和结构。css3
② 本地存储特性(OFFLINE & STORAGE)
基于HTML5开发的网页APP拥有更短的启动时间,更快的联网速度,这些全得益于HTML5 APP Cache,以及本地存储功能。
③ 设备访问特性 (DEVICE ACCESS)
从Geolocation功能的API文档公开以来,HTML5为网页应用开发者们提供了更多功能上的优化选择,带来了更多体验功能的优点。HTML5提供了史无前例的数据与应用接入开放接口。使外部应用能够直接与浏览器内部的数据直接相连,例如视频影音可直接与microphones及摄像头相联。
④ 链接特性(CONNECTIVITY)
更有效的链接工做效率,使得基于页面的实时聊天,更快速的网页游戏体验,更优化的在线交流获得了实现。HTML5拥有更有效的服务器推送技术,Server-Sent Event和WebSockets就是其中的两个特性,这两个特性可以帮助咱们实现服务器将数据“推送”到客户端的功能。
⑤ 网页多媒体特性(MULTIMEDIA)
支持网页端的Audio、Video等多媒体功能, 与网站自带的APPS,摄像头,影音功能相得益彰。
⑥ 三维、图形及特效特性(3D, Graphics & Effects)
基于SVG、Canvas、WebGL及CSS3的3D功能,用户会惊叹于在浏览器中,所呈现的惊人视觉效果。
⑦ 性能与集成特性(Performance & Integration)
没有用户会永远等待你的Loading——HTML5会经过XMLHttpRequest2等技术,解决之前的跨域等问题,帮助您的Web应用和网站在多样化的环境中更快速的工做。
下面分别对这七个新特性进行研究。
① 语义特性(Semantic)
HTML5增长了新的内容标签,这些标签带有必定的语义,使搜索引擎爬取你的网站信息更高效。
HTML4中的内容标签级别相同,没法区分各部份内容。而HTML5中的内容标签互相独立,级别不一样,搜索引擎以及统计软件等都可快速识别各部份内容。
这些标签在新闻类网站,博客类网站颇有用。
最大的问题就是当使用这些新的语义元素时,那些不支持的浏览器如何处理这些元素。
见过的最多的解决方法是这样的。
<section class="section"> <!-- content --> </section>
- 1
- 2
- 3
- 4
- 5
.section { color: blue; }
- 1
- 2
- 3
- 4
若是还想了解更多语义标签兼容性问题,能够参考这篇文章http://html5.group.iteye.com/group/wiki/3293-html5
② 本地存储特性(OFFLINE & STORAGE)
HTML5提供了网页存储的API,方便Web应用的离线使用。除此以外,新的API相对于cookie也有着高安全性,高效率,更大空间等优势。
先看W3C对离线存储的介绍。
Web Apps can start faster and work even if there is no internet connection, thanks to the HTML5 App Cache, as well as the Local Storage, Indexed DB, and the File API specifications.
HTML5离线存储包含 应用程序缓存
,本地存储
,索引数据库
,文件接口
。
下面依次展开介绍。
(1)应用程序缓存(Application Cache)
使用 HTML5,经过建立 cache manifest 文件,能够轻松地建立 web 应用的离线版本。
HTML5引入了应用程序缓存,这意味着 web 应用可进行缓存,并可在没有因特网链接时进行访问。
应用程序缓存为应用带来三个优点:
- 离线浏览 – 用户可在应用离线时使用它们
- 速度 – 已缓存资源加载得更快
- 减小服务器负载 – 浏览器将只从服务器下载更新过或更改过的资源。
甭废话,先来感觉一下Application Cache的魅力。Shut up,show me the demo!
Demo连接:http://www.ganecheng.tech/welcome_offline.html
1.打开这个网页,第一次等待加载完成以后,页面和普通的网页没有区别。
2.点击刷新按钮,或者强制刷新按钮,看一下第二次打开的速度。有没有快到爆。(速度)
3.如今我要求你拔掉网线,断开WiFi,再次点击刷新按钮,或者强制刷新按钮,看一下第三次打开的速度。有没有快到爆。注意,如今并无联网,和服务器失去链接,依然秒开网页,还能正常操做网页。(离线浏览,减小服务器负载)
看完了效果,看一下App Cache的原理。
当咱们第一次正确配置cache manifest后,浏览器会将清单文件中的资源缓存下来。当咱们再次访问该应用时,浏览器会直接返回缓存中的资源,而后检查manifest文件是否有变更,若是有变更就会把相应的变更更新下来,同时改变浏览器里面的app cache。
使用方法
页面声明使用App Cache
<!DOCTYPE HTML> <html manifest="index.manifest">
- 1
- 2
清单文件中写明资源。
CACHE MANIFEST
theme.css logo.gif main.js NETWORK: login.asp FALLBACK: /html5/ /404.html
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
manifest 文件可分为三个部分:
CACHE MANIFEST - 在此标题下列出的文件将在首次下载后进行缓存
NETWORK - 在此标题下列出的文件须要与服务器的链接,且不会被缓存
FALLBACK - 在此标题下列出的文件规定当页面没法访问时的回退页面(好比 404 页面)
CACHE MANIFEST,是必需的
NETWORK 规定文件 “login.asp” 永远不会被缓存,且离线时是不可用的
FALLBACK 规定若是没法创建因特网链接,则用 “404.html” 替代 /html5/ 目录中的全部文件
一旦应用被缓存,它就会保持缓存直到发生下列状况:
- 用户清空浏览器缓存 ,用户怎么作,页面左右不了,说了等于没说。
- manifest 文件被修改,请注意:更新清单中列出的某个文件并不意味着浏览器会从新缓存该资源。清单文件自己必须进行更改,通常是加一个注释,注释的内容是日期,想更新了,改一下日期。
- 由程序来更新应用缓存,这个稍微靠谱一点。
须要注意的是,更新后的资源须要下次打开页面才能生效,本次打开的页面在更新资源以前就已经从缓存中拿到资源并加载完毕了。
由程序来更新,须要依赖manifest文件被修改这一条,由于调用的是浏览器提供的接口,检测 window.applicationCache.status 的值,若是是 UPDATEREADY,说明浏览器比较manifest文件完毕,能够更新缓存了。window.applicationCache.swapCache()。更新完了,不会当即生效,window.location.reload();从新加载一下页面。
缓存有这么多状态。
var appCache = window.applicationCache; switch (appCache.status) { case appCache.UNCACHED: // UNCACHED == 0 return 'UNCACHED'; break; case appCache.IDLE: // IDLE == 1 return 'IDLE'; break; case appCache.CHECKING: // CHECKING == 2 return 'CHECKING'; break; case appCache.DOWNLOADING: // DOWNLOADING == 3 return 'DOWNLOADING'; break; case appCache.UPDATEREADY: // UPDATEREADY == 4 return 'UPDATEREADY'; break; case appCache.OBSOLETE: // OBSOLETE == 5 return 'OBSOLETE'; break; default: return 'UKNOWN CACHE STATUS'; break; };
- 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
程序更新缓存的方法。
// Check if a new cache is available on page load. window.addEventListener('load', function(e) { window.applicationCache.addEventListener('updateready', function(e) { if (window.applicationCache.status == window.applicationCache.UPDATEREADY) { // Browser downloaded a new app cache. // Swap it in and reload the page to get the new hotness. window.applicationCache.swapCache(); if (confirm('A new version of this site is available. Load it?')) { window.location.reload(); } } else { // Manifest didn't changed. Nothing new to server. } }, false); }, false);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
更多更新缓存的方法,能够参考这篇文章:http://www.jb51.net/html5/67850.html
总的来讲,App Cache的三个优势很是明显。可是有几个坑,却会致使没人愿意使用这个新特性。
1.使用了App Cache的页面在清单文件更新以后去更新页面资源,可是只在下次打开页面才能生效,这意味着,咱们须要使用代码判断是否是最新版本,不是的话,刷新一次页面。这种体验很很差。
2.使用了App Cache的页面也会被缓存,这对于须要动态更新的页面来讲,几乎是个噩梦。用户访问到的页面不是最新的,会致使很是多的问题。
3.App cache与browser cache混合在一块儿会使更新机制变得更加复杂,主要有如下几个因素:
1) App cache在各浏览器平台实现上存在差别;
2) 各浏览器又提供了不一样的页面刷新机制;
3) app cache还与传统的browser cache有着千丝万缕的联系;对于它俩如何协同工做,HTML5的相关规范没有对app cache的细节给出很是明确的规定; 浏览器官方文档有没有给出很是明确的说明。
4) browser cache的更新机制自己就已经很复杂。
5) 若是manifest文件自己就有缓存时间,或设置为永远均可用,那你的网页永远都不会被更新了。
App Cache更多的坑,能够参考这篇文章:http://dreclo.blog.163.com/blog/static/528789512014111675023409/ , 而后再决定要不要使用这一新特性。
从惊叹于App Cache的强大,到填坑,W3C花了这么长时间,就弄出来这么个东西,真是使人失望。学会使用App Cache只用了不到一小时,填坑填了一下午。每一个页面都要有manifest,每一个页面都要加代码去判断去更新缓存,我能够骂人吗。
(2)本地存储(Local Storage)
本地存储发展历史.
最先的Cookies天然是你们都知道,问题主要就是过小,大概也就4KB的样子,并且IE6只支持每一个域名20个cookies,太少了。优点就是你们都支持,并且支持得还蛮好。很早之前那些禁用cookies的用户也都慢慢的不存在了,就好像之前禁用javascript的用户不存在了同样。
userData是IE的东西,垃圾。如今用的最多的是Flash吧,空间是Cookie的25倍,基本够用。再以后Google推出了Gears,虽然没有限制,但不爽的地方就是要装额外的插件(没具体研究过)。到了HTML5把这些都统一了,官方建议是每一个网站5MB,很是大了,就存些字符串,足够了。比较诡异的是竟然全部支持的浏览器目前都采用的5MB,尽管有一些浏览器可让用户设置,但对于网页制做者来讲,目前的形势就5MB来考虑是比较稳当的。
首先天然是检测浏览器是否支持本地存储。在HTML5中,本地存储是一个window的属性,包括localStorage和sessionStorage,从名字应该能够很清楚的辨认两者的区别,前者是一直存在本地的,后者只是伴随着session,窗口一旦关闭就没了。两者用法彻底相同,这里以localStorage为例。
if(window.localStorage) { alert('This browser supports localStorage'); } else { alert('This browser does NOT support localStorage'); }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
存储数据的方法就是直接给window.localStorage添加一个属性,例如:window.localStorage.a 或者 window.localStorage[“a”]。它的读取、写、删除操做方法很简单,是以键值对的方式存在的,以下:
localStorage.a = 3;//设置a为"3" localStorage["a"] = "sfsf";//设置a为"sfsf",覆盖上面的值 localStorage.setItem("b","isaac");//设置b为"isaac" var a1 = localStorage["a"];//获取a的值 var a2 = localStorage.a;//获取a的值 var b = localStorage.getItem("b");//获取b的值 localStorage.removeItem("c");//清除c的值
- 1
- 2
- 3
- 4
- 5
- 6
- 7
这里最推荐使用的天然是getItem()和setItem(),清除键值对使用removeItem()。若是但愿一次性清除全部的键值对,能够使用clear()。另外,HTML5还提供了一个key()方法,能够在不知道有哪些键值的时候使用,以下:
var storage = window.localStorage; function showStorage() { for(var i=0;i<storage.length;i++) { //key(i)得到相应的键,再用getItem()方法得到对应的值 document.write(storage.key(i)+ " : " + storage.getItem(storage.key(i)) + "<br>"); } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
写一个最简单的,利用本地存储的计数器:
var storage = window.localStorage; if (!storage.getItem("pageLoadCount")) { storage.setItem("pageLoadCount",0); } storage.pageLoadCount = parseInt(storage.getItem("pageLoadCount")) + 1;//必须格式转换 document.getElementById("count").innerHTML = storage.pageLoadCount; showStorage();
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
不断刷新就能看到数字在一点点上涨,以下图所示:
须要注意的是,HTML5本地存储只能存字符串,任何格式存储的时候都会被自动转为字符串,因此读取的时候,须要本身进行类型的转换。这也就是上一段代码中parseInt必需要使用的缘由。
想要对localStorage了解更多请参考这篇文章:http://www.cnblogs.com/xiaowei0705/archive/2011/04/19/2021372.html
(3)索引数据库(Indexed DB)
从本质上说,IndexedDB容许用户在浏览器中保存大量的数据。任何须要发送大量数据的应用均可以得益于这个特性,能够把数据存储在用户的浏览器端。当前这只是IndexedDB的其中一项功能,IndexedDB也提供了强大的基于索引的搜索api功能以得到用户所须要的数据。
用户可能会问:IndexedDB是和其余之前的存储机制(如cookie,session)有什么不一样?
Cookies是最经常使用的浏览器端保存数据的机制,但其保存数据的大小有限制而且有隐私问题。Cookies而且会在每一个请求中来回发送数据,彻底没办法发挥客户端数据存储的优点。
再来看下Local Storage本地存储机制的特色。Local Storage在HTML 5中有不错的支持,但就总的存储量而言依然是有所限制的。Local Storage并不提供真正的“检索API”,本地存储的数据只是经过键值对去访问。Local Storage对于一些特定的须要存储数据的场景是很适合的,例如,用户的喜爱习惯,而IndexedDB则更适合存储如广告等数据(它更象一个真正的数据库)。
通常来讲,有两种不一样类型的数据库:关系型和文档型(也称为NoSQL或对象)。关系数据库如SQL Server,MySQL,Oracle的数据存储在表中。文档数据库如MongoDB,CouchDB,Redis将数据集做为个体对象存储。IndexedDB是一个文档数据库,它在彻底内置于浏览器中的一个沙盒环境中(强制依照(浏览器)同源策略)。
对数据库的每次操做,描述为经过一个请求打开数据库,访问一个object store,再继续。
打开数据库的请求生命周期
IndexedDB是否适合个人应用程序?
如今最关键的问题:“IndexedDB是否适合个人应用程序?“像往常同样,答案是确定的:“视状况而定。“首先当你试图在客户端保存数据时,你会考虑HTML5本地存储。本地存储获得普遍浏览器的支持,有很是易于使用的API。简单有其优点,但其劣势是没法支持复杂的搜索策略,存储大量的数据,并提供事务支持。
IndexedDB是一个数据库。因此,当你想为客户端作出决定,考虑你如何在服务端选择一个持久化介质的数据库。你可能会问本身一些问题来帮助决定客户端数据库是否适合您的应用程序,包括:
- 你的用户经过浏览器访问您的应用程序,(浏览器)支持IndexedDB API吗 ?
- 你须要存储大量的数据在客户端?
- 你须要在一个大型的数据集合中快速定位单个数据点?
-
你的架构在客户端须要事务支持吗?
若是你对其中的任何问题回答了“是的”,颇有可能,IndexedDB是你的应用程序的一个很好的候选。
IndexedDB用法
如今,你已经有机会熟悉了一些的总体概念,下一步是开始实现基于IndexedDB的应用程序。第一个步骤须要统一IndexedDB在不一样浏览器的实现。您能够很容易地添加各类厂商特性的选项的检查,同时在window对象上把它们设置为官方对象相同的名称。下面的清单展现了window.indexedDB,window.IDBTransaction,window.IDBKeyRange的最终结果是如何都被更新,它们被设置为相应的浏览器的特定实现。
window.indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB; window.IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction || window.msIDBTransaction; window.IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange || window.msIDBKeyRange;
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
如今,每一个数据库相关的全局对象持有正确的版本,应用程序能够准备使用IndexedDB开始工做。
打开数据库
// 打开咱们的数据库,数据库名称,版本号 var request = indexedDB.open("MyTestDatabase", 3);
- 1
- 2
indexedDB的三个事件
//数据库打开成功执行 request.onsuccess = function (event) { // Better use "this" than "req" to get the result to avoid problems with // garbage collection. // db = request.result; db = this.result; console.debug("initDb DONE"); }; //数据库打开失败执行 request.onerror = function (event) { console.error("initDb:", evt.target.errorCode); }; //在数据库第一次被打开时或者当指定的版本号高于当前被持久化的数据库的版本号时,触发此事件,能够在这个地方建立对象存储空间结构,更新结构,加索引等. request.onupgradeneeded = function (event) { console.debug("initDb.onupgradeneeded"); var store = event.currentTarget.result.createObjectStore( DB_STORE_NAME, { keyPath: 'id', autoIncrement: true }); };
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
添加数据或更新数据
// 咱们的客户数据看起来像这样。 const customerData = [ { ssn: "444-44-4444", name: "Bill", age: 35, email: "bill@company.com" }, { ssn: "555-55-5555", name: "Donna", age: 32, email: "donna@home.org" } ]; var transaction = db.transaction(["customers"], "readwrite"); // 当全部的数据都被增长到数据库时执行一些操做 transaction.oncomplete = function(event) { alert("All done!"); }; transaction.onerror = function(event) { // 不要忘记进行错误处理! }; var objectStore = transaction.objectStore("customers"); for (var i in customerData) { var request = objectStore.add(customerData[i]); request.onsuccess = function(event) { // event.target.result == customerData[i].ssn }; }
- 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
删除数据
var request = db.transaction(["customers"], "readwrite") .objectStore("customers") .delete("444-44-4444"); request.onsuccess = function(event) { // 删除数据成功! };
- 1
- 2
- 3
- 4
- 5
- 6
获取数据
var transaction = db.transaction(["customers"]); var objectStore = transaction.objectStore("customers"); var request = objectStore.get("444-44-4444"); request.onerror = function(event) { // 错误处理! }; request.onsuccess = function(event) { // 对 request.result 作些操做! alert("Name for SSN 444-44-4444 is " + request.result.name); };
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
对于一个“简单”的提取这里的代码有点多了。下面看咱们怎么把它再缩短一点,假设你在数据库的级别上来进行的错误处理:
db.transaction("customers").objectStore("customers").get("444-44-4444").onsuccess = function(event) { alert("Name for SSN 444-44-4444 is " + event.target.result.name); };
- 1
- 2
- 3
- 4
使用游标获取数据
使用 get() 要求你知道你想要检索哪个键。若是你想要遍历对象存储空间中的全部值,那么你能够使用游标。看起来会像下面这样:
var customers = []; var objectStore = db.transaction("customers").objectStore("customers"); objectStore.openCursor().onsuccess = function(event) { var cursor = event.target.result; if (cursor) { customers.push(cursor.value); cursor.continue(); } else { alert("Got all customers: " + customers); } };
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
这里有一个封装好的完整的例子,简化了这些操做。
<script type="text/javascript"> window.indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB; window.IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction || window.msIDBTransaction; window.IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange || window.msIDBKeyRange; (function(window){ 'use strict'; var db = { version: 1, // important: only use whole numbers! objectStoreName: 'tasks', instance: {}, upgrade: function (e) { var _db = e.target.result, names = _db.objectStoreNames, name = db.objectStoreName; if (!names.contains(name)) { _db.createObjectStore( name, { keyPath: 'id', autoIncrement: true }); } }, errorHandler: function (error) { window.alert('error: ' + error.target.code); debugger; }, open: function (callback) { var request = window.indexedDB.open( db.objectStoreName, db.version); request.onerror = db.errorHandler; request.onupgradeneeded = db.upgrade; request.onsuccess = function (e) { db.instance = request.result; db.instance.onerror = db.errorHandler; callback(); }; }, getObjectStore: function (mode) { var txn, store; mode = mode || 'readonly'; txn = db.instance.transaction( [db.objectStoreName], mode); store = txn.objectStore( db.objectStoreName); return store; }, save: function (data, callback) { db.open(function () { var store, request, mode = 'readwrite'; store = db.getObjectStore(mode), request = data.id ? store.put(data) : store.add(data); request.onsuccess = callback; }); }, getAll: function (callback) { db.open(function () { var store = db.getObjectStore(), cursor = store.openCursor(), data = []; cursor.onsuccess = function (e) { var result = e.target.result; if (result && result !== null) { data.push(result.value); result.continue(); } else { callback(data); } }; }); }, get: function (id, callback) { id = parseInt(id); db.open(function () { var store = db.getObjectStore(), request = store.get(id); request.onsuccess = function (e){ callback(e.target.result); }; }); }, 'delete': function (id, callback) { id = parseInt(id); db.open(function () { var mode = 'readwrite', store, request; store = db.getObjectStore(mode); request = store.delete(id); request.onsuccess = callback; }); }, deleteAll: function (callback) { db.open(function () { var mode, store, request; mode = 'readwrite'; store = db.getObjectStore(mode); request = store.clear(); request.onsuccess = callback; }); } }; window.app = window.app || {}; window.app.db = db; }(window)); //将数据显示在页面上的方法 var bindData = function (data) { $("#IndexedDB").html(''); if(data.length === 0){ $("#IndexedDB").html("没有数据"); return; } data.forEach(function (note) { $("#IndexedDB").append(note.id+","+note.title+","+note.text); }); }; //一个新数据 var note = { id:1, title: "数据1", text: "数据1的另外一个字段" }; //又一个新数据 var note2 = { id:2, title: "数据2", text: "数据2的另外一个字段" }; //插入或更新数据(insert or update) window.app.db.save(note,function() { //window.app.db.getAll(bindData); }); window.app.db.save(note2); //获取数据 window.app.db.get(1,function(item) { //alert("window.app.db.get:"+item.id+","+item.title+","+item.text); }); //删除数据 window.app.db.delete(1,function() { //alert("window.app.db.get:"+item.id+","+item.title+","+item.text); }); //删除全部 window.app.db.deleteAll(function() { //alert("window.app.db.get:"+item.id+","+item.title+","+item.text); }); </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
- 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
想对IndexedDB更多用法进行了解的能够参考这两篇文章。
https://developer.mozilla.org/zh-CN/docs/Web/API/IndexedDB_API/Using_IndexedDB
除了IndexedDB之外,还有一种已经被W3C放弃的Web SQL。
浏览器支持本地数据库并非从IndexedDB才开始实现,它是在Web SQL实现以后的一种新方法。相似IndexedDB,Web SQL是一个客户端数据库,但它做为一个关系数据库的实现,使用结构化查询语言(SQL)与数据库通讯。Web SQL的历史充满了曲折,但底线是没有主流的浏览器厂商对Web SQL继续支持。
若是Web SQL其实是一个废弃的技术,为何还要提它呢?有趣的是,Web SQL在浏览器里获得稳固的支持。Chrome, Safari, iOS Safari, and Android 浏览器都支持。另外,并非这些浏览器的最新版本才提供支持,许多这些最新最好的浏览器以前的版本也能够支持。有趣的是,若是你为Web SQL添加支持来支持IndexedDB,你忽然发现,许多浏览器厂商和版本成为支持浏览器内置数据库的某种化身。
所以,若是您的应用程序真正须要一个客户端数据库,你想要达到的最高级别的采用可能,当IndexedDB不可用时,也许您的应用程序可能看起来须要选择使用Web SQL来支持客户端数据架构。虽然文档数据库和关系数据库管理数据有鲜明的差异,但只要你有正确的抽象,就能够使用本地数据库构建一个应用程序。
Web SQL的用法相似于操做SQLite数据库,在这里不展开介绍了。
想对Web SQL了解更多,能够参考这篇文章:http://www.ibm.com/developerworks/cn/web/1210_jiangjj_html5db/
(4)文件接口(File API)
在以前咱们操做本地文件都是使用flash、silverlight或者第三方的activeX插件等技术,因为使用了这些技术后就很难进行跨平台、或者跨浏览器、跨设备等状况下实现统一的表现,从另一个角度来讲就是让咱们的web应用依赖了第三方的插件,而不是很独立,不够通用。在HTML5标准中,默认提供了操做文件的API让这一切直接标准化。有了操做文件的API,让咱们的Web应用能够很轻松的经过JS来控制文件的读取、写入、文件夹、文件等一系列的操做。
先看一个demo。以前咱们操做一个图片文件,都是先将图片上传到服务器端,而后再使用一个img标签指向到服务器的url地址,而后再进行一个使用第三方插件进行图片处理,而如今这一切都不须要服务器端了,由于FileReader对象提供的几个读取文件的方法变得异常简单,并且所有是客户端js的操做。
http://html5demos.com/file-api/
如今咱们本身来使用这些API。
先写好咱们的HTML页面。
<input type="file" multiple="multiple" name="fileDemo" id="fileDemo" /> <br /> <input type="button" value="获取文件的名字" id="btnGetFile" /> <input type="button" value="readAsDataURL" id="readAsDataURL" onclick="showDataByURL();" /> <input type="button" value="readAsBinaryString" id="readAsBinaryString" onclick="showDataByBinaryString();" /> <input type="button" value="readAsText" id="readAsText" onclick="showDataByText();" /> <div id="result"></div>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
获取文件名
$("#btnGetFile").click(function(e) { var fileList = document.getElementById("fileDemo").files; for (var i = 0; i < fileList.length; i++) { if (!(/image\/\w+/.test(fileList[i].type))) { $("#result").append("<span>type:" + fileList[i].type + "--******非图片类型*****--name:" + fileList[i].name + "--size:" + fileList[i].size + "</span><br />"); } else { $("#result").append("<span>type:" + fileList[i].type + "--name:" + fileList[i].name + "--size:" + fileList[i].size + "</span><br />"); } } });
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
readAsDataURL()
开始读取指定的Blob对象或File对象中的内容. 当读取操做完成时,readyState属性的值会成为DONE,若是设置了onloadend事件处理程序,则调用之.同时,result属性中将包含一个data: URL格式的字符串以表示所读取文件的内容.
这个方法颇有用,好比,能够实现图片的本地预览.
function showDataByURL() { var resultFile = document.getElementById("fileDemo").files[0]; if (resultFile) { var reader = new FileReader(); reader.readAsDataURL(resultFile); reader.onload = function(e) { var urlData = this.result; document.getElementById("result").innerHTML += "<img src='" + urlData + "' alt='" + resultFile.name + "' />"; }; } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
readAsBinaryString()
开始读取指定的Blob对象或File对象中的内容. 当读取操做完成时,readyState属性的值会成为DONE,若是设置了onloadend事件处理程序,则调用之.同时,result属性中将包含所读取文件的原始二进制数据.
function showDataByBinaryString() { var resultFile = document.getElementById("fileDemo").files[0]; if (resultFile) { var reader = new FileReader(); //异步方式,不会影响主线程 reader.readAsBinaryString(resultFile); reader.onload = function(e) { var urlData = this.result; document.getElementById("result").innerHTML += urlData; }; } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
readAsText()
开始读取指定的Blob对象或File对象中的内容. 当读取操做完成时,readyState属性的值会成为DONE,若是设置了onloadend事件处理程序,则调用之.同时,result属性中将包含一个字符串以表示所读取的文件内容.
function showDataByText() { var resultFile = document.getElementById("fileDemo").files[0]; if (resultFile) { var reader = new FileReader(); reader.readAsText(resultFile, 'gb2312'); reader.onload = function(e) { var urlData = this.result; document.getElementById("result").innerHTML += urlData; }; } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
事件处理程序
onabort
当读取操做被停止时调用.
onerror
当读取操做发生错误时调用.
onload
当读取操做成功完成时调用.
onloadend
当读取操做完成时调用,无论是成功仍是失败.该处理程序在onload或者onerror以后调用.
onloadstart
当读取操做将要开始以前调用.
onprogress
在读取数据过程当中周期性调用.
在文件上传时,HTML5还支持拖拽功能。
<div id="holder"></div> var holder = document.getElementById('holder'); holder.ondrop = function (e) { e.preventDefault(); var file = e.dataTransfer.files[0], reader = new FileReader(); reader.onload = function (event) { console.log(event.target); holder.style.background = 'url(' + event.target.result + ') no-repeat center'; }; console.log(file); reader.readAsDataURL(file); return false; };
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
想对HTML5文件接口操做文件了解更多请参考下面的文章:
https://developer.mozilla.org/zh-CN/docs/Using_files_from_web_applications
https://developer.mozilla.org/zh-CN/docs/Web/API/FileReader
http://blog.csdn.net/testcs_dn/article/details/8695532
http://www.cnblogs.com/fly_dragon/archive/2012/06/02/2532035.html
③ 设备访问特性 (DEVICE ACCESS)
来一段W3C对这个特性的介绍。
Beginning with the Geolocation API, Web Applications can present rich, device-aware features and experiences. Incredible device access innovations are being developed and implemented, from audio/video input access to microphones and cameras, to local data such as contacts & events, and even tilt orientation.
大体包含 地理位置API
,媒体访问API
,访问联系人及事件
,设备方向
。
下面分别进行研究。
(1)地理位置API(Geolocation API)
HTML5 Geolocation API 用于得到用户的地理位置。
鉴于该特性可能侵犯用户的隐私,除非用户赞成,不然用户位置信息是不可用的。通常网页在调用此信息时,会弹出权限申请窗口。
先上Demo:http://www.w3school.com.cn/tiy/t.asp?f=html5_geolocation_error
若是电脑获取不到位置信息的话,就在手机上试一下吧。
getCurrentPosition()
getCurrentPosition() 方法来得到用户的位置。
标准用法以下:
<script> var x=document.getElementById("demo"); function getLocation() { if (navigator.geolocation) { navigator.geolocation.getCurrentPosition(showPosition); } else { x.innerHTML="Geolocation is not supported by this browser."; } } function showPosition(position) { x.innerHTML="Latitude: " + position.coords.latitude + "<br />Longitude: " + position.coords.longitude; } </script>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
-检测是否支持地理定位
-若是支持,则运行 getCurrentPosition() 方法。若是不支持,则向用户显示一段消息。
-若是getCurrentPosition()运行成功,则向参数showPosition中规定的函数返回一个coordinates对象
-showPosition() 函数得到并显示经度和纬度
上面的例子是一个很是基础的地理定位脚本,不含错误处理。
完整的处理应该是这样的.
<script> var x = document.getElementById("demo"); function getLocation() { if (navigator.geolocation) { navigator.geolocation.getCurrentPosition(showPosition, showError); } else { x.innerHTML = "Geolocation is not supported by this browser."; } } function showPosition(position) { x.innerHTML = "Latitude: " + position.coords.latitude + "<br />Longitude: " + position.coords.longitude; } function showError(error) { switch (error.code) { case error.PERMISSION_DENIED: x.innerHTML = "User denied the request for Geolocation." break; case error.POSITION_UNAVAILABLE: x.innerHTML = "Location information is unavailable." break; case error.TIMEOUT: x.innerHTML = "The request to get user location timed out." break; case error.UNKNOWN_ERROR: x.innerHTML = "An unknown error occurred." break; } } </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
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
错误代码:
-Permission denied - 用户不容许地理定位
-Position unavailable - 没法获取当前位置
-Timeout - 操做超时
-Unknown error - 未知错误
若是想对地理位置API了解更多,能够参考这个网址。http://www.w3school.com.cn/html5/html_5_geolocation.asp
(2)媒体访问API(media API)
先上demo。麦克风和摄像头也涉及到用户隐私,因此浏览器会向用户申请访问权限。
https://www.ganecheng.tech/html5/media.html
getUserMedia()
以上的效果,都是经过getUserMedia()方法实现的。
<video id="video" autoplay="autoplay" style='width: 640px; height: 480px'></video> <button id='picture'>PICTURE</button> <canvas id="canvas" width="640" height="480"></canvas>
- 1
- 2
- 3
var video = document.getElementById("video"); var context = canvas.getContext("2d"); var errocb = function(code) { console.log(code); }; if (navigator.getUserMedia) { // 标准的API navigator.getUserMedia( { "video" : true }, function(stream) { video.src = stream; video.play(); }, errocb); } else if (navigator.webkitGetUserMedia) { // WebKit 核心的API console.log(navigator.webkitGetUserMedia); navigator.webkitGetUserMedia( { "video" : true }, function(stream) { video.src = window.webkitURL.createObjectURL(stream); video.play(); }, errocb); } // 将拍好的照片显示在画布上 document.getElementById("picture").addEventListener("click", function() { context.drawImage(video, 0, 0, 640, 480); });
- 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
视频获取就是这个样子,音频获取添加"audio" : true
便可。
想对媒体API了解更多,参考文章:http://blog.csdn.net/u010359143/article/details/50326981
http://blog.csdn.net/renfufei/article/details/21168239
http://www.jb51.net/html5/81028.html
(3)联系人管理API(Contacts Manager API)
这个纯属于W3C的美好设想,尚未正式归入标准。目前没有搜到靠谱的资料。
当初的提议能够参考这里。http://contacts-manager-api.sysapps.org/
可是有两个相似的变通实现。
FirefoxOS(已结束生命)示例代码。
var request = window.navigator.contacts.getAll(); var count = 0; request.onsuccess = function () { if(this.result) { count++; // Display the name of the contact console.log(this.result.givenName + ' ' + this.result.familyName); // Move to the next contact which will call the request.onsuccess with a new result this.continue(); } else { console.log(count + 'contacts found.'); } } request.onerror = function () {}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
想要对这个已无生命气息的web操做系统了解更多的参考这里 https://developer.mozilla.org/en-US/docs/Mozilla/B2G_OS/API/ContactManager
Cordova(Hybrid App)实现方式参考这里https://cordova.apache.org/docs/en/3.0.0/cordova/contacts/contacts.html
(4)设备方向和运动API(DeviceOrientation & DeviceMotion API)
DeviceOrientation经常使用于检测重力感应方向,DeviceMotion经常使用于摇一摇功能。
① DeviceOrientation
重力感应也是原生APP中常常见到的一个功能,在Web App中的应用多见于判断屏幕的旋转方向,以及在此基础上实现的场景应用,如控制页面上物体的左右移动,加减速等。
在Web App中实现以上的功能,须要实时获取屏幕的旋转方向参数,这些参数能够从浏览器的利用HTML5的DeviceOrientation API得到。
当浏览器的Orientation发生变化时,触发DeviceOrientation事件,并返回一个DeviceOrientationEvent对象,其属性列表以下:
属性 | 释义 |
---|---|
alpha | 设备指示的方向,根据指南针的设定状况而定 |
beta | 设备绕x轴旋转的角度 |
gamma | 设备绕y轴旋转的角度 |
注:不一样版本的手机操做系统和浏览器,以及不一样的应用程序中内置的浏览器对deviceorientation事件的支持不尽相同。尤为在Android平台上,可能会出现有的设备正常工做,有的则毫无反应的状况。
工做原理
根据event对象的三个方向的参数来肯定设备的旋转角度。其中,alpha的取值范围是0-360,这个须要根据设备的指南针设定状况而定,通常来讲,设备指向正北方向时为0.beta值为设备绕x轴旋转的角度,取值范围为-180-180。gamma取值范围-90-90.
这里面alpha值的意义并不大,主要参考beta和gamma值。
当屏幕从水平沿y轴向左倾斜时gamma值变为负值,向右倾斜变为正值。
档屏幕从水平沿x轴向前倾斜时beta值变为正值,向后倾斜时变为负值。
因此,若是咱们设定一个阈值,当beta和gamma的绝对值大于这个阈值时,咱们就认为设备发生了旋转。另外根据beta和gamma的值来判断向左倾斜仍是向右倾斜,以及倾斜的程度。
实现方式和示例
首先是为浏览器绑定deviceorientation事件和处理程序。
//add deviceorientation event listener if(window.DeviceOrientationEvent) { window.addEventListener('deviceorientation',DeviceOrientationHandler,false); }else { alert("您的浏览器不支持DeviceOrientation"); } function DeviceOrientationHandler(event) { var alpha = event.alpha, beta = event.beta, gamma = event.gamma; if(alpha != null || beta != null || gamma != null) { dataContainerOrientation.innerHTML = "alpha:" + alpha + "<br />beta:" + beta + "<br />gamma:" + gamma; //判断屏幕方向 var html = ""; if( Math.abs(gamma) < GAMMA_MIN && Math.abs(beta) > BETA_MAX ){ html = "屏幕方向:Portrait"; } if( Math.abs(beta) < BETA_MIN && Math.abs(gamma) > GAMMA_MAX ){ html = "屏幕方向:Landscape"; } var gamma_html = ""; if( gamma > 0 ){ gamma_html = "向右倾斜"; }else{ gamma_html = "向左倾斜"; } html += "<br />"+gamma_html stage.innerHTML = html; }else { dataContainerOrientation.innerHTML = "当前浏览器不支持DeviceOrientation"; } }
- 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
这个示例中展现了如何利用beta和gamma值来展现屏幕的旋转方向和侧翻方向。要实现更精确的物体判断,还须要复杂的算法来计算。
扩展应用
使用DeviceOrientation API接口能够实如今web中获取手机设备的屏幕旋转方向参数,在示例的基础上进行改进,能够扩展到在屏幕上控制页面元素的移动,实现动画或游戏的目的。例如经过调整屏幕的方向控制页面上的小球走迷宫,控制小车的移动躲避障碍等。
② DeviceMotion
摇一摇功能是不少原生APP均可以实现的功能,如微信中的摇一摇找好友,QQ音乐中的摇一摇换歌等。它们都是利用了手机加速传感器提供的API,当监听到手机加速变化的事件时,根据获取的加速值来执行不一样的动做。
在Web APP中HTML5 也提供了相似的接口,就是DeviceMotionEvent。DeviceMotion封装了运动传感器数据的事件,能够获取手机运动状态下的运动加速度等数据。
DeviceMotionEvent对象属性列表:
属性 | 释义 |
---|---|
event.accelaration | x(y,z):设备在x(y,z)方向上的移动加速度值 |
event.accelarationIncludingGravity | x(y,z):考虑了重力加速度后设备在x(y,z)方向上的移动加速度值 |
event.rotationRate | alpha,beta,gamma:设备绕x,y,z轴旋转的角度 |
event.accelarationIncludingGravity与event.accelaration的区别在于前者加入了重力加速度,即在z轴方向加了9.8,在x,y方向上的值二者相同。
旋转速度rotationRate:alpha、beta、gamma的概念与DeviceOrientationEvent一致。
区别在于:
DeviceOrientationEvent的值是相对于初始状态的差值,只要设备方向不变,怎么动都不会影响数值;
DeviceMotionEvent是相对于以前的某个瞬间值的差值时间比,即变化的速度,一旦设备静止则会恢复为0。
实现摇一摇
function Shake(threshold, callback) { //定义阈值 this.SHAKE_THRESHOLD = threshold ? threshold : 2000; this.last_update = 0; this.x = this.y = this.z = this.last_x = this.last_y = this.last_z = 0; this.init = function() { if (window.DeviceMotionEvent) { window.addEventListener('devicemotion', this.deviceMotionHandler, false); } else { alert('您的浏览器不支持DeviceMotion'); } }; var that = this; this.deviceMotionHandler = function(eventData) { var acceleration = eventData.accelerationIncludingGravity; var curTime = new Date().getTime(); if ((curTime - that.last_update) > 100) { var diffTime = curTime - that.last_update; that.last_update = curTime; that.x = acceleration.x; that.y = acceleration.y; that.z = acceleration.z; var speed = Math.abs(that.x + that.y + that.z - that.last_x - that.last_y - that.last_z) / diffTime * 10000; if (speed > that.SHAKE_THRESHOLD) { if (window.console && console.log) { console.log("shaked"); } if (callback != undefined) { callback(that); } } that.last_x = that.x; that.last_y = that.y; that.last_z = that.z; } } }; window.onload = function() { var shake1 = new Shake(2000, function(obj) { alert("shaked"); var r = document.getElementById("result"); r.innerHTML = "x:" + obj.x + ""; r.innerHTML += "y:" + obj.y + ""; r.innerHTML += "z:" + obj.z + ""; }); shake1.init(); };
- 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
设备方向和运动API涉及到一些数学知识。想要对这一API了解更多,能够参考下面两篇文章。
http://www.jianshu.com/p/5769075e9885
http://w3c.github.io/deviceorientation/spec-source-orientation.html
④ 链接特性(CONNECTIVITY)
先看W3C的定义。
More efficient connectivity means more real-time chats, faster games, and better communication. Web Sockets and Server-Sent Events are pushing (pun intended) data between client and server more efficiently than ever before.
HTTP是无链接的,一次请求,一次响应。想要实现微信网页版扫一扫登陆,网页版微信聊天的功能,须要使用轮询的方式达到长链接的效果,轮询的大部分时间是在作无用功,浪费网络,浪费资源。如今HTML5为咱们带来了更高效的链接方案 Web Sockets 和 Server-Sent Events 。
下面对这两种链接方式分别进行研究。
(1)网页Sockets(Web Sockets)
运行原理
浏览器端示例
var wsServer = 'ws://localhost:8888/Demo'; //服务器地址 var websocket = new WebSocket(wsServer); //建立WebSocket对象 websocket.send("hello");//向服务器发送消息 alert(websocket.readyState);//查看websocket当前状态 websocket.onopen = function (evt) { //已经创建链接 }; websocket.onclose = function (evt) { //已经关闭链接 }; websocket.onmessage = function (evt) { //收到服务器消息,使用evt.data提取 }; websocket.onerror = function (evt) { //产生异常 };
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
服务器端
握手协议的客户端数据已经由浏览器代劳了,服务器端须要咱们本身来实现,目前市场上开源的实现也比较多如:
Kaazing WebSocket Gateway(一个 Java 实现的 WebSocket Server); mod_pywebsocket(一个 Python 实现的 WebSocket Server); Netty(一个 Java 实现的网络框架其中包括了对 WebSocket 的支持); node.js(一个 Server 端的 JavaScript 框架提供了对 WebSocket 的支持); WebSocket4Net(一个.net的服务器端实现);
其实在目前的.net4.5框架中已经实现了WebSocket,不用官方实现,咱们本身来写个简单的。服务器端须要根据协议来握手、接收和发送。
握手
/// <summary> /// 生成Sec-WebSocket-Accept /// </summary> /// <param name="handShakeText">客户端握手信息</param> /// <returns>Sec-WebSocket-Accept</returns> private static string GetSecKeyAccetp(byte[] handShakeBytes,int bytesLength) { string handShakeText = Encoding.UTF8.GetString(handShakeBytes, 0, bytesLength); string key = string.Empty; Regex r = new Regex(@"Sec-WebSocket-Key:(.*?)rn"); Match m = r.Match(handShakeText); if (m.Groups.Count != 0) { key = Regex.Replace(m.Value, @"Sec-WebSocket-Key:(.*?)rn", "$1").Trim(); } byte[] encryptionString = SHA1.Create().ComputeHash(Encoding.ASCII.GetBytes(key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11")); return Convert.ToBase64String(encryptionString); }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
若是握手成功,将会触发客户端的onopen事件。
解析接收的客户端信息
/// <summary> /// 解析客户端数据包 /// </summary> /// <param name="recBytes">服务器接收的数据包</param> /// <param name="recByteLength">有效数据长度</param> /// <returns></returns> private static string AnalyticData(byte[] recBytes, int recByteLength) { if (recByteLength < 2) { return string.Empty; } bool fin = (recBytes[0] & 0x80) == 0x80; // 1bit,1表示最后一帧 if (!fin){ return string.Empty;// 超过一帧暂不处理 } bool mask_flag = (recBytes[1] & 0x80) == 0x80; // 是否包含掩码 if (!mask_flag){ return string.Empty;// 不包含掩码的暂不处理 } int payload_len = recBytes[1] & 0x7F; // 数据长度 byte[] masks = new byte[4]; byte[] payload_data; if (payload_len == 126) { Array.Copy(recBytes, 4, masks, 0, 4); payload_len = (UInt16)(recBytes[2] << 8 | recBytes[3]); payload_data = new byte[payload_len]; Array.Copy(recBytes, 8, payload_data, 0, payload_len); } else if (payload_len == 127) { Array.Copy(recBytes, 10, masks, 0, 4); byte[] uInt64Bytes = new byte[8]; for (int i = 0; i < 8; i++) { uInt64Bytes[i] = recBytes[9 - i]; } UInt64 len = BitConverter.ToUInt64(uInt64Bytes, 0); payload_data = new byte[len]; for (UInt64 i = 0; i < len; i++) { payload_data[i] = recBytes[i + 14]; } }else { Array.Copy(recBytes, 2, masks, 0, 4); payload_data = new byte[payload_len]; Array.Copy(recBytes, 6, payload_data, 0, payload_len); } for (var i = 0; i < payload_len; i++) { payload_data[i] = (byte)(payload_data[i] ^ masks[i % 4]); } return Encoding.UTF8.GetString(payload_data); }
- 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
发送数据至客户端
/// <summary> /// 打包服务器数据 /// </summary> /// <param name="message">数据</param> /// <returns>数据包</returns> private static byte[] PackData(string message) { byte[] contentBytes = null; byte[] temp = Encoding.UTF8.GetBytes(message); if (temp.Length < 126) { contentBytes = new byte[temp.Length + 2]; contentBytes[0] = 0x81; contentBytes[1] = (byte)temp.Length; Array.Copy(temp, 0, contentBytes, 2, temp.Length); }else if (temp.Length < 0xFFFF) { contentBytes = new byte[temp.Length + 4]; contentBytes[0] = 0x81; contentBytes[1] = 126; contentBytes[2] = (byte)(temp.Length & 0xFF); contentBytes[3] = (byte)(temp.Length >> 8 & 0xFF); Array.Copy(temp, 0, contentBytes, 4, temp.Length); }else { // 暂不处理超长内容 } return contentBytes; }
- 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
Web Sockets 是一个很是棒的设计,想要了解更多,参考这里。
http://www.javashuo.com/article/p-zfliclhu-kc.html
(2)服务器发送事件(Server-Sent Events)
运行原理
浏览器经过HTTP向服务器发送请求,服务器端拿出数据库中的最新的信息,当即返回给客户端,客户端等待三秒后再次发出下一个请求。
客户端示例
<script> if(typeof(EventSource)!=="undefined") { var source=new EventSource("/example/html5/demo_sse.php"); source.onmessage=function(event) { document.getElementById("result").innerHTML+=event.data + "<br />"; }; } else { document.getElementById("result").innerHTML="Sorry, your browser does not support server-sent events..."; } </script>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
服务器端示例
<?php header('Content-Type: text/event-stream'); header('Cache-Control: no-cache'); $time = date('r'); echo "data: The server time is: {$time}\n\n"; flush(); ?>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
想要对Server-Sent Events了解更多的,能够参考这两篇文章。
http://www.w3school.com.cn/html5/html_5_serversentevents.asp
http://www.ibm.com/developerworks/cn/web/1307_chengfu_serversentevent/
服务器负载
-Long-polling占一小部分CPU资源,可是建立空的进程将浪费系统的内存
-Server-Sent Events工做的方式有不少,除非Server-Sent Events没必要在每一次响应发出后都关闭链接。
-WebSockets,服务器只须要一个进程处理全部的请求,没有循环,没必要为每一个客户端都分配cpu和内存。
客户端负载
-Long-polling取决于实现方式,但终究是一个异步的方式
-Server-Sent Events采用浏览器的内置的实现方式,只花费不多的一部分资源。
-WebSockets跟Server-Sent Events同样,采用浏览器的内置的实现方式,只花费不多的一部分资源。
时间线
-Long-polling接近实时,可是发送新的请求和发送相应会有必定的时延。
-Server-Sent Events默认延时3秒,可是能够调整。
-WebSockets真正的实时
实现方式复杂度
-Long-polling实现起来很是简单
-Server-Sent Events甚至比Long-polling更简单
-须要一个WebSockets服务器处理事件,并开放一个端口
想要对轮询,Web Sockets,Server-Sent Events三者的区别有所了解的,能够参考这篇文章。
https://www.oschina.net/question/82993_63312
⑤ 网页多媒体特性(MULTIMEDIA)
看一下W3C的定义。
Audio and video are first class citizens in the HTML5 web, living in harmony with your apps and sites. Lights, camera, action!
看的出来HTML5原生支持音视频让W3C很兴奋。也是广大开发者多年的期待。终于能够将Flash踹入茅坑了。
Audio 和 Video 标签
虽然支持音视频很强大,可是确实没有什么好说的,就是两个标签。
<audio controls="controls"> <source src="song.ogg" type="audio/ogg"> <source src="song.mp3" type="audio/mpeg"> Your browser does not support the audio tag. </audio>
- 1
- 2
- 3
- 4
- 5
<video width="320" height="240" controls="controls"> <source src="movie.ogg" type="video/ogg"> <source src="movie.mp4" type="video/mp4"> Your browser does not support the video tag. </video>
- 1
- 2
- 3
- 4
- 5
可是因为音视频相对于其余新特性来讲会被更多的使用,各个浏览器的兼容问题应该引发开发者的重视。统一各平台的播放器ckplayer值得借鉴。能够参考此网页http://www.ckplayer.com/。
Audio API
HTML5 音频处理接口与Audio标签是不同的。页面上的Audio标签只是HTML5更语义化的一个表现,而HTML5提供给JavaScript编程用的Audio API则让咱们有能力在代码中直接操做原始的音频流数据,对其进行任意加工再造。
先上demo,http://wayou.github.io/HTML5_Audio_Visualizer/,打开页面以后,打开一首本地歌曲。而后能够显示可视化效果。
原理
一段音频到达扬声器进行播放以前,半路对其进行拦截,因而咱们就获得了音频数据了,这个拦截工做是由window.AudioContext来作的,咱们全部对音频的操做都基于这个对象。经过AudioContext能够建立不一样各种的AudioNode,即音频节点,不一样节点做用不一样,有的对音频加上滤镜好比提升音色(好比BiquadFilterNode),改变单调,有的音频进行分割,好比将音源中的声道分割出来获得左右声道的声音(ChannelSplitterNode),有的对音频数据进行频谱分析。
全部的操做都是基于AudioContext这个对象进行的。
获得AudioContext对象。
window.AudioContext = window.AudioContext || window.webkitAudioContext || window.mozAudioContext || window.msAudioContext; try { var audioContext = new window.AudioContext(); } catch (e) { Console.log('!Your browser does not support AudioContext'); }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
后续的操做,都是与音频解码相关的。想要了解更多HTML5 Audio API。能够参考下面的文章。
http://www.cnblogs.com/Wayou/p/3543577.html
https://developer.mozilla.org/zh-CN/docs/Web/API/AudioContext
搜索了一下,并无找到与Audio API这么强大的功能同一级别的Video API。
⑥ 三维、图形及特效特性(3D, Graphics & Effects)
来看W3C的介绍。
Between SVG, Canvas, WebGL, and CSS3 3D features, you’re sure to amaze your users with stunning visuals natively rendered in the browser.
大体包含SVG, Canvas, WebGL, 和 CSS3 3D,下面分别进行研究。
(1)可缩放矢量图形(SVG)
SVG是用于描述二维矢量图形的一种图形格式。
先上demo,http://www.ganecheng.tech/html5/svg.html
与其余图像格式相比,使用 SVG 的优点在于:
-SVG 可被很是多的工具读取和修改(好比记事本)
-SVG 与 JPEG 和 GIF 图像比起来,尺寸更小,且可压缩性更强。
-SVG 是可伸缩的
-SVG 图像可在任何的分辨率下被高质量地打印
-SVG 可在图像质量不降低的状况下被放大
-SVG 图像中的文本是可选的,同时也是可搜索的(很适合制做地图)
-SVG 能够与 Java 技术一块儿运行
-SVG 是开放的标准
-SVG 文件是纯粹的 XML
SVG有三种用法。
① 把SVG直接当成图片放在网页上
<img style="width: 250px;" alt="kiwi" src="./kiwi.svg">
- 1
② SVG动画
画一个五角星
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" height="190"> <polygon points="100,10 40,180 190,60 10,60 160,180" style="fill:red;stroke:blue;stroke-width:3;fill-rule:evenodd;" /> </svg>
- 1
- 2
- 3
- 4
③ SVG图片的交互和滤镜效果
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="612px" height="502.174px" viewBox="0 65.326 612 502.174" enable-background="new 0 65.326 612 502.174" xml:space="preserve" class="logo99"> <style> .kiwi { fill: #94d31b; // stroke: #909; // stroke-width: 10; // not sure which of these is correct // stroke-linejoin : butt | round | square; // stroke-linecap: butt| round| square; // stroke-dasharray: 5, 5, 10; // filter: url(#pictureFilter); // mask: url(#f1); // clip-path: url(); } .kiwi:hover { fill: #ace63c; } .ground { fill: #787f6a; } .ground:hover { filter: url(#pictureFilter); fill: #896d3d; } </style> <ellipse class="ground" cx="283.5" cy="487.5" rx="259" ry="80"></ellipse> <path class="kiwi" d="M210.333,65.331C104.367,66.105-12.349,150.637,1.056,276.449c4.303,40.393,18.533,63.704,52.171,79.03 c36.307,16.544,57.022,54.556,50.406,112.954c-9.935,4.88-17.405,11.031-19.132,20.015c7.531-0.17,14.943-0.312,22.59,4.341 c20.333,12.375,31.296,27.363,42.979,51.72c1.714,3.572,8.192,2.849,8.312-3.078c0.17-8.467-1.856-17.454-5.226-26.933 c-2.955-8.313,3.059-7.985,6.917-6.106c6.399,3.115,16.334,9.43,30.39,13.098c5.392,1.407,5.995-3.877,5.224-6.991 c-1.864-7.522-11.009-10.862-24.519-19.229c-4.82-2.984-0.927-9.736,5.168-8.351l20.234,2.415c3.359,0.763,4.555-6.114,0.882-7.875 c-14.198-6.804-28.897-10.098-53.864-7.799c-11.617-29.265-29.811-61.617-15.674-81.681c12.639-17.938,31.216-20.74,39.147,43.489 c-5.002,3.107-11.215,5.031-11.332,13.024c7.201-2.845,11.207-1.399,14.791,0c17.912,6.998,35.462,21.826,52.982,37.309 c3.739,3.303,8.413-1.718,6.991-6.034c-2.138-6.494-8.053-10.659-14.791-20.016c-3.239-4.495,5.03-7.045,10.886-6.876 c13.849,0.396,22.886,8.268,35.177,11.218c4.483,1.076,9.741-1.964,6.917-6.917c-3.472-6.085-13.015-9.124-19.18-13.413 c-4.357-3.029-3.025-7.132,2.697-6.602c3.905,0.361,8.478,2.271,13.908,1.767c9.946-0.925,7.717-7.169-0.883-9.566 c-19.036-5.304-39.891-6.311-61.665-5.225c-43.837-8.358-31.554-84.887,0-90.363c29.571-5.132,62.966-13.339,99.928-32.156 c32.668-5.429,64.835-12.446,92.939-33.85c48.106-14.469,111.903,16.113,204.241,149.695c3.926,5.681,15.819,9.94,9.524-6.351 c-15.893-41.125-68.176-93.328-92.13-132.085c-24.581-39.774-14.34-61.243-39.957-91.247 c-21.326-24.978-47.502-25.803-77.339-17.365c-23.461,6.634-39.234-7.117-52.98-31.273C318.42,87.525,265.838,64.927,210.333,65.331 z M445.731,203.01c6.12,0,11.112,4.919,11.112,11.038c0,6.119-4.994,11.111-11.112,11.111s-11.038-4.994-11.038-11.111 C434.693,207.929,439.613,203.01,445.731,203.01z"> </path> <filter id="pictureFilter"> <feGaussianBlur stdDeviation="15"></feGaussianBlur> </filter> </svg>
- 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
如何用SVG画出一条会动的线
先看demo,http://www.ganecheng.tech/html5/svg_animate_line.html
<path fill="none" stroke="deeppink" stroke-width="14" stroke-miterlimit="0" d="M11.6 269s-19.7-42.4 6.06-68.2 48.5-6.06 59.1 12.1l-3.03 28.8 209-227s45.5-21.2 60.6 1.52c15.2 22.7-3.03 47-3.03 47l-225 229s33.1-12 48.5 7.58c50 63.6-50 97-62.1 37.9" stroke-dasharray="988.0 988.0" stroke-dashoffset="988.0" />
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
var path = document.querySelector('.squiggle-animated path'); var length = path.getTotalLength(); // 清除以前的动做 path.style.transition = path.style.WebkitTransition = 'none'; // 设置起始点 path.style.strokeDasharray = length + ' ' + length; path.style.strokeDashoffset = length; // 获取一个区域,获取相关的样式,让浏览器寻找一个起始点。 path.getBoundingClientRect(); // 定义动做 path.style.transition = path.style.WebkitTransition = 'stroke-dashoffset 2s ease-in-out'; // Go! path.style.strokeDashoffset = '0';
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
原理:使用d属性获得一条贝塞尔曲线,使每段线段间距值为线段的1倍长度,慢慢下降偏移值,获得正向的移动效果。
属性stroke-dasharray是让你指定画出的线段每段的长度,第二个值是各段之间空隙的长度。
属性stroke-dashoffset是让你指定每一个小段的起始偏移量。
想对SVG了解更多的,能够参考下面的网址。
http://www.w3school.com.cn/svg/svg_examples.asp
http://www.w3school.com.cn/svg/svg_reference.asp
http://www.webhek.com/animated-line-drawing-in-svg/
http://www.webhek.com/demo/svg/
(2)画布(Canvas)
Canvas也是前端工程师炫技的最热门的阵地。有许多demo。
http://www.17sucai.com/pins/demoshow/9386
http://www.17sucai.com/pins/demoshow/12676
http://www.17sucai.com/pins/demoshow/5937
http://www.html5tricks.com/demo/html5-canvas-waterful/index.html
http://www.html5tricks.com/demo/html5-canvas-loader/index.html
http://www.html5tricks.com/demo/html5-canvas-particle-effect/index.html
http://www.html5tricks.com/demo/html5-cool-fire-effect/index.html
HTML5 的 canvas 元素使用 JavaScript 在网页上绘制图像。
画布是一个矩形区域,您能够控制其每一像素。
canvas 拥有多种绘制路径、矩形、圆形、字符以及添加图像的方法。
建立 Canvas 元素
<canvas id="myCanvas" width="200" height="100"></canvas>
- 1
经过 JavaScript 来绘制
canvas 元素自己是没有绘图能力的。全部的绘制工做必须在 JavaScript 内部完成:
<script type="text/javascript"> var c=document.getElementById("myCanvas"); var cxt=c.getContext("2d"); cxt.fillStyle="#FF0000"; cxt.fillRect(0,0,150,75); </script>
- 1
- 2
- 3
- 4
- 5
- 6
Canvas 与 SVG 对比
Canvas 和 SVG 都容许您在浏览器中建立图形,可是它们在根本上是不一样的。
SVG
SVG 是一种使用 XML 描述 2D 图形的语言。
SVG 基于 XML,这意味着 SVG DOM 中的每一个元素都是可用的。您能够为某个元素附加 JavaScript 事件处理器。
在 SVG 中,每一个被绘制的图形均被视为对象。若是 SVG 对象的属性发生变化,那么浏览器可以自动重现图形。
Canvas
Canvas 经过 JavaScript 来绘制 2D 图形。
Canvas 是逐像素进行渲染的。
在 canvas 中,一旦图形被绘制完成,它就不会继续获得浏览器的关注。若是其位置发生变化,那么整个场景也须要从新绘制,包括任何或许已被图形覆盖的对象。
Canvas 与 SVG 的比较
Canvas
-依赖分辨率
-不支持事件处理器
-弱的文本渲染能力
-可以以 .png 或 .jpg 格式保存结果图像
-最适合图像密集型的游戏,其中的许多对象会被频繁重绘
SVG
-不依赖分辨率
-支持事件处理器
-最适合带有大型渲染区域的应用程序(好比谷歌地图)
-复杂度高会减慢渲染速度(任何过分使用 DOM 的应用都不快)
-不适合游戏应用
想对Canvas了解更多的,能够参考下面连接。
http://www.w3school.com.cn/html/html5_canvas.asp
http://www.w3school.com.cn/tags/html_ref_canvas.asp
https://developer.mozilla.org/zh-CN/docs/Web/API/Canvas_API
http://blog.csdn.net/clh604/article/details/8536059
(3)网页图形库(WebGL)
先上demo。
https://www.cubeslam.com/ncokau
http://labs.gooengine.com/pearl-boy/indexBelow.html
http://www.cnblogs.com/lhb25/p/9-mind-blowing-webgl-demos.html
http://www.open-open.com/news/view/9d8136/
http://www.oschina.net/news/26547/webgl-chrome/
WebGL(全写Web Graphics Library)是一种3D绘图标准,这种绘图技术标准容许把JavaScript和OpenGL ES 2.0结合在一块儿,经过增长OpenGL ES 2.0的一个JavaScript绑定,WebGL能够为HTML5 Canvas提供硬件3D加速渲染,这样Web开发人员就能够借助系统显卡来在浏览器里更流畅地展现3D场景和模型了,还能建立复杂的导航和数据视觉化。显然,WebGL技术标准免去了开发网页专用渲染插件的麻烦,可被用于建立具备复杂3D结构的网站页面,甚至能够用来设计3D网页游戏等等。
WebGL 基本原理
WebGL 的出现使得在浏览器上面实时显示 3D 图像成为现实,WebGL 本质上是基于光栅化的 API ,而不是基于 3D 的 API。
WebGL对象获取
WebGL也是基于Canvas画布作的,下面看一下WebGL获取方法。
// Get A WebGL context var canvas = document.getElementById("canvas"); var gl = canvas.getContext("experimental-webgl");
- 1
- 2
- 3
WebGL 只关注两个方面,即投影矩阵的坐标和投影矩阵的颜色。使用 WebGL 程序的任务就是实现具备投影矩阵坐标和颜色的 WebGL 对象便可。能够使用“着色器”来完成上述任务。顶点着色器能够提供投影矩阵的坐标,片断着色器能够提供投影矩阵的颜色。
不管要实现的图形尺寸有多大,其投影矩阵的坐标的范围始终是从 -1 到 1 。下面是一个关于实现 WebGL 对象的一个简单例子。
// Get A WebGL context
var canvas = document.getElementById("canvas"); var gl = canvas.getContext("experimental-webgl"); // setup a GLSL program var program = createProgramFromScripts(gl, ["2d-vertex-shader", "2d-fragment-shader"]); gl.useProgram(program); // look up where the vertex data needs to go. var positionLocation = gl.getAttribLocation(program, "a_position"); // Create a buffer and put a single clipspace rectangle in // it (2 triangles) var buffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, buffer); gl.bufferData( gl.ARRAY_BUFFER, new Float32Array([ -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0]), gl.STATIC_DRAW); gl.enableVertexAttribArray(positionLocation); gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0); // draw gl.drawArrays(gl.TRIANGLES, 0, 6);
- 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
下面是两个着色器。
<script id="2d-vertex-shader" type="x-shader/x-vertex"> attribute vec2 a_position; void main() { gl_Position = vec4(a_position, 0, 1); } </script> <script id="2d-fragment-shader" type="x-shader/x-fragment"> void main() { gl_FragColor = vec4(0, 1, 0, 1); // green } </script>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
它将绘出一个绿色的长方形来填充整个画板。
再日后面实在是太过专业了,须要掌握图形图像学的知识,经过demo确实能看出来很是强大,可是须要更专业的人才能搞了。
想对WebGL了解更多,参考这里。
https://developer.mozilla.org/zh-CN/docs/Web/API/WebGL_API
http://blog.csdn.net/column/details/webgl.html
http://www.w3cschool.cn/webgl/i4gf1oh1.html
http://blog.163.com/hongshaoguoguo@126/blog/static/18046981201311310510380/
(4)CSS3 3D(CSS3 3D)
先上demo。
http://www.ganecheng.tech/html5/css33d.html
CSS3容许元素以3D的形式显示。
下面讲解一下这个demo是怎样显示出来的。
<!DOCTYPE html> <html lang="zh-cn"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <style type="text/css"> *{margin: 0;padding: 0;} html,body{height: 80%;background: black;} .wrap { height: 100%;position: relative; -webkit-transform-style:preserve-3d; -webkit-perspective:0px; -moz-transform-style:preserve-3d; -moz-perspective:0px; -webkit-animation:mydhua 5s ease infinite; -moz-animation:mydhua 5s ease infinite; } .box { width: 200px; height: 200px; position: absolute; top: 50%; left: 50%; margin:-100px 0 0 -100px; line-height: 200px; text-align: center; font-size: 48px; color: white; } .box1{ -webkit-transform:rotatey(90deg) translatez(-100px); -moz-transform:rotatey(90deg) translatez(-100px); background: rgba(128,0,128,.5); } .box2{ -webkit-transform:rotatey(90deg) translatez(100px); -moz-transform:rotatey(90deg) translatez(100px); background: rgba(255,0,255,.5); } .box3{ -webkit-transform:rotatex(90deg) translatez(100px); -moz-transform:rotatex(90deg) translatez(100px); background: rgba(255,153,204,.5); } .box4{ -webkit-transform:rotatex(90deg) translatez(-100px); -moz-transform:rotatex(90deg) translatez(-100px); background: rgba(0,204,255,.5); } .box5{ -webkit-transform: translatez(-100px); -moz-transform:translatez(-100px); background: rgba(153,204,255,.5); } .box6{ -webkit-transform: translatez(100px); -moz-transform:translatez(100px); background: rgba(0,255,255,.5); } @-webkit-keyframes mydhua{ 0%{-webkit-transform:rotateX(0deg) rotateY(0deg) rotateZ(0deg);-webkit-transform-origin: center center;} 100%{-webkit-transform:rotateX(180deg) rotateY(180deg) rotateZ(180deg);-webkit-transform-origin: center center; } } @-moz-keyframes mydhua{ 0%{-moz-transform:rotateX(0deg) rotateY(0deg) rotateZ(0deg);-webkit-transform-origin: center center;} 100%{-moz-transform:rotateX(180deg) rotateY(180deg) rotateZ(180deg); -webkit-transform-origin: center center;} } </style> </head> <body style="width:960px;margin:0px auto;"> <div class="wrap"> <div class="box1 box">1</div> <div class="box2 box">2</div> <div class="box3 box">3</div> <div class="box4 box">4</div> <div class="box5 box">5</div> <div class="box6 box">6</div> </div> <script type="text/javascript"> </script> </body> </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
首先是使子元素能够以3D的效果显示出来。
.wrap { height: 100%;position: relative; -webkit-transform-style:preserve-3d; -webkit-perspective:0px; -moz-transform-style:preserve-3d; -moz-perspective:0px; }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
接下来规定6个div的初始位置。
rotateX()表示沿 X 轴的 3D 旋转角度。
rotateY()表示沿 Y 轴的 3D 旋转角度。
translateZ() 表示 3D 转化,仅使用用于 Z 轴的值。表示在Z轴的移动位置,值越大,离屏幕越近,值越小,离屏幕越远。在此网址感觉一下translateZ()的效果。http://www.zhangxinxu.com/study/201209/transform-perspective-translateZ.html
<div class="box1 box">1</div> <div class="box2 box">2</div> <div class="box3 box">3</div> <div class="box4 box">4</div> <div class="box5 box">5</div> <div class="box6 box">6</div> .box { width: 200px; height: 200px; position: absolute; top: 50%; left: 50%; margin:-100px 0 0 -100px; line-height: 200px; text-align: center; font-size: 48px; color: white; } .box1{ -webkit-transform:rotatey(90deg) translatez(-100px); -moz-transform:rotatey(90deg) translatez(-100px); background: rgba(128,0,128,.5); } .box2{ -webkit-transform:rotatey(90deg) translatez(100px); -moz-transform:rotatey(90deg) translatez(100px); background: rgba(255,0,255,.5); } .box3{ -webkit-transform:rotatex(90deg) translatez(100px); -moz-transform:rotatex(90deg) translatez(100px); background: rgba(255,153,204,.5); } .box4{ -webkit-transform:rotatex(90deg) translatez(-100px); -moz-transform:rotatex(90deg) translatez(-100px); background: rgba(0,204,255,.5); } .box5{ -webkit-transform: translatez(-100px); -moz-transform:translatez(-100px); background: rgba(153,204,255,.5); } .box6{ -webkit-transform: translatez(100px); -moz-transform:translatez(100px); background: rgba(0,255,255,.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
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
如今定义关键帧动画。
用@keyframes定义一个关键帧动画,0%表示起始状态,100%表示结束状态,还能够根据须要定义50%等等。动画的名称叫mydhua。
@-webkit-keyframes mydhua{ 0%{-webkit-transform:rotateX(0deg) rotateY(0deg) rotateZ(0deg);-webkit-transform-origin: center center;} 100%{-webkit-transform:rotateX(180deg) rotateY(180deg) rotateZ(180deg);-webkit-transform-origin: center center; } } @-moz-keyframes mydhua{ 0%{-moz-transform:rotateX(0deg) rotateY(0deg) rotateZ(0deg);-webkit-transform-origin: center center;} 100%{-moz-transform:rotateX(180deg) rotateY(180deg) rotateZ(180deg); -webkit-transform-origin: center center;} }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
接下来就是使用动画。
.wrap { -webkit-animation:mydhua 5s ease infinite; -moz-animation:mydhua 5s ease infinite; }
- 1
- 2
- 3
- 4
- 5
这样咱们就获得了一个CSS3 3D旋转的效果。
只要涉及到3D,都和空间立体几何相关。若是想往这方面更进一步发展,桌前要常备这一类的书籍。
想要对CSS3 3D了解更多的,能够参考下面网址。
http://www.javashuo.com/article/p-pdfieeia-kr.html
http://www.w3school.com.cn/css3/css3_3dtransform.asp
http://www.cnblogs.com/duanhuajian/archive/2012/08/30/2664026.html
⑦ 性能与集成特性(Performance & Integration)
看看W3C的介绍。
Make your Web Apps and dynamic web content faster with a variety of techniques and technologies such as Web Workers and XMLHttpRequest 2. No user should ever wait on your watch.
性能与集成特性主要包括两个东西,Web Workers 和 XMLHttpRequest 2。
下面依次介绍。
(1)网页后台任务(Web Workers)
先上demo,http://www.w3school.com.cn/tiy/t.asp?f=html5_webworker。
当在 HTML 页面中执行脚本时,页面的状态是不可响应的,直到脚本已完成。
Ajax向服务器端发送请求,是异步接收响应的。否则页面会卡住。
Web Workers 是运行在浏览器后台的 JavaScript,独立于其余脚本,不会影响页面的性能。您能够继续作任何愿意作的事情:点击、选取内容等等,而此时 Web Workers 在后台运行。
setInterval和setTimeout是单线程执行的。
虽然在JavaScript中有setInterval和setTimeout函数使javaScript看起来好像使多线程执行,单实际上JavaScript是单线程的,一次只能作一件事情。
看一个例子。
<!DOCTYPE html> <html> <head> <title>Web Workers</title> </head> <body> <h1>Web Workers</h1> <script type="text/javascript"> setTimeout(function(){ console.log('timeout function'); },1000); alert('do not close'); </script> </body> </html>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
页面一运行就会弹出一个对话框,若是setTimeout是在另一个线程运行,那么过一秒钟控制台就会打印“timeout function”,事实是只要不关闭对话框,控制台永远不会输出文字,这两句话确实是在一个线程内运行的。
这样的设计使JavaScript比较简单,但有时候也很使人烦恼,由于单线程的设计意味着JavaScript代码必须很快运行完,常见的问题就是一段复杂的JavaScript脚本会中断页面其它脚本执行,甚至会出现页面失去响应,这也就是为何Ajax的API要设计成异步的。
Web Workers 用法
在html5规范中引入了web workers概念,解决客户端JavaScript没法多线程的问题,其定义的worker是指代码的并行线程,不过web worker处于一个自包含的环境中,没法访问主线程的window对象和document对象,和主线程通讯只能经过异步消息传递机制。
咱们须要把但愿单独执行的javascript代码放到一个单独的js文件中,而后在页面中调用Worker构造函数来建立一个线程,参数是该文件路径,参数存放若是是相对地址,那么要以包含调用Worker构造函数语句所在脚本为参照,若是是绝对路径,须要保证同源(协议+主机+端口)。这个文件不须要咱们在页面使用script标签显示引用
w=new Worker("/example/html5/demo_workers.js");
- 1
worker对象只有两个属性,实际上是两个回调函数句柄。
onerror:当worker运行出现错误,而且没有在worker中被捕获,会在此捕获
onmessage:当worker向主线程发送消息是调用
在其prototype内有两个重要方法
postMessage:很熟悉的赶脚,以前咱们介绍过window对象的postMessage()方法,woker的postMessage方法和window的比较相似,但参数略有不一样,只须要传递消息内容就能够,并且支持全部JavaScript原生数据类型,固然不放心的话一样也能够序列化为字符串传递
terminate:终止worker执行,有些worker执行比较慢,主线程能够主动终止其执行
demo_workers.js
var i=0; function timedCount() { i=i+1; postMessage(i); setTimeout("timedCount()",500); } timedCount();
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
demo.html
<!DOCTYPE html> <html> <body> <p>计数: <output id="result"></output></p> <button onclick="startWorker()">开始 Worker</button> <button onclick="stopWorker()">中止 Worker</button> <br /><br /> <script> var w; function startWorker() { if(typeof(Worker)!=="undefined") { if(typeof(w)=="undefined") { w=new Worker("/example/html5/demo_workers.js"); } w.onmessage = function (event) { document.getElementById("result").innerHTML=event.data; }; } else { document.getElementById("result").innerHTML="Sorry, your browser does not support Web Workers..."; } } function stopWorker() { w.terminate(); } </script> </body> </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
想对 Web Workers 了解更多的,参考下面连接。
http://www.w3school.com.cn/html5/html_5_webworkers.asp
http://www.cnblogs.com/dolphinX/p/3452684.html
https://developer.mozilla.org/zh-CN/docs/Web/API/Web_Workers_API/Using_web_workers
(2)新的Ajax(XMLHttpRequest 2)
XMLHttpRequest是一个浏览器接口,使得Javascript能够进行HTTP(S)通讯。也就是Ajax。
上一代Ajax有如下缺点。
- 只支持文本数据的传送,没法用来读取和上传二进制文件。
- 传送和接收数据时,没有进度信息,只能提示有没有完成。
- 受到”同域限制”(Same Origin Policy),只能向同一域名的服务器请求数据,不能跨域。
XMLHttpRequest 2也就是新的Ajax。针对老版本的缺点,作出了大幅改进,有下面的特色。
- 能够设置HTTP请求的时限。
- 能够使用FormData对象管理表单数据。
- 能够上传文件。
- 能够请求不一样域名下的数据(跨域请求)。
- 能够获取服务器端的二进制数据。
- 能够得到数据传输的进度信息。
XMLHttpRequest 2示例
前台代码.
<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>第二代XmlHttpRequest异步上传</title> <script type="text/javascript"> function upload(){ if (!window.FormData){ alert('您的浏览器不支持第二代XmlHttpRequest'); return; } // HTML5 新增对象 var formData = new FormData(document.getElementById('uploadForm')); //添加其余表单域 formData.append('user', 'haolin'); formData.append('pass', '111111'); var xhr = new XMLHttpRequest(); xhr.open('POST', 'upload'); //请求url //上传完成回调函数 xhr.onload = function(event) { if (xhr.status === 200) { alert("上传成功"); } else { alert('出错了'); } }; xhr.send(formData); } </script> </head> <body> <h1>第二代XmlHttpRequest对象实现异步上传</h1> <form id="uploadForm" action="" method="post" enctype="multipart/form-data"> <input id="upfile" type="file" name="upfile"/> <input type="button" value="上传" onclick="upload()"/> </form> </body> </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
后台代码
PrintWriter out = new PrintWriter(response.getOutputStream()); FileItemFactory factory = new DiskFileItemFactory();// 为该请求建立一个DiskFileItemFactory对象,经过它来解析请求。执行解析后,全部的表单项目都保存在一个List中。 ServletFileUpload upload = new ServletFileUpload(factory); List<FileItem> items; try { items = upload.parseRequest(request); Iterator<FileItem> itr = items.iterator(); while (itr.hasNext()) { FileItem item = (FileItem) itr.next(); System.out.println("是不是FormField: " + item.isFormField()); System.out.println("接收到域: " + item.getFieldName()); System.out.println("接收到值: " + item.getString("utf-8")); // 检查当前项目是普通表单项目仍是上传文件。 if (item.isFormField()) {// 若是是普通表单项目,显示表单内容。 String fieldName = item.getFieldName(); out.println("the field name is " + fieldName);// 显示表单域名称。 } else {// 若是是上传文件,显示文件名。 out.println("the upload file name is " + item.getName()); } } out.flush(); out.close(); } catch (FileUploadException e) { e.printStackTrace(); }
- 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
对XMLHttpRequest 2进行封装。
var AjaxForm = function(cfg){ if (!window.FormData){ alert("Sorry, your browser doesn't supoort FormData!"); } /** * null or undefined 返回true, 不然false */ this.isNullOrUndefined = function(v, errMsg){ if (!v){ alert(errMsg); return true; } return false; }; var cfg = cfg || {}; if (this.isNullOrUndefined(cfg.id, "id can't be empty")) return; if (this.isNullOrUndefined(cfg.url, "url can't be empty")) return; this.id = cfg.id; // 表单id this.method = cfg.method || "POST"; //默认POST方法 this.url = cfg.url; this.async = !cfg.sync; //同步否 this.resultType = cfg.resultType || "text"; //返回结果类型 json对象或text this.formData = new FormData(document.getElementById(this.id)); //form数据 this.xhr = new XMLHttpRequest(); //当前请求对象 /** * 超时事件 * 配置格式: * timeout : xxx, * onTimeout: function(event){} */ if (cfg.timeout){ this.xhr.timeout = cfg.timeout; this.xhr.ontimeout = cfg.onTimeout; } /** * 发送过程事件 * 配置格式: * onProgress: function(loaded, total){} */ if (cfg.onProgress){ //发送数据过程 this.xhr.upload.onprogress = function(e){ if (e.lengthComputable) { cfg.onProgress(e.loaded, e.total); } }; } /** * 上传完成事件 */ if (cfg.onComplete){ this.xhr.onload = function(event){ var res = event.target.responseText; if (this.resultType === 'json'){ if ((typeof JSON) === 'undefine'){ res = eval("("+res+")"); } else{ res = JSON.parse(res); } } cfg.onComplete(res); }; } /** * 发出请求 */ this.request = function(){ this.xhr.open(this.method, this.url, this.async); this.xhr.send(this.formData); }; };
- 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
调用
var af = new AjaxForm({ id: "uploadForm", url: 'upload', method: 'POST', timeout: 5000, onTimeout: function(event){ alert('It is timeout.'); }, onProgress: function(loaded, total){ var complete = (loaded / total * 100 | 0); var progress = document.getElementById('uploadProgress'); progress.value = complete; progress.innerHTML = complete; }, onComplete: function(result){ alert(result); } }); af.request();
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
能够看出,虽然是XMLHttpRequest 2,可是仍是XMLHttpRequest对象。使用新的特性,须要先判断是否支持window.FormData。
若是想对XMLHttpRequest 2了解更多,能够参考下面文章。
https://www.w3.org/TR/XMLHttpRequest2
https://my.oschina.net/indestiny/blog/215472
http://www.ruanyifeng.com/blog/2012/09/xmlhttprequest_level_2.html
http://blog.csdn.net/hills/article/details/41246939
后记
以前一直在作web开发。可是历来没有对HTML5仔细研究过,写这篇文章,前先后后花了4天时间,从惊叹于HTML5的强大,到目击有这么多的坑,浏览器的性能也跟不上。
可是咱们不该该悲观,解决方法总比困难多,Chrome和Firefox也一直在推进浏览器性能提高。前端不再是切个图,调调颜色,改改字体大小,放个Flash的时代了。HTML5会给咱们一个更加美好的将来,前端开发者也有一个提高技术含量,迎接各类机遇的机会了。
祝愿前端愈来愈好。