第25章 新兴的API
1.无论是使用setInterval()还是 setTimeout()制作动画都不十分精确,这两个实际上只是指定了把动画代码添加到浏览器 UI 线程队列中以等待执行的时间。
为了保证动画平滑,Mozilla提出了mozRequestAnimationFrame()方法,Chrome 提出了webkit- RequestAnimationFrame()方法,IE10+提出了msRequestAnimationFrame()方法。那么可以在 Firefox 4+、IE10+和 Chrome 中可以参考以下模式创建动画循环。
(function(){
function draw(timestamp){
//计算两次重绘的时间间隔
var drawStart = (timestamp || Date.now()),
diff = drawStart - startTime;
//使用 diff 确定下一步的绘制时间
//把 startTime 重写为这一次的绘制时间
startTime = drawStart;
//重绘 UI
requestAnimationFrame(draw);
}
var requestAnimationFrame = window.requestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.msRequestAnimationFrame,
startTime = window.mozAnimationStartTime || Date.now();
requestAnimationFrame(draw);
})();
2.Page Visibility API(页面可见性 API):让开发人员知道页面是否对用户可见,由以下三部分组成:
- document.hidden:表示页面是否隐藏的布尔值。页面隐藏包括页面在后台标签页中或者浏览器最小化。
- document.visibilityState:有下列 4 个可能状态的值:
- 页面在后台标签页中或浏览器最小化。
- 页面在前台标签页中。
- 实际的页面已经隐藏,但用户可以看到页面的预览(就像在 Windows 7 中,用户把鼠标移动到任务栏的图标上,就可以显示浏览器中当前页面的预览)。
- 页面在屏幕外执行预渲染处理。
- visibilitychange 事件:当文档从可见变为不可见或从不可见变为可见时,触发该事件。
-
//检查浏览器是否支持API function isHiddenSupported(){ return ("hidden" in document || "msHidden" in document || "webkitHidden" in document); } //在IE和Chrome 中侦听visibilitychange 事件,IE 中,叫 msvisibilitychange,在 Chrome 中叫 webkitvisibilitychange。 function handleVisibilityChange(){ var output = document.getElementById("output"), msg; if (document.hidden || document.msHidden || document.webkitHidden){ msg = "Page is now hidden. " + (new Date()) + "<br>"; } else { msg = "Page is now visible. " + (new Date()) + "<br>"; } output.innerHTML += msg; } //要为两个事件都指定事件处理程序 EventUtil.addHandler(document, "msvisibilitychange", handleVisibilityChange); EventUtil.addHandler(document, "webkitvisibilitychange", handleVisibilityChange);
3.Geolocation API的实现是 navigator.geolocation 对象,包含 3 个方法:
getCurrentPosition():触发请求用户共享地理定位信息的对话框,接收 3 个参数:成功回调函数、可选的失败回调函数和可选的选项对象。
- 成功回调函数,接收到一个 Position 对象,有两个属性:coords 和 timestamp
coords 包含下列与位置相关的信息:
latitude:以十进制度数表示的纬度。
longitude:以十进制度数表示的经度。
accuracy:经、纬度坐标的精度,以米为单位。 - 失败回调函数,接收到一个对象。有两个属性:message 和 code。
message :保存着给人看的文本消息,解释为什么会出错。
code :保存着一个数值,表示错误的类 型:用户拒绝共享(1)、位置无效(2)或者超时(3)。 - 选项对象,用于设定信息的类型。可以设置的选项有三个:
enableHighAccuracy :一个布尔值,表示必须尽可能使用最准确的位置信息。
timeout :以毫秒数表示的等待位置信息的最长时间。
maximumAge :上一次取得的坐标信息的有效时间,以毫秒表示,如果时间到则重新取得新坐标信息。
navigator.geolocation.getCurrentPosition(function(position){
drawMapCenteredAt(position.coords.latitude, positions.coords.longitude);
}, function(error){
console.log("Error code: " + error.code);
console.log("Error message: " + error.message);
}, {
enableHighAccuracy: true,
timeout: 5000,
maximumAge: 25000
});
watchPosition():接收的参数 与 getCurrentPosition()方 法 完 全 相 同 。与 定 时 调 用 getCurrentPosition()的效果相同。在第一次调用 watchPosition()方法后,会取得当前位置,执行成功回调或者错误回调。然后,watchPosition()就地等待系统发出位置已改变的信号(它不会自己轮询位置)。调用 watchPosition()会返回一个数值标识符,用于跟踪监控的操作。
clearWatch():取消了监控操作。
var watchId = navigator.geolocation.watchPosition(function(position){ drawMapCenteredAt(position.coords.latitude, positions.coords.longitude);
}, function(error){
console.log("Error code: " + error.code);
console.log("Error message: " + error.message);
});
clearWatch(watchId);
4.File API:可以读取文件内容,用于显示、处理和上传。与 HTML5 的拖放功能结合,很容易就能创造出拖放上传功能。
(1)在通过文件输入字段选择了一或多个文件时,files 集合中将包含一组 File 对象,每个 File 对象对应着一个文件。每个 File 对象都有下列只读属性:
- name:本地文件系统中的文件名。
- size:文件的字节大小。
- type:字符串,文件的 MIME 类型。
- lastModifiedDate:字符串,文件上一次被修改的时间。
-
//通过侦听 change 事件并读取 files 集合知道选择的每个文件的信息 var filesList = document.getElementById("files-list"); EventUtil.addHandler(filesList, "change", function(event){ var files = EventUtil.getTarget(event).files, i = 0, len = files.length; while (i < len){ console.log(files[i].name + " (" + files[i].type + ", " + files[i].size +" bytes) "); i++; } });
(2)FileReader 类型实现一种异步文件读取机制,提供了如下 几个方法:
- readAsText(file,encoding):以纯文本形式读取文件,将读取到的文本保存在 result 属 性中。第二个参数用于指定编码类型,是可选的。
- readAsDataURL(file):读取文件并将文件以数据 URI 的形式保存在 result 属性中。
- readAsBinaryString(file):读取文件并将一个字符串保存在result属性中,字符串中的每个字符表示一字节。 、
- readAsArrayBuffer(file):读取文件并将一个包含文件内容的 ArrayBuffer 保存在result 属性中。
还提供了三个最有用的事件:
- progress事件:每过 50ms 左右,就会触发,通过事件对象可以属):lengthComputable、loaded 和 total。
- error 事件:由于种种原因无法读取文件,就会触发 ,触发时,相关的信息将保存到 FileReader 的 error 属性中,保存一个对象,该对象只有一个属性 code,即错误码。 这个错误码是 1 表示未找到文件,是 2 表示安全性错误,是 3 表示读取中断,是 4 表示文件不可读,是 5 表示编码错误。
- load 事件:文件成功加载后会触发
-
var filesList = document.getElementById("files-list"); EventUtil.addHandler(filesList, "change", function(event){ var info = "", output = document.getElementById("output"), progress = document.getElementById("progress"), files = EventUtil.getTarget(event).files, type = "default", reader = new FileReader(); if (/image/.test(files[0].type)){ reader.readAsDataURL(files[0]); type = "image"; } else { reader.readAsText(files[0]); type = "text"; } reader.onerror = function(){ output.innerHTML = "Could not read file, error code is " + reader.error.code; }; reader.onprogress = function(event){ if (event.lengthComputable){ progress.innerHTML = event.loaded + "/" + event.total; } }; reader.onload = function(){ var html = ""; 20 switch(type){ case "image": html = "<img src=\"" + reader.result + "\">"; break; case "text": html = reader.result; break; } output.innerHTML = html; }; });
(3)slice()方法读取部分内容,接收两个参数:起始字节及要读取的字节数,返回一 个 Blob 的实例,Blob 是 File 类型的父类型。
//跨浏览器的blobSlice函数
function blobSlice(blob, startByte, length){
if (blob.slice){
return blob.slice(startByte, length);
} else if (blob.webkitSlice){
return blob.webkitSlice(startByte, length);
} else if (blob.mozSlice){
return blob.mozSlice(startByte, length);
} else {
return null;
}
}
(4) 使用对象 URL (被称为 blob URL,指的是引用保存在 File 或 Blob 中数据的 URL)可以不必把文件内容读取到 JavaScript 中而直接使用文件内容。
//创建对象 URL函数,消除不同浏览器的命名差异,返回值是一个字符串,指向一块内存的地址
function createObjectURL(blob){
if (window.URL){
return window.URL.createObjectURL(blob);
} else if (window.webkitURL){
return window.webkitURL.createObjectURL(blob);
} else {
return null;
}
}
/*直接把对象 URL 放在<img>标签中,省去了把数据先读到 JavaScript 中的麻烦。
<img> 标签会找到相应的内存地址,直接读取数据并将图像显示在页面中。*/
var filesList = document.getElementById("files-list");
EventUtil.addHandler(filesList, "change", function(event){
var info = "",
output = document.getElementById("output"),
progress = document.getElementById("progress"),
files = EventUtil.getTarget(event).files,
reader = new FileReader(),
url = createObjectURL(files[0]);
if (url){
if (/image/.test(files[0].type)){
output.innerHTML = "<img src=\"" + url + "\">";
} else {
output.innerHTML = "Not an image.";
}
} else {
output.innerHTML = "Your browser doesn't support object URLs.";
}
});
//最好在不需要某个对象 URL 时,就马上手工释放其占用的内存,消除不同浏览器的命名差异
function revokeObjectURL(url){
if (window.URL){
window.URL.revokeObjectURL(url);
} else if (window.webkitURL){
window.webkitURL.revokeObjectURL(url);
}
}
(5)结合使用 HTML5 拖放 API 和文件 API,将放置到页面中自定义的放置目标中的文件信息显示出来
var droptarget = document.getElementById( "droptarget");
function handleEvent(event){
var info = "",
output = document.getElementById("output"),
files, i, len;
EventUtil.preventDefault(event);//取消拖放的默认行为
if (event.type == "drop"){
//通过event.dataTransfer.files读取文件信息
files = event.dataTransfer.files;
i = 0;
len = files.length;
while (i < len){
info += files[i].name + " (" + files[i].type + ", " + files[i].size+" bytes)<br>";
i++;
}
output.innerHTML = info;
}
}
EventUtil.addHandler(droptarget, "dragenter", handleEvent); EventUtil.addHandler(droptarget, "dragover", handleEvent); EventUtil.addHandler(droptarget, "drop", handleEvent);
(6)通过 XHR 直接把文件上传到服务器
var droptarget = document.getElementById("droptarget");
function handleEvent(event){
var info = "",
output = document.getElementById("output"),
data, xhr,
files, i, len;
EventUtil.preventDefault(event);
if (event.type == "drop"){
data = new FormData();//为了以表单提交的方式来上传文件,创建一个 FormData 对象
files = event.dataTransfer.files;
i = 0;
len = files.length;
while (i < len){
data.append("file" + i, files[i]);//调用 append()方法并传入相应的 File 对象作为参数
i++;
}
xhr = new XMLHttpRequest();
xhr.open("post", "FileAPIExample06Upload.php", true);
xhr.onreadystatechange = function(){
if (xhr.readyState == 4){
alert(xhr.responseText);
}
};
xhr.send(data);//发送给服务器
}
}
EventUtil.addHandler(droptarget, "dragenter", handleEvent); EventUtil.addHandler(droptarget, "dragover", handleEvent); EventUtil.addHandler(droptarget, "drop", handleEvent);
5.Web Timing:给出了页面加载和渲染过程的很多信息,对性能优化非常有价值,核心是 window.performance 对象,有两个属性:
- performance.navigation 属性也是一个对象,包含着与页面导航有关的多个属性。
- performance.timing 属性也是一个对象,但这个对象的属性都是时间戳(从软件纪元开始经过的毫秒数),不同的事件会产生不同的时间值。
6.Web Workers:可以运行异步 JavaScript 代码,避免阻塞用户界面。
-
在执行复杂计算和数据处理的时候,非常有用;要不然,这些任务轻则会占用很长时间,重则会导致用户无法与页面交互。
(1)创建一个新的 Web Worker:实例化 Worker 对象并传入要执行的 JavaScript 文件名。
var worker = new Worker("stufftodo.js");
(2)使用 postMessage()方法给 Worker 传递消息,可以随便传递任何形式的对象数据,Worker 接收到消息才会实际执行文件中的代码。
worker.postMessage(“start! ");
worker.postMessage({
type: "command",
message: "start! "
});
(3)Worker 是通过 message 和 error 事件与页面通信的,来自 Worker 的数据保存在 event.data 中。
worker.onmessage = function(event){
var data = event.data;
//对数据进行处理
}
(4)建议在使用 Web Workers 时,始终都要使用 onerror 事件处理程序,即使这个函数除了把错误记录到日志中什么也不做都可以。否则,Worker 就会在发生错误时,悄无声息地失败了。 error 事件对象中包含三个属性:filename(发生错误的文件名)、 lineno(代码行号) 和 message(完整的错误消息)。
worker.onerror = function(event){
console.log("ERROR: " + event.filename + " (" + event.lineno + "): " + event.message);
};
(5)Web Worker 中的全局对象是 worker 对象本身,this 和 self 引用的都是 worker 对象,不能访问 DOM,也无法通过任何方式影响页面的外观 。当页面在 worker 对象上调用 postMessage()时,数据会以异步方式被传递给 worker,进而触发 worker 中的 message 事件。为了处理来自页面的数据,同样也需要创建一个 onmessage 事件处理程序。
//在页面中
var data = [23,4,7,9,2,14,6,651,87,41,7798,24],
worker = new Worker("WebWorkerExample01.js");
worker.onmessage = function(event){
var data = event.data;
//对排序后的数组进行操作
};
//将数组发送给 worker 排序
worker.postMessage(data);
//---------------------------------
//Web Worker 内部的代码
self.onmessage = function(event){
var data = event.data;
//默认的 sort()方法只比较字符串
data.sort(function(a, b){
return a – b;
});
self.postMessage(data);
};
//在 Worker 内部,调用 close()方法也可以停止工作
self.close();