JS学习23(几个有用的API)

requestAnimationFrame()

早期动画循环

JS的动画很长时间以来都是使用计时器setInterval来达成的。就像这样:

(function(){
    function updateAnimations(){
        doAnimation1();
        doAnimation2();
    }
    setInterval(updateAnimations, 100);
})()

但是这样使用是有问题的,最大的问题就是时间间隔的问题。不能太长,否则看起来就会卡卡的,也不能太短,更新速度超过了屏幕的刷新速度会造成丢帧。
而且浏览器的计时器其实精度是有限的,精度最高的chrome为4ms。且在页面没有显示在屏幕上的时候,大多数浏览器会对计时器的运行频率做出限制。
这样的动画绘制机制就造成了绘制下一帧动画的时机我们并不能准确掌握。最好的结果应该是正好在屏幕刷新的那一刻绘制下一帧,也就是动画绘制的速度与电脑屏幕刷新速度一致。然而由于上述问题,这个最优的结果很难达成。于是Mozilla就提出了 requestAnimationFrame()方法。

requestAnimationFrame()

function updateProgress(){   
    var div = document.getElementById("status");  
    div.style.width = (parseInt(div.style.width, 10) + 5) + "%";
    if (div.style.width != "100%"){        
        mozRequestAnimationFrame(updateProgress);    
    } 
}
mozRequestAnimationFrame(updateProgress);

这个方法接收一个函数,这个函数会在下次屏幕刷新时调用,把动画写在这里就保证了动画下一帧的绘制是在屏幕刷新时同步绘制的。这个方法只会在下次屏幕刷新时调用一次,这样设计的目的是可以让开发人员方便的决定是否继续动画。想继续就继续调用这个函数就好。
有个问题就是这个动画的API还不成熟,各个厂家的实现都还带自己的前缀,使用下面这个方法可以统一接口。

(function() {
    var lastTime = 0;
    var vendors = ['ms', 'moz', 'webkit', 'o'];
    for (var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
        window.requestAnimationFrame = window[vendors[x] + 'RequestAnimationFrame'];
        window.cancelAnimationFrame = window[vendors[x] + 'CancelAnimationFrame'] || window[vendors[x] + 'CancelRequestAnimationFrame'];
    }
    if (!window.requestAnimationFrame)
        window.requestAnimationFrame = function(callback, element) {
        var currTime = new Date().getTime();
        var timeToCall = Math.max(0, 16 - (currTime - lastTime));
        var id = window.setTimeout(function() {
            callback(currTime + timeToCall);
        }, timeToCall);
        lastTime = currTime + timeToCall;
        return id;
    };
    if (!window.cancelAnimationFrame)
        window.cancelAnimationFrame = function(id) {
        clearTimeout(id);
    };
}());

Page Visibility API

用户是否真的正在与页面交互是我们需要知道的。如果页面最小化了或隐藏起来了,那么有些功能是可以停下来的。API由3部分组成:

  • document.hidden:表示页面是否隐藏的布尔值
  • document.visibilityState:页面在后台,页面在前台,页面隐藏但正在被预览,页面在屏幕外执行预渲染处理
  • visibilitychange:在可见和不可见转换时触发的事件
function handleVisibilityChange(){
    var output = document.getElementById("myDiv"),
        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);

同样,这个API暂时需要浏览器前缀。

Geolocation API

这个API让JS可以通过浏览器来获取用户的地理位置。当然,这是需要获得用户同意的。这个API在浏览器中的实现是navigator.geolocation。这个对象有3个方法:

getCurrentPosition()

这个方法接收3个参数,成功的回调函数,可选的失败的回调函数,可选的选项对象。
成功的回调函数会接收到一个Position对象参数,它有两个属性:coords是代表位置信息的对象(latitude、longitude、accuracy、altitude、altitudeAccuracy、heading、speed),timestamp是时间戳。
失败的回调函数会接收到一个包含错误信息的对象,包含message和code。里面有失败的原因和代码。
第三个参数是一个选项对象。用来配置获取信息的方式,可以设置的值有3个,enableHighAccuracy(布尔值,是否尽可能准确的获取信息),timeout(等待位置信息返回的最长时间),maximumAge(上一次取得坐标信息的有效时长,如果没到这个时长时再次尝试获取,会使用上次的位置信息)。

navigator.geolocation.getCurrentPosition(
    function(position){
        alert(position.coords.latitude+"####"+positions.coords.longitude);
    },
    function(error){
        console.log("Error code: " + error.code);
        console.log("Error message: " + error.message); },
    {
        enableHighAccuracy: false,
        timeout: 5000,
        maximumAge: 25000
    });

watchPosition()

这个方法可以跟踪用户的位置,他的使用和上面的方法一样。只不过在第一次获取位置信息后这个方法会等待系统发出位置改变的信号再次调用。这个方法会返回一个ID用来跟踪,基于这个ID可以使用clearWatch()方法清除监控。

var watchId = navigator.geolocation.watchPosition(
    function(position){
        alert(position.coords.latitude+"####"+positions.coords.longitude);
    }, 
    function(error){   
        console.log("Error code: " + error.code);
        console.log("Error message: " + error.message);
    });
navigator.geolocation.clearWatch(watchId); 

IE9+、Firefox 3.5+、Opera 10.6+、Safari 5+、ChromeiOS版 Safari、Android 版 WebKit
File API
========
不能直接访问用户计算机中的文件一直都是Web应用的一大障碍。File API旨在为Web开发人员提供一种安全的方式来访问用户的文件。
支持File API的有IE10+、Firefox 4+、Safari 5.0.5+、Opera 11.1+、 Chrome。
HTML5在DOM中为文件输入元素添加了一个files集合,在通过文件输入字段选择了一个或多个文件时,files集合中将包含一组File对象。 每个File对象对应一个文件,每个都有下列只读属性:name、size、type( MIME类型)、lastModifiedDate。
FileReader


这个类型实现的是一种异步文件读取的机制,它提供了以下几种方法:

  • readAsText(file,encoding):以纯文本形式读取文件,将读取到的文件保存到result属性中
  • readAsDataURL(file):读取文件并将文件以数据URI的形式保存在result属性中
  • readAsBinaryString(file)
  • readAsArrayBuffer(file)

由于是异步读取,所以有几个事件:progress、error、load
progress事件和Ajax的一样,有lengthComputable、loaded、total属性。

var filesList = document.getElementById("files-list");
EventUtil.addHandler(filesList, "change", function(event){
    var info = "",
        output = document.getElementById("myDiv"),
        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 = "";
        switch(type){
            case "image":
                html = "<img src=\"" + reader.result + "\">";
                break;
            case "text":
                html = reader.result;
                break;
        }
        output.innerHTML = html;
    };
});

读取部分内容

有时候我们只想读取部分文件,File对象有个slice()方法。

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;   
    }
}
var blob = blobSlice(files[0], 0, 32);
reader = new FileReader();
reader.readAsDataURL(files[0]);

对象URL

也叫blob URL,指的是引用保存在File或Blob中数据的URL。使用对象URL的好处是不必把文件读到JS中就可以直接使用文件内容。只需要在使用文件内容的地方提供对象URL即可。要创建对象URL可以使用createObjectURL方法。各浏览器不同,可以hack一下。

function createObjectURL(blob){
    if (window.URL){
        return window.URL.createObjectURL(blob);
    } else if (window.webkitURL){
        return window.webkitURL.createObjectURL(blob);
    } else {
        return null;
    }
}

这个函数返回一个URL字符串,指向一块内存的地址。所以直接当URL用就好。这个和readAsDataURL(file)返回的东西是一样的。
如果不需要相应数据了,就放开内存,但是只要有代码在引用对象URL,内存就不会释放。可以把URL传给revokeObjectURL()来释放。

读取拖放的文件

在页面上创建自定义的放置目标,使用HTML5的拖放API和文件API可以做出令人瞩目的用户界面。

var droptarget = document.getElementById( "droptarget");
function handleEvent(event){
    var info = "",
        output = document.getElementById("myDiv"),
        files,
        i,
        len;
    EventUtil.preventDefault(event);
    if (event.type == "drop"){
        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);

使用XHR上传文件

File可以访问文件内容,所以利用这一点可以通过XHR直接把文件上传到服务器。
使用FormData类型,append一个File类型,再把FormData传递给XHR的send方法,就可以吧文件传给服务器啦。

if (event.type == "drop"){
  data = new FormData();
    files = event.dataTransfer.files;
    i = 0;
    len = files.length;
    while (i < len){
        data.append("file" + i, files[i]);
        i++;
    }
    xhr = new XMLHttpRequest();
    xhr.open("post", "hahaha.jsp", true);
    xhr.onreadystatechange = function(){
        if (xhr.readyState == 4){
            alert(xhr.responseText);
        }
    };
    xhr.send(data);
}

Web计时

为了度量页面性能的API,核心是window.performance对象
performance.navigation


包含与页面导航有关的多个属性
redirectCount:页面加载前重定向数
type:页面导航类型(第一次加载,重载,前进/后退)

performance.timing

这个对象的属性都是时间戳,储存着页面载入时各个阶段的时间。
navigationStart
unloadEventStart
unloadEventEnd
redirectStart
redirectEnd
fetchStart
domainLookupStart
domainLookupEnd
connectStart
connectEnd
secureConnectionStart
requestStart
responseStart
responseEnd
domLoading
domInteractive
domContentLoadedEventStart
domContentLoadedEventEnd
domComplete
loadEventStart
loadEventEnd
Web Workers
===
JS正在像越来越复杂的方向发展,有时长时间运行JS是很常见的,但这会导致冻结用户界面,Web Workers通过使JS在后台运行解决了这个问题。
使用Worker


实例化Worker并传入要执行的JS文件名就创建了一个新的Web Worker。

var worker = new Worker("stufftodo.js"); 

这时浏览器会开始下载JS文件,但是并不会运行。
当我们给Worker实例发送消息时,Worker才会开始执行 。发送的数据可以是任何可以序列化的值,包括对象。

worker.postMessage({     
    type: "command",      
    message: "start! " 
}); 

在Worker中会接到这个消息并开始执行这个JS文件。
Worker也会向页面发回消息,包括数据和报错。在本页面中,使用两个事件来接收它们。

worker.onmessage = function(event){
    var data = event.data;
}
worker.onerror = function(event){
    console.log("ERROR: " + event.filename + " (" + event.lineno + "): " +
        event.message);
};

错误处理一定要有,否则Worker会失败的悄无声息。
想要终止Worker:

worker.terminate();

Worker全局作用域

Web Worker中所执行的代码完全在另一个作用域中,与当前代码不共享作用域。在其自己的作用域中有自己的全局对象和方法。不过就目前来说,Worker中的代码不能访问DOM,也不能影响页面外观,它能做的就是处理页面发送给它消息中的数据并发回给页面。
在Woeker中,全局对象是Worker对象本身,也就是说self,this这两个引用都指向Worker。Worker是一个最小化的环境,很多window对象的方法这里都没有,不过为了便于处理数据,在Worker中有这些对象和属性可用:

  • 最小化的navigator对象,包括:onLine、appName、appVersion、userAgent、platform
  • 只读的location
  • setTimeout()、setInterval()、clearTimeout()、clearInterval()
  • XMLHttpRequest构造函数

当在页面上通过postMessage将数据传递给Worker的时候,数据是以异步形式传递给Worker的。在Worker中,同样使用onmessage事件来接收页面发来的数据。处理完还是使用postMessage将数据发回给页面:

self.onmessage = function(event){
    var data = event.data;
    self.postMessage("Data from Worker:"+data.type+"&"+data.message);
};

在Worker内部,可以使用close()方法来终止脚本的运行。

包含其他脚本

在Worker中,因为不能访问DOM的原因,自然也不能创建script元素来添加并运行脚本。不过没关系,使用importScripts()方法可以引入外部代码,这个方法接收一个或多个指向JS文件的URL。每个加载过程都是异步执行的,在要引入的所有脚本下载完成后,这些脚本会在Woeker的作用域中按照参数的先后顺序执行。

importScripts("file1.js", "file2.js");
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,132评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,802评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,566评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,858评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,867评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,695评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,064评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,705评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,915评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,677评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,796评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,432评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,041评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,992评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,223评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,185评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,535评论 2 343

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,598评论 18 139
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,472评论 25 707
  • 新增的API 1.语义: 能够让你更恰当地描述你的内容是什么。 2.连通性: 能够让你和服务器之间通过创新的新技术...
    红鲤鱼不理绿鲤鱼阅读 6,878评论 0 5
  • “我知道早睡早起对身体好,但是,,,,”“我知道要养成读书的习惯,但是,,,,"我知道,但是,,,,"我们身边经常...
    S小姐说东西阅读 323评论 0 0
  • 1898年,人智学创始人施泰纳提出了他的"基本社会学法则":在文明的起初,人类努力创造一种社会结构,这种结构牺牲个...
    韧性十足的牛皮糖阅读 273评论 0 0