javascript--25新兴的API
25.1requestAnimationFrame()很长时间以来,计时器和循环间隔一直都是JavaScript动画的最核心技术。虽然CSS变换及动画为Web开发人员提供了实现动画的简单手段,但JavaScript动画开发领域的状况这些年来并没有大的变化。Firefox4最早为JavaScript动画添加了一个新API,即mozRequestAnimationFrame()。这个方法会告诉浏览器:有一个动画开始了。进而浏览器就可以确定重绘的最佳方式。
25.1.1早期动画循环在JavaScript中创建动画的典型方式,就是使用setInterval()方法来控制所有动画。以下是一个使用setInterval()的基本动画循环:(function(){functionupdateAnimations(){doAnimation1();doAnimation2();//其他动画}setInterval(updateAnimations,100);})();为了创建一个小型动画库,updateAnimations()方法就得不断循环地运行每个动画,并相应地改变不同元素的状态(例如,同时显示一个新闻跑马灯和一个进度条)。如果没有动画需要更新,这个方法可以退出,什么也不用做,甚至可以把动画循环停下来,等待下一次需要更新的动画。编写这种动画循环的关键是要知道延迟时间多长合适。一方面,循环间隔必须足够短,这样才能让不同的动画效果显得更平滑流畅;另一方面,循环间隔还要足够长,这样才能确保浏览器有能力渲染产生的变化。大多数电脑显示器的刷新频率是60Hz,大概相当于每秒钟重绘60次。大多数浏览器都会对重绘操作加以限制,不超过显示器的重绘频率,因为即使超过那个频率用户体验也不会有提升。因此,最平滑动画的最佳循环间隔是1000ms/60,约等于17ms。以这个循环间隔重绘的动画是最平滑的,因为这个速度最接近浏览器的最高限速。为了适应17ms的循环间隔,多重动画可能需要加以节制,以便不会完成得太快。
25.1.2循环间隔的问题知道什么时候绘制下一帧是保证动画平滑的关键。然而,直至最近,开发人员都没有办法确保浏览器按时绘制下一帧。随着<canvas>元素越来越流行,新的基于浏览器的游戏也开始崭露头脚,面对不十分精确的setInterval()和setTimeout(),开发人员一筹莫展。浏览器使用的计时器的精度进一步恶化了问题。具体地说,浏览器使用的计时器并非精确到毫秒级别。以下是几个浏览器的计时器精度。IE8及更早版本的计时器精度为15.625ms。IE9及更晚版本的计时器精度为4ms。Firefox和Safari的计时器精度大约为10ms。Chrome的计时器精度为4ms。IE9之前版本的计时器精度为15.625ms,因此介于0和15之间的任何值只能是0和15。IE9把计时器精度提高到了4ms,但这个精度对于动画来说仍然不够明确。Chrome的计时器精度为4ms,而Firefox和Safari的精度是10ms。更为复杂的是,浏览器都开始限制后台标签页或不活动标签页的计时器。因此,即使你优化了循环间隔,结果仍然只能接近你想要的效果。
25.1.3mozRequestAnimationFrameMozilla的RobertO’Callahan认识到了这个问题,提出了一个非常独特的方案。他指出,CSS变换和动画的优势在于浏览器知道动画什么时候开始,因此会计算出正确的循环间隔,在恰当的时候刷新UI。而对于JavaScript动画,浏览器无从知晓什么时候开始。因此他的方案就是创造一个新方法mozRequestAnimationFrame(),通过它告诉浏览器某些JavaScript代码将要执行动画。这样浏览器可以在运行某些代码后进行适当的优化。
mozRequestAnimationFrame()方法接收一个参数,即在重绘屏幕前调用的一个函数。这个函数负责改变下一次重绘时的DOM样式。为了创建动画循环,可以像以前使用setTimeout()一样,把多个对mozRequestAnimationFrame()的调用连缀起来。比如:functionupdateProgress(){vardiv=document.getElementById("status");div.style.width=(parseInt(div.style.width,10)+5)+"%";if(div.style.left!="100%"){mozRequestAnimationFrame(updateProgress);}}mozRequestAnimationFrame(updateProgress);因为mozRequestAnimationFrame()只运行一次传入的函数,因此在需要再次修改UI从而生成动画时,需要再次手工调用它。同样,也需要同时考虑什么时候停止动画。这样就能得到非常平滑流畅的动画。目前来看,mozRequestAnimationFrame()解决了浏览器不知道JavaScript动画什么时候开始、不知道最佳循环间隔时间的问题,但不知道代码到底什么时候执行的问题呢?同样的方案也可以解决这个问题。我们传递的mozRequestAnimationFrame()函数也会接收一个参数,它是一个时间码(从1970年1月1日起至今的毫秒数),表示下一次重绘的实际发生时间。注意,这一点很重要:mozRequestAnimationFrame()会根据这个时间码设定将来的某个时刻进行重绘,而根据这个时间码,你也能知道那个时刻是什么时间。然后,再优化动画效果就有了依据。要知道距离上一次重绘已经过去了多长时间,可以查询mozAnimationStartTime,其中包含上一次重绘的时间码。用传入回调函数的时间码减去这个时间码,就能计算出在屏幕上重绘下一组变化之前要经过多长时间。使用这个值的典型方式如下:functiondraw(timestamp){//计算两次重绘的时间间隔vardiff=timestamp-startTime;//使用diff确定下一步的绘制时间//把startTime重写为这一次的绘制时间startTime=timestamp;//重绘UImozRequestAnimationFrame(draw);}varstartTime=mozAnimationStartTime;mozRequestAnimationFrame(draw);这里的关键是第一次读取mozAnimationStartTime的值,必须在传递给mozRequestAnimationFrame()的回调函数外面进行。如果是在回调函数内部读取mozAnimationStartTime,得到的值与传入的时间码是相等的。
25.1.4webkitRequestAnimationFrame与msRequestAnimationFrame基于mozRequestAnimationFrame(),Chrome和IE10+也都给出了自己的实现,分别叫webkit-RequestAnimationFrame()和msRequestAnimationFrame()。这两个版本与Mozilla的版本有两个方面的微小差异。首先,不会给回调函数传递时间码,因此你无法知道下一次重绘将发生在什么时间。其次,Chrome又增加了第二个可选的参数,即将要发生变化的DOM元素。知道了重绘将发生在页面中哪个特定元素的区域内,就可以将重绘限定在该区域中。既然没有下一次重绘的时间码,那Chrome和IE没有提供mozAnimationStartTime的实现也就很容易理解了——没有那个时间码,实现这个属性也没有什么用。不过,Chrome倒是又提供了另一个方法webkitCancelAnimationFrame(),用于取消之前计划执行的重绘操作。假如你不需要知道精确的时间差,那么可以在Firefox4+、IE10+和Chrome中可以参考以下模式创建动画循环。(function(){functiondraw(timestamp){//计算两次重绘的时间间隔vardrawStart=(timestamp||Date.now()),diff=drawStart-startTime;//使用diff确定下一步的绘制时间//把startTime重写为这一次的绘制时间startTime=drawStart;//重绘UIrequestAnimationFrame(draw);}varrequestAnimationFrame=window.requestAnimationFrame||window.mozRequestAnimationFrame||window.webkitRequestAnimationFrame||window.msRequestAnimationFrame,startTime=window.mozAnimationStartTime||Date.now();requestAnimationFrame(draw);})();以上模式利用已有的功能创建了一个动画循环,大致计算出了两次重绘的时间间隔。在Firefox中,计算时间间隔使用的是既有的时间码,而在Chrome和IE中,则使用不十分精确的Date对象。这个模式可以大致体现出两次重绘的时间间隔,但不会告诉你在Chrome和IE中的时间间隔到底是多少。不过,大致知道时间间隔总比一点儿概念也没有好些。因为首先检测的是标准函数名,其次才是特定于浏览器的版本,所以这个动画循环在将来也能够使用。目前,W3C已经着手起草requestAnimationFrame()API,而且作为WebPerformanceGroup的一部分,Mozilla和Google正共同参与该标准草案的制定工作。
25.2PageVisibilityAPI不知道用户是不是正在与页面交互,这是困扰广大Web开发人员的一个主要问题。如果页面最小化了或者隐藏在了其他标签页后面,那么有些功能是可以停下来的,比如轮询服务器或者某些动画效果。而PageVisibilityAPI(页面可见性API)就是为了让开发人员知道页面是否对用户可见而推出的。这个API本身非常简单,由以下三部分组成。document.hidden:表示页面是否隐藏的布尔值。页面隐藏包括页面在后台标签页中或者浏览器最小化。document.visibilityState:表示下列4个可能状态的值。页面在后台标签页中或浏览器最小化。页面在前台标签页中。实际的页面已经隐藏,但用户可以看到页面的预览(就像在Windows7中,用户把鼠标移动到任务栏的图标上,就可以显示浏览器中当前页面的预览)。页面在屏幕外执行预渲染处理。visibilitychange事件:当文档从可见变为不可见或从不可见变为可见时,触发该事件。在编写本书时,只有IE10和Chrome支持PageVisibilityAPI。IE的版本是在每个属性或事件前面加上ms前缀,而Chrome则是加上webkit前缀。因此document.hidden在IE的实现中就是document.msHidden,而在Chrome的实现中则是document.webkitHidden。检查浏览器是否支持这个API的最佳方式如下:functionisHiddenSupported(){returntypeof(document.hidden||document.msHidden||document.webkitHidden)!="undefined";}PageVisibilityAPIExample01.htm类似地,使用同样的模式可以检测页面是否隐藏:if(document.hidden||document.msHidden||document.webKitHidden){//页面隐藏了}else{//页面未隐藏}PageVisibilityAPIExample01.htm注意,以上代码在不支持该API的浏览器中会提示页面未隐藏。这是PageVisibilityAPI有意设计的结果,目的是为了向后兼容。为了在页面从可见变为不可见或从不可见变为可见时收到通知,可以侦听visibilitychange事件。在IE中,这个事件叫msvisibilitychange,而在Chrome中这个事件叫webkitvisibility-change。为了在两个浏览器中都能侦听到该事件,可以像下面的例子一样,为每个事件都指定相同的事件处理程序:
functionhandleVisibilityChange(){varoutput=document.getElementById("output"),msg;if(document.hidden||document.msHidden||document.webkitHidden){msg="Pageisnowhidden."+(newDate())+"<br>";}else{msg="Pageisnowvisible."+(newDate())+"<br>";}output.innerHTML+=msg;}//要为两个事件都指定事件处理程序EventUtil.addHandler(document,"msvisibilitychange",handleVisibilityChange);EventUtil.addHandler(document,"webkitvisibilitychange",handleVisibilityChange);PageVisibilityAPIExample01.htm以上代码同时适用于IE和Chrome。而且,API的这一部分已经相对稳定,因此在实际的Web开发中也可以使用以上代码。关于这一API的实现,差异最大的是document.visibilityState属性。IE10PR2的document.msVisibilityState是一个表示如下4种状态的数字值。(1)document.MS_PAGE_HIDDEN(0)(2)document.MS_PAGE_VISIBLE(1)(3)document.MS_PAGE_PREVIEW(2)(4)document.MS_PAGE_PRERENDER(3)在Chrome中,document.webkitVisibilityState可能是下列3个字符串值:(1)"hidden"(2)"visible"(3)"prerender"Chrome并没有给每个状态定义对应的常量,但最终的实现很可能会使用常量。由于存在以上差异,所以建议大家先不要完全依赖带前缀的document.visibilityState,最好只使用document.hidden属性。
25.3GeolocationAPI地理定位(geolocation)是最令人兴奋,而且得到了广泛支持的一个新API。通过这套API,JavaScript代码能够访问到用户的当前位置信息。当然,访问之前必须得到用户的明确许可,即同意在页面中共享其位置信息。如果页面尝试访问地理定位信息,浏览器就会显示一个对话框,请求用户许可共享其位置信息。图25-1展示了Chrome中的这样一个对话框。图25-1GeolocationAPI在浏览器中的实现是navigator.geolocation对象,这个对象包含3个方法。第一个方法是getCurrentPosition(),调用这个方法就会触发请求用户共享地理定位信息的对话框
这个方法接收3个参数:成功回调函数、可选的失败回调函数和可选的选项对象。其中,成功回调函数会接收到一个Position对象参数,该对象有两个属性:coords和timestamp。而coords对象中将包含下列与位置相关的信息。latitude:以十进制度数表示的纬度。longitude:以十进制度数表示的经度。accuracy:经、纬度坐标的精度,以米为单位。有些浏览器还可能会在coords对象中提供如下属性。altitude:以米为单位的海拔高度,如果没有相关数据则值为null。altitudeAccuracy:海拔高度的精度,以米为单位,数值越大越不精确。heading:指南针的方向,0°表示正北,值为NaN表示没有检测到数据。speed:速度,即每秒移动多少米,如果没有相关数据则值为null。在实际开发中,latitude和longitude是大多数Web应用最常用到的属性。例如,以下代码将在地图上绘制用户的位置:navigator.geolocation.getCurrentPosition(function(position){drawMapCenteredAt(position.coords.latitude,positions.coords.longitude);});以上介绍的是成功回调函数。getCurrentPosition()的第二个参数,即失败回调函数,在被调用的时候也会接收到一个参数。这个参数是一个对象,包含两个属性:message和code。其中,message属性中保存着给人看的文本消息,解释为什么会出错,而code属性中保存着一个数值,表示错误的类型:用户拒绝共享(1)、位置无效(2)或者超时(3)。实际开发中,大多数Web应用只会将错误消息保存到日志文件中,而不一定会因此修改用户界面。例如:navigator.geolocation.getCurrentPosition(function(position){drawMapCenteredAt(position.coords.latitude,positions.coords.longitude);},function(error){console.log("Errorcode:"+error.code);console.log("Errormessage:"+error.message);});getCurrentPosition()的第三个参数是一个选项对象,用于设定信息的类型。可以设置的选项有三个:enableHighAccuracy是一个布尔值,表示必须尽可能使用最准确的位置信息;timeout是以毫秒数表示的等待位置信息的最长时间;maximumAge表示上一次取得的坐标信息的有效时间,以毫秒表示,如果时间到则重新取得新坐标信息。例如:navigator.geolocation.getCurrentPosition(function(position){drawMapCenteredAt(position.coords.latitude,positions.coords.longitude);},function(error){console.log("Errorcode:"+error.code);console.log("Errormessage:"+error.message);},{enableHighAccuracy:true,timeout:5000,maximumAge:25000});
这三个选项都是可选的,可以单独设置,也可以与其他选项一起设置。除非确实需要非常精确的信息,否则建议保持enableHighAccuracy的false值(默认值)。将这个选项设置为true需要更长的时候,而且在移动设备上还会导致消耗更多电量。类似地,如果不需要频繁更新用户的位置信息,那么可以将maximumAge设置为Infinity,从而始终都使用上一次的坐标信息。如果你希望跟踪用户的位置,那么可以使用另一个方法watchPosition()。这个方法接收的参数与getCurrentPosition()方法完全相同。实际上,watchPosition()与定时调用getCurrentPosition()的效果相同。在第一次调用watchPosition()方法后,会取得当前位置,执行成功回调或者错误回调。然后,watchPosition()就地等待系统发出位置已改变的信号(它不会自己轮询位置)。调用watchPosition()会返回一个数值标识符,用于跟踪监控的操作。基于这个返回值可以取消监控操作,只要将其传递给clearWatch()方法即可(与使用setTimeout()和clearTimeout()类似)。例如:varwatchId=navigator.geolocation.watchPosition(function(position){drawMapCenteredAt(position.coords.latitude,positions.coords.longitude);},function(error){console.log("Errorcode:"+error.code);console.log("Errormessage:"+error.message);});clearWatch(watchId);以上例子调用了watchPosition()方法,将返回的标识符保存在了watchId中。然后,又将watchId传给了clearWatch(),取消了监控操作。支持地理定位的浏览器有IE9+、Firefox3.5+、Opera10.6+、Safari5+、Chrome、iOS版Safari、Android版WebKit。要了解使用地理定位的更多精彩范例,请访问http://html5demos.com/geo。
25.4FileAPI不能直接访问用户计算机中的文件,一直都是Web应用开发中的一大障碍。2000年以前,处理文件的唯一方式就是在表单中加入<inputtype="file">字段,仅此而已。FileAPI(文件API)的宗旨是为Web开发人员提供一种安全的方式,以便在客户端访问用户计算机中的文件,并更好地对这些文件执行操作。支持FileAPI的浏览器有IE10+、Firefox4+、Safari5.0.5+、Opera11.1+和Chrome。FileAPI在表单中的文件输入字段的基础上,又添加了一些直接访问文件信息的接口。HTML5在DOM中为文件输入元素添加了一个files集合。在通过文件输入字段选择了一或多个文件时,files集合中将包含一组File对象,每个File对象对应着一个文件。每个File对象都有下列只读属性。name:本地文件系统中的文件名。size:文件的字节大小。type:字符串,文件的MIME类型。lastModifiedDate:字符串,文件上一次被修改的时间(只有Chrome实现了这个属性)。举个例子,通过侦听change事件并读取files集合就可以知道选择的每个文件的信息:varfilesList=document.getElementById("files-list");EventUtil.addHandler(filesList,"change",function(event){varfiles=EventUtil.getTarget(event).files,i=0,len=files.length;while(i";break;case"text":html=reader.result;break;}output.innerHTML=html;};});FileAPIExample02.htm这个例子读取了表单字段中选择的文件,并将其内容显示在了页面中。如果文件有MIMI类型,表示文件是图像,因此在load事件中就把它保存为数据URI,并在页面中将这幅图像显示出来。如果文件不是图像,则以字符串形式读取文件内容,然后如实在页面中显示读取到的内容。这里使用了progress事件来跟踪读取了多少字节的数据,而error事件则用于监控发生的错误。如果想中断读取过程,可以调用abort()方法,这样就会触发abort事件。在触发load、error或abort事件后,会触发另一个事件loadend。loadend事件发生就意味着已经读取完整个文件,或者读取时发生了错误,或者读取过程被中断。实现FileAPI的所有浏览器都支持readAsText()和readAsDataURL()方法。但IE10PR2并未实现readAsBinaryString()和readAsArrayBuffer()方法。
25.4.2读取部分内容有时候,我们只想读取文件的一部分而不是全部内容。为此,File对象还支持一个slice()方法,这个方法在Firefox中的实现叫mozSlice(),在Chrome中的实现叫webkitSlice(),Safari的5.1及之前版本不支持这个方法。slice()方法接收两个参数:起始字节及要读取的字节数。这个方法返回一个Blob的实例,Blob是File类型的父类型。下面是一个通用的函数,可以在不同实现中使用slice()方法:functionblobSlice(blob,startByte,length){if(blob.slice){returnblob.slice(startByte,length);}elseif(blob.webkitSlice){returnblob.webkitSlice(startByte,length);}elseif(blob.mozSlice){returnblob.mozSlice(startByte,length);}else{returnnull;}}FileAPIExample03.htmBlob类型有一个size属性和一个type属性,而且它也支持slice()方法,以便进一步切割数据。通过FileReader也可以从Blob中读取数据。下面这个例子只读取文件的32B内容。varfilesList=document.getElementById("files-list");EventUtil.addHandler(filesList,"change",function(event){varinfo="",output=document.getElementById("output"),progress=document.getElementById("progress"),files=EventUtil.getTarget(event).files,reader=newFileReader(),blob=blobSlice(files[0],0,32);if(blob){reader.readAsText(blob);reader.onerror=function(){output.innerHTML="Couldnotreadfile,errorcodeis"+reader.error.code;};reader.onload=function(){output.innerHTML=reader.result;};}else{alert("Yourbrowserdoesn'tsupportslice().");}});FileAPIExample03.htm只读取文件的一部分可以节省时间,非常适合只关注数据中某个特定部分(如文件头部)的情况。
25.4.3对象URL对象URL也被称为blobURL,指的是引用保存在File或Blob中数据的URL。使用对象URL的好处是可以不必把文件内容读取到JavaScript中而直接使用文件内容。为此,只要在需要文件内容的地方提供对象URL即可。要创建对象URL,可以使用window.URL.createObjectURL()方法,并传入File或Blob对象。这个方法在Chrome中的实现叫window.webkitURL.createObjectURL(),因此可以通过如下函数来消除命名的差异:functioncreateObjectURL(blob){if(window.URL){returnwindow.URL.createObjectURL(blob);}elseif(window.webkitURL){returnwindow.webkitURL.createObjectURL(blob);}else{returnnull;}}FileAPIExample04.htm这个函数的返回值是一个字符串,指向一块内存的地址。因为这个字符串是URL,所以在DOM中也能使用。例如,以下代码可以在页面中显示一个图像文件:varfilesList=document.getElementById("files-list");EventUtil.addHandler(filesList,"change",function(event){varinfo="",output=document.getElementById("output"),progress=document.getElementById("progress"),files=EventUtil.getTarget(event).files,reader=newFileReader(),url=createObjectURL(files[0]);if(url){if(/image/.test(files[0].type)){output.innerHTML="<imgsrc="%5C%22%22"url>";}else{output.innerHTML="Notanimage.";}}else{output.innerHTML="Yourbrowserdoesn'tsupportobjectURLs.";}});FileAPIExample04.htm直接把对象URL放在<img>标签中,就省去了把数据先读到JavaScript中的麻烦。另一方面,<img>标签则会找到相应的内存地址,直接读取数据并将图像显示在页面中。如果不再需要相应的数据,最好释放它占用的内容。但只要有代码在引用对象URL,内存就不会释放。要手工释放内存,可以把对象URL传给window.URL.revokeOjbectURL()(在Chrome中是window.webkitURL.revokeObjectURL())。要兼容这两种方法的实现,可以使用以下函数:
functionrevokeObjectURL(url){if(window.URL){window.URL.revokeObjectURL(url);}elseif(window.webkitURL){window.webkitURL.revokeObjectURL(url);}}页面卸载时会自动释放对象URL占用的内存。不过,为了确保尽可能少地占用内存,最好在不需要某个对象URL时,就马上手工释放其占用的内存。支持对象URL的浏览器有IE10+、Firefox4和Chrome。
25.4.4读取拖放的文件围绕读取文件信息,结合使用HTML5拖放API和文件API,能够创造出令人瞩目的用户界面:在页面上创建了自定义的放置目标之后,你可以从桌面上把文件拖放到该目标。与拖放一张图片或者一个链接类似,从桌面上把文件拖放到浏览器中也会触发drop事件。而且可以在event.dataTransfer.files中读取到被放置的文件,当然此时它是一个File对象,与通过文件输入字段取得的File对象一样。下面这个例子会将放置到页面中自定义的放置目标中的文件信息显示出来:vardroptarget=document.getElementById("droptarget");functionhandleEvent(event){varinfo="",output=document.getElementById("output"),files,i,len;EventUtil.preventDefault(event);if(event.type=="drop"){files=event.dataTransfer.files;i=0;len=files.length;while(i";i++;}output.innerHTML=info;}}EventUtil.addHandler(droptarget,"dragenter",handleEvent);EventUtil.addHandler(droptarget,"dragover",handleEvent);EventUtil.addHandler(droptarget,"drop",handleEvent);FileAPIExample05.htm与之前展示的拖放示例一样,这里也必须取消dragenter、dragover和drop的默认行为。在drop事件中,可以通过event.dataTransfer.files读取文件信息。还有一种利用这个功能的流行做法,即结合XMLHttpRequest和拖放文件来实现上传。
25.4.5使用XHR上传文件通过FileAPI能够访问到文件内容,利用这一点就可以通过XHR直接把文件上传到服务器。当然啦,把文件内容放到send()方法中,再通过POST请求,的确很容易就能实现上传。但这样做传递的是文件内容,因而服务器端必须收集提交的内容,然后再把它们保存到另一个文件中。其实,更好的做法是以表单提交的方式来上传文件。这样使用FormData类型就很容易做到了(第21章介绍过FormData)。首先,要创建一个FormData对象,通过它调用append()方法并传入相应的File对象作为参数。然后,再把FormData对象传递给XHR的send()方法,结果与通过表单上传一模一样。vardroptarget=document.getElementById("droptarget");functionhandleEvent(event){varinfo="",output=document.getElementById("output"),data,xhr,files,i,len;EventUtil.preventDefault(event);if(event.type=="drop"){data=newFormData();files=event.dataTransfer.files;i=0;len=files.length;while(i元素,那是不是就不能向Worker中添加其他脚本了呢?不是,Worker的全局作用域提供这个功能,即我们可以调用importScripts()方法。这个方法接收一个或多个指向JavaScript文件的URL。每个加载过程都是异步进行的,因此所有脚本加载并执行之后,importScripts()才会执行。例如://WebWorker内部的代码importScripts("file1.js","file2.js");即使file2.js先于file1.js下载完,执行的时候仍然会按照先后顺序执行。而且,这些脚本是在Worker的全局作用域中执行,如果脚本中包含与页面有关的JavaScript代码,那么脚本可能无法正确运行。请记住,Worker中的脚本一般都具有特殊的用途,不会像页面中的脚本那么功能宽泛。
25.6.4WebWorkers的未来WebWorkers规范还在继续制定和改进之中。本节所讨论的Worker目前被称为“专用Worker”(dedicatedworker),因为它们是专门为某个特定的页面服务的,不能在页面间共享。该规范的另外一个概念是“共享Worker”(sharedworker),这种Worker可以在浏览器的多个标签中打开的同一个页面间共享。虽然Safari5、Chrome和Opera10.6都实现了共享Worker,但由于该规范尚未完稿,因此很可能还会有变动。另外,关于在Worker内部能访问什么不能访问什么,到如今仍然争论不休。有人认为Worker应该像页面一样能够访问任意数据,不光是XHR,还有localStorage、sessionStorage、IndexedDB、WebSockets、Server-SendEvents等。好像支持这个观点的人更多一些,因此未来的Worker全局作用域很可能会有更大的空间。
25.7小结
与HTML5同时兴起的是另外一批JavaScriptAPI。从技术规范角度讲,这批API不属于HTML5,但从整体上可以称它们为HTML5JavaScriptAPI。这些API的标准有不少虽然还在制定当中,但已经得到了浏览器的广泛支持,因此本章重点讨论了它们。requestAnimationFrame():是一个着眼于优化JavaScript动画的API,能够在动画运行期间发出信号。通过这种机制,浏览器就能够自动优化屏幕重绘操作。PageVisibilityAPI:让开发人员知道用户什么时候正在看着页面,而什么时候页面是隐藏的。GeolocationAPI:在得到许可的情况下,可以确定用户所在的位置。在移动Web应用中,这个API非常重要而且常用。FileAPI:可以读取文件内容,用于显示、处理和上传。与HTML5的拖放功能结合,很容易就能创造出拖放上传功能。WebTiming:给出了页面加载和渲染过程的很多信息,对性能优化非常有价值。WebWorkers:可以运行异步JavaScript代码,避免阻塞用户界面。在执行复杂计算和数据处理的时候,这个API非常有用;要不然,这些任务轻则会占用很长时间,重则会导致用户无法与页面交互。
</canvas>