有很長一段時間沒有更新博客了,近一段時間開始重新梳理知識點和寫博客了。重要的事情說三遍,新的博客地址:歡迎訪問,新的博客地址:歡迎訪問,新的博客地址:歡迎訪問。
Web Workers##
javaScript語言採用的是單線程模型,也就是說,所有任務排成一個隊列,一次只能做一件事。
Web Worker的目的:就是爲JavaScript創造多線程環境,允許主線程將一些任務分配給子線程。在主線程運行的同時,子線程在後臺運行,兩者互不干擾。等到子線程完成計算任務,再把結果返回給主線程。
當在 HTML 頁面中執行腳本時,頁面的狀態是不可響應的,直到腳本已完成。web worker 是運行在後臺的 JavaScript,獨立於其他腳本,不會影響頁面的性能。您可以繼續做任何願意做的事情:點擊、選取內容等等,而此時 web worker 在後臺運行。可以讓web應用程序具備後臺處理能力,對多線程的支持非常好。
###Web Worker API###
-
new Worker(‘後臺處理的JS地址’)
使用Web Worker:
實例化Worker對象並傳入要執行的Javascript文件名就可以創建一個新的Web Worker。
例如:var worker = new Worker(“worker.js”);
這行代碼會導致瀏覽器下載worker.js,但只有Worker接收到消息纔會實際執行文件中的代碼。 -
利用postMessage傳輸數據
要給Worker傳遞消息,可以使用postMessage()方法。
Worker是通過message和error事件與頁面通信的。來自Worker的數據保存在event.data中。Worker返回的數據也可以是任何能夠被序列化的值。
Worker不能完成給定的任務時就會觸發error事件。具體來說,Worker內部的JavaScript在執行過程中只要遇到錯誤,就會觸發error事件。發生error事件時,**事件對象中包含3個屬性:**filename、lineto和message,分別表示發生錯誤的文件名、代碼行號和完整的錯誤信息 -
**terminate() 方法:**終止 web worker,並釋放瀏覽器/計算機資源(這個方法是在頁面中調用的)
-
importScripts(‘導入其他JS文件’)
###Worker全局作用域###
關於Web Worker,**最重要的是:**要知道它所執行的JavaScript代碼完全在另一個作用域中,與當前網頁中的代碼不共享作用域。在Web Worker中,同樣有一個全局對象和其他對象以及方法。但是,Web Worker中的代碼不能訪問DOM,也無法通過任何方式影響頁面的外觀。
Web Worker中的全局對象是worker對象本身。也就是說,在這個特殊的全局作用域中,this和self引用的都是worker對象。
爲了便於處理數據,Web Worker本身也是一個最小化的運行環境: -
最小化的navgator對象 : online、appName、appVersion、userAgent、platform
-
只讀的location對象 : 所有屬性都是隻讀的
-
self : 指向全局 worker 對象
-
所有的ECMA對象,Object、Array、Date等
-
XMLHttpRequest構造器
-
setTimeout和setInterval方法
-
close()方法,立刻停止worker運行(在worker內部使用,而terminate() 方法是在頁面中調用的)
-
importScripts方法
在Worker的全局作用域中,我們可以調用importScripts方法來接收一個或者多個JavaScript文件的URL。每個加載過程都是異步進行的,因此所有腳本加載並執行之後,importScripts()纔會執行。
例如:
importScripts(‘file1.js’,‘file2.js’);
即使file2.js先於file1.js下載完,執行的時候仍然會按照先後順序執行。這些腳本都是在Worker的全局作用域中執行的。Worker中的腳本一般都具有特殊的用途,不會像頁面中的腳本那麼功能寬泛。
Web Worker的運行環境與頁面環境相比,功能是相當有限的。
Demo1:
<body>
<p>計數:<span id="res"></span></p>
<input type="button" name="" value="開始計數" onclick="startWorker()">
<input type="button" name="" value="停止計數" onclick="stopWorker()">
<script type="text/javascript">
var w;
function startWorker(){
//先判斷是否支持Web Worker
if(typeof(Worker) !== "undefined"){
if(typeof(w) == "undefined"){
//新建一個worker對象
w = new Worker("webworker1.js");
}
w.onmessage = function(e){
document.getElementById("res").innerHTML = e.data;
}
}else{
alert("對不起,當前瀏覽器不支持web Workers");
}
}
function stopWorker(){
w.terminate();
w = undefined;
}
</script>
</body>
webworker1.js
var i=0;
function numCount(){
i++;
self.postMessage(i);
setTimeout("numCount()",500);
}
numCount();
Demo2:
<script type="text/javascript">
var data = [23,87,45,12,56,9,34];
console.log("排序前:"+data);
var worker = new Worker("webworkers2.js");
worker.onmessage = function(e){
var data = e.data;
console.log("排序後:"+data);//排序後:9,12,23,34,45,56,87
};
worker.onerror = function(e){
console.log("Error:"+e.filename+"("+e.lineto+"):"+e.message);
}
worker.postMessage(data);
</script>
webworker2.js
//這裏self引用的是Worker全局作用域中的worker對象(與頁面中的Worker對象不是同一個對象)
self.onmessage = function(e){
var data = e.data;
data.sort(function(a,b){
return a-b;//從小到大排序
});
//Worker完成工作後,通過調用postMessage()可以把數據再發回頁面
//這裏將數組排序後的結果發回頁面
//排序的確是比較消耗時間的操作,因此轉交給Worker做就不會阻塞用戶界面了
self.postMessage(data);
};
當頁面在worker對象上調用postMessage()方法時,數據會以異步方式被傳遞給worker,進而觸發worker中的message事件。爲了處理來自頁面的數據,同樣也需要創建一個onmessage事件處理程序。
**Web Worker:**可以運行異步Javascript代碼,避免阻塞用戶界面。在執行復雜計算和數據處理的時候,這個API非
常有用;否則,這些任務輕則會佔用很長時間,重則會導致用戶無法與頁面交互。
Hisyory API
在HTML5之前,即使採用的是腳本語言的方式,只要瀏覽器地址欄中的URL地址被切換,都會觸發一個頁面刷新的過程,這個過程將耗費一些時間與資源。在很多時候,尤其是在兩個大部分內容相同的頁面之間進行切換時,這個過程往往被視爲一種浪費。
HTML5的Hisyory API允許在不刷新頁面的前提下,通過腳本語言的方式來進行頁面上某塊局部內容的更新。
處理方式:
- 通過Ajax請求向服務器端請求頁面B與頁面A中不同的局部區域及該區域中的信息
- 在頁面A中通過腳本語言裝載該區域及其中的信息
- 通過Hisyory API在不刷新頁面的前提下在瀏覽器的地址欄中從頁面A的URL地址切換到頁面B的URL地址
在Hisyory API中,我們使用pushState方法在加載完相應的文件後,將該文件的URL地址添加到瀏覽器的歷史記錄中。
pushState方法使用3個參數:
- 參數1:可以爲任何對象,用於保存一個當用戶單擊瀏覽器後退按鈕或前進按鈕時可以使用的數據
- 參數2:可以爲一個字符串,用於設置瀏覽器窗口的標題,但是目前尚沒有任何瀏覽器支持該參數的使用,所以暫時可以將該參數設爲null
- 參數3:是可選參數,參數值可以爲任何URL地址,該URL地址將出現在用戶單擊瀏覽器後退按鈕或前進按鈕後瀏覽器的地址欄中
此外,我們還需要監聽popstate事件,當用戶單擊瀏覽器的後退或前進按鈕時觸發該事件,在事件處理函數中讀取觸發事件的事件對象的state屬性值,該屬性值即爲執行pushState()方法時所使用的第一個參數值,其中保存了在向瀏覽器歷史記錄中添加記錄時同步保存的對象,可以讀取該對象並根據該對象值設置當前頁面中所需顯示的數據。
我GitHub上的一個History API使用示例:傳送門
canvas
###canvas寬高設置 ##
canvas跟其他標籤一樣,也可以通過css來定義樣式。但這裏需要注意的是:canvas的默認寬高爲300px * 150px,在css中爲canvas定義寬高,實際上把寬高爲300px * 150px的畫布進行了拉伸,如果在這樣的情況下進行canvas繪圖,你得到的圖形可能就是變形的效果。所以,在canvas繪圖時,應該在canvas標籤裏直接定義寬高。
在css中定義的canvas寬和高只是其在html中的顯示寬高,不是canvas的真實分辨率,canvas的真是分辨率需要通過其width和height屬性來設置。
###save和restore方法##
save()方法調用後,將所有設置都會進入一個棧結構,得以妥善保管。然後可以對上下文進行其他修改
棧結構–>後進先出
想要返回之前保存的設置,可以調用restore()方法,在保存設置的棧結構中向前返回一級,恢復之前的狀態。
連續調用save()方法可以將更多設置保存在棧結構中,之後再連續調用restore()方法則可以一級一級返回
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Canvas Save-Restore</title>
</head>
<body>
<canvas id="drawing" width="200" height="200">
當前瀏覽器不支持canvas
</canvas>
<script type="text/javascript">
var drawing = document.getElementById("drawing");
//確保瀏覽器支持canvas元素
if (drawing.getContext){
var context = drawing.getContext("2d");
context.fillStyle = "#ff0000";//紅色
context.save();//調用save()方法保存上下文狀態
context.fillStyle = "#00ff00";//綠色
context.translate(100, 100);//把座標原點變換到(100, 100)
context.save();//調用save()方法保存上下文狀態
context.fillStyle = "#0000ff";//藍色
context.fillRect(0, 0, 100, 200); //因爲此時的座標原點已經變爲(100, 100),所以從(100, 100)點開始繪製藍色矩形
context.restore();//調用restore(),fillStyle變爲綠色,因爲座標位置的變換仍然起作用,所以矩形的起點座標是(110, 110)
context.fillRect(10, 10, 100, 200); //從 (110, 110)點開始繪製綠色矩形
context.restore();//再一次調用restore(),fillStyle變爲紅色,座標位置的變換不起作用了,所以矩形的起點座標是(0, 0)
context.fillRect(0, 0, 100, 200); //從(0,0)點開始繪製紅色矩形
//特別注意,save()方法保存的是對繪圖上下文的設置和變換,不會保存繪圖上下文的內容
}
</script>
</body>
</html>
###toDataURL方法##
toDataURL()方法可以導出在canvas元素上繪製的圖像,該方法接收一個參數:圖像的格式類型
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>toDataURL方法</title>
</head>
<body>
<canvas id="drawing" width="200" height="200">
當瀏覽器不支持canvas元素
</canvas>
<input type="button" value="導出圖片" id="export-btn" >
<p>點擊導出圖片,即可以將圖像變成圖片元素,右鍵點擊圖片元素即可以將它保存到本地</p>
<script type="text/javascript">
var drawing = document.getElementById("drawing"),
btn= document.getElementById("export-btn");
//確保瀏覽器支持canvas元素
if (drawing.getContext){
var context = drawing.getContext("2d");
//繪製紅色矩形
context.fillStyle = "#ff0000";
context.fillRect(10, 10, 50, 50);
//繪製半透明的藍色矩形
context.fillStyle = "rgba(0,0,255,0.5)";
context.fillRect(30, 30, 50, 50);
}
btn.onclick = function()
{
//取得圖像數據的URI
//var imgURI = drawing.toDataURL();
//這裏沒有指定圖像的類型格式,默認是png格式的圖像
var imgURI = drawing.toDataURL("image/png");
//顯示圖像
var image = document.createElement("img");
image.src = imgURI;
document.body.appendChild(image);
};
</script>
</body>
</html>
Geolocation(地理定位)
HTML5 Geolocation(地理定位)用於定位用戶的位置
###地理位置對象navigator.geolocation###
####getCurrentPosition() 方法###
可以使用 getCurrentPosition() 方法來獲得用戶的位置,該方法接收3個參數:請求成功函數、請求失敗函數和數據收集方式
請求成功函數:
- 經度 : coords.longitude
- 緯度 : coords.latitude
- 位置準確度 : coords.accuracy
- 海拔 : coords.altitude
- 海拔準確度 : coords.altitudeAcuracy
- 行進方向 : coords.heading 從正北開始以度計
- 地面速度 : coords.speed 以米/每秒計
- 時間戳 : new Date(position.timestamp) 響應的日期/時間
請求失敗函數:
失敗編號:code - 0(Unknown_error) : 不包括其他錯誤編號中的錯誤
- 1( Permission denied) : 用戶拒絕瀏覽器獲取位置信息
- 2(Position unavailable) : 嘗試獲取用戶信息,但失敗了
- 3(Timeout ) : 設置了timeout值,獲取位置超時了
數據收集方式:json的形式
- enableHighAcuracy : 更精確的查找,默認false
- timeout : 獲取位置允許最長時間,默認infinity
- maximumAge : 位置可以緩存的最大時間,默認0
Demo1:
<body>
<div id="demo"></div>
<input type="button" name="" value="點擊獲取位置" onclick="getLocation();">
<script type="text/javascript">
var x=document.getElementById("demo");
function getLocation(){
if (navigator.geolocation)
{
navigator.geolocation.getCurrentPosition(showPosition,showError);
}
else{
x.innerHTML="該瀏覽器不支持獲取地理位置。";
}
}
function showPosition(position)
{
x.innerHTML="緯度: " + position.coords.latitude +
"<br>經度: " + position.coords.longitude;
}
function showError(error)
{
switch(error.code)
{
case error.PERMISSION_DENIED:
// alert(error.code); 1
x.innerHTML="用戶拒絕對獲取地理位置的請求。"
break;
case error.POSITION_UNAVAILABLE:
// alert(error.code); 2
x.innerHTML="位置信息是不可用的。"
break;
case error.TIMEOUT:
// alert(error.code); 3
x.innerHTML="請求用戶地理位置超時。"
break;
case error.UNKNOWN_ERROR:
// alert(error.code); 0
x.innerHTML="未知錯誤。"
break;
}
}
</script>
</body>
Demo2:
<body>
<input type="button" value="請求" id="btn" /><br>
<textarea id="t1" style="width:400px; height:400px; border:1px #000 solid;">
</textarea>
<script type="text/javascript">
var oBtn = document.getElementById('btn');
var oTxt = document.getElementById('t1');
oBtn.onclick = function(){
navigator.geolocation.getCurrentPosition(function(position){
oTxt.value += '經度:' + position.coords.longitude+'\n';
oTxt.value += '緯度 :' + position.coords.latitude+'\n';
oTxt.value += '準確度 :' + position.coords.accuracy+'\n';
oTxt.value += '海拔 :' + position.coords.altitude+'\n';
oTxt.value += '海拔準確度 :' + position.coords.altitudeAcuracy+'\n';
oTxt.value += '行進方向 :' + position.coords.heading+'\n';
oTxt.value += '地面速度 :' + position.coords.speed+'\n';
oTxt.value += '時間戳:' + new Date(position.timestamp)+'\n';
},function(err){
//err.code--失敗所對應的編號
alert( err.code );
},{
enableHighAcuracy : true,
timeout : 5000,
maximumAge : 5000//設置緩存過期時間,提高性能
});
};
</script>
</body>
####watchPosition() 方法###
watchPosition() - 多次定位請求(像setInterval) ,返回用戶的當前位置,並繼續返回用戶移動時的更新位置(就像汽車上的 GPS)。移動設備有用,位置改變纔會觸發
配置參數:frequency 表示更新的頻率
clearWatch() - 停止 watchPosition() 方法(像clearInterval)
Demo:
<body>
<input type="button" value="請求" id="btn" /><br>
<textarea id="t1" style="width:400px; height:400px; border:1px #000 solid;">
</textarea>
<script type="text/javascript">
var oBtn = document.getElementById('btn');
var oTxt = document.getElementById('t1');
var timer = null;
oBtn.onclick = function(){
//多次請求
timer = navigator.geolocation.getCurrentPosition(function(position){
oTxt.value += '經度:' + position.coords.longitude+'\n';
oTxt.value += '緯度 :' + position.coords.latitude+'\n';
oTxt.value += '準確度 :' + position.coords.accuracy+'\n';
oTxt.value += '海拔 :' + position.coords.altitude+'\n';
oTxt.value += '海拔準確度 :' + position.coords.altitudeAcuracy+'\n';
oTxt.value += '行進方向 :' + position.coords.heading+'\n';
oTxt.value += '地面速度 :' + position.coords.speed+'\n';
oTxt.value += '時間戳:' + new Date(position.timestamp)+'\n';
},function(err){
//err.code--失敗所對應的編號
alert( err.code );
navigator.geolocation.clearWatch(timer);//關閉更新請求
},{
enableHighAcuracy : true,
timeout : 5000,
maximumAge : 5000,//設置緩存過期時間,提高性能
frequency : 1000
});
};
</script>
</body>
應用Demo:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>地理位置應用</title>
<style type="text/css">
#box{
width: 500px;
height: 500px;
border: 1px solid #000;
}
</style>
</head>
<body>
<input type="button" name="" value="獲取位置" id="btn">
<div id="box"></div>
<script src="http://api.map.baidu.com/api?v=1.4"></script>
<script type="text/javascript">
var oBtn = document.getElementById('btn');
oBtn.onclick = function(){
navigator.geolocation.getCurrentPosition(function(position){
//獲取經度和緯度
var y = position.coords.longitude;
var x = position.coords.latitude;
// 百度地圖API功能
var map = new BMap.Map("box");
// 初始化地圖,設置中心點座標和地圖級別
var pt = new BMap.Point(y, x);
map.centerAndZoom(pt, 14);
map.enableScrollWheelZoom();//開啓鼠標滾輪縮放
//創建圖片
var myIcon = new BMap.Icon("dut.jpg", new BMap.Size(200,200));
var marker2 = new BMap.Marker(pt,{icon:myIcon}); // 創建標註
map.addOverlay(marker2);// 將標註添加到地圖中
//創建信息窗口
var opts = {
width : 200, // 信息窗口寬度
height: 60, // 信息窗口高度
title : "大連理工大學"// 信息窗口標題
}
var infoWindow = new BMap.InfoWindow("高等學府", opts);//創建信息窗口對象
map.openInfoWindow(infoWindow,pt); //開啓信息窗口
});
};
</script>
</body>
</html>
WebSocket
###Socket.IO類庫實現WebSocket通信###
Socket.IO類庫可以接收所有與服務器端相連接的客戶端發送的消息,也可以向這些客戶端發送消息。該類庫的一個顯著特徵是在服務器端與瀏覽器之間提供一個共享接口。
###Socket.IO ##
客戶端:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>socket.io</title>
</head>
<body>
<script type="text/javascript" src="/socket.io/socket.io.js"></script>
<script type="text/javascript">
/*
/socket.io/socket.io.js由服務器端Socket.IO類庫自動提供,不需要在客戶端實際放置一個socket.io.js文件
這裏首先使用Socket.IO類庫中的io.connect方法連接服務器端Socket.IO服務器
*/
var socket = io.connect();//io.connect()返回一個Socket對象,代表一個用於與服務器端建立連接的客戶端socket端口對象
//客戶端與服務器建立連接後,當接收到服務端發送消息時,觸發客戶端socket端口對象的message事件
socket.on('message', function(data){//data爲服務器端發送的消息
console.log(data);
//使用客戶端socket端口對象的send方法向服務端發送一條消息
socket.send('消息已接收到。');
});
//當服務器端斷開連接時觸發客戶端socket端口對象的disconnect事件
socket.on('disconnect', function() {
console.log('服務器端斷開連接。');
});
/*
注意:客戶端的消息處理機制與服務器端的消息處理機制是完全一致的,因爲Socket.IO確保客戶端與服務器端共享相同的API
*/
</script>
</body>
</html>
服務器端:
var http = require('http');//引入HTTP模塊
var sio = require('socket.io');
var fs = require('fs');//引入fs模塊
var server = http.createServer(function(req, res) {
res.writeHead(200, { 'Content-type': 'text/html'});
res.end(fs.readFileSync('./index.html'));
});
server.listen(1337);
/*
server是一個HTTP服務器,監聽1337端口
使用Socket.IO類庫很簡單,創建一個Socket.IO服務器即可,但是該服務器依賴於一個已經創建的HTTP服務器。
在HTTP服務器運行之後,使用listen方法爲該HTTP服務器附加一個Socket.IO服務器。
*/
var socket = sio.listen(server);//這裏listen方法返回一個Socket.IO服務器
//這裏當客戶端與服務器建立連接時,觸發Socket.IO服務器的connection事件,可以通過監聽該事件並指定事件回調函數的方法
//指定當客戶端與服務器建立連接時所需執行的處理
socket.on('connection', function(socket){
console.log('客戶端建立連接。');
socket.send('你好。');
socket.on('message', function(msg) {//msg爲客戶端發送的消息
console.log('接收到一個消息: ',msg);
});
//當客戶端斷開連接時觸發socket端口對象的disconnect事件
socket.on('disconnect', function() {
console.log('客戶端斷開連接。');
});
});
/*
客戶端與服務器建立連接後,當接收到客戶端發送消息時,觸發socket端口對象的message事件
*/
###emit方法互相發送事件###
使用Socket.IO類庫時,服務器端與客戶端之間除了可以互相發送消息之外,也可以使用socket端口對象的emit方法互相發送事件
socket.emit(event,data,[callback])
- event參數值爲一個用於指定事件名的字符串
- data參數值代表該事件中攜帶的數據
- callback用於指定一個當對方確認收到數據時調用的回調函數
客戶端:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>socket.io</title>
</head>
<body>
<script type="text/javascript" src="/socket.io/socket.io.js"></script>
<script type="text/javascript">
var socket = io.connect();
//在接收到服務器端發送的news事件後,輸出相應的服務器端傳遞的信息
socket.on('news', function (data) {
console.log(data.hello);
//同時向服務器發送my other event事件,該事件攜帶一個對象,對象的my屬性值爲"data"字符串
socket.emit('my other event',{my:'data'});
});
</script>
</body>
</html>
服務器端:
var http = require('http');
var sio = require('socket.io');
var fs = require('fs');
var server = http.createServer(function(req, res) {
res.writeHead(200, { 'Content-type': 'text/html'});
res.end(fs.readFileSync('./index.html'));
});
server.listen(1337);
var socket = sio.listen(server);
//在客戶端與服務器端建立連接後,向客戶端發送一個news事件,事件中攜帶一個對象,該對象的hello屬性值爲"你好"
socket.on('connection', function(socket){
socket.emit('news', {hello: '你好。' });
//在接收到客戶端發送的my other event事件時,在控制檯輸出相應的信息
//data爲客戶端發送事件中攜帶的數據
socket.on('my other event', function (data) {
console.log('服務器端接收到數據:%j',data);
});
});