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(){
if(typeof(Worker) !== "undefined"){
if(typeof(w) == "undefined"){
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>
- 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
- 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
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);
};
worker.onerror = function(e){
console.log("Error:"+e.filename+"("+e.lineto+"):"+e.message);
}
worker.postMessage(data);
</script>
webworker2.js
self.onmessage = function(e){
var data = e.data;
data.sort(function(a,b){
return a-b;
});
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");
if (drawing.getContext){
var context = drawing.getContext("2d");
context.fillStyle = "#ff0000";
context.save();
context.fillStyle = "#00ff00";
context.translate(100, 100);
context.save();
context.fillStyle = "#0000ff";
context.fillRect(0, 0, 100, 200);
context.restore();
context.fillRect(10, 10, 100, 200);
context.restore();
context.fillRect(0, 0, 100, 200);
}
</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
- 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
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");
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()
{
var imgURI = drawing.toDataURL("image/png");
var image = document.createElement("img");
image.src = imgURI;
document.body.appendChild(image);
};
</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
- 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
Geolocation(地理定位)
HTML5 Geolocation(地理定位)用于定位用户的位置
地理位置对象navigator.geolocation
getCurrentPosition() 方法
可以使用 getCurrentPosition() 方法来获得用户的位置,该方法接收3个参数:请求成功函数、请求失败函数和数据收集方式
请求成功函数:
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:
x.innerHTML="用户拒绝对获取地理位置的请求。"
break;
case error.POSITION_UNAVAILABLE:
x.innerHTML="位置信息是不可用的。"
break;
case error.TIMEOUT:
x.innerHTML="请求用户地理位置超时。"
break;
case error.UNKNOWN_ERROR:
x.innerHTML="未知错误。"
break;
}
}
</script>
</body>
- 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
- 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
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){
alert( err.code );
},{
enableHighAcuracy : true,
timeout : 5000,
maximumAge : 5000
});
};
</script>
</body>
- 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
- 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
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){
alert( err.code );
navigator.geolocation.clearWatch(timer);
},{
enableHighAcuracy : true,
timeout : 5000,
maximumAge : 5000,
frequency : 1000
});
};
</script>
</body>
- 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
- 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
应用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;
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>
- 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
- 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
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">
var socket = io.connect();
socket.on('message', function(data){
console.log(data);
socket.send('消息已接收到。');
});
socket.on('disconnect', function() {
console.log('服务器端断开连接。');
});
</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
- 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
服务器端:
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);
socket.on('connection', function(socket){
console.log('客户端建立连接。');
socket.send('你好。');
socket.on('message', function(msg) {
console.log('接收到一个消息: ',msg);
});
socket.on('disconnect', function() {
console.log('客户端断开连接。');
});
});
- 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
- 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
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();
socket.on('news', function (data) {
console.log(data.hello);
socket.emit('my other event',{my:'data'});
});
</script>
</body>
</html>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
服务器端:
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);
socket.on('connection', function(socket){
socket.emit('news', {hello: '你好。' });
socket.on('my other event', function (data) {
console.log('服务器端接收到数据:%j',data);
});
});
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18