requestAnimationFrame()详解

快乐打工仔 分类:实例代码

关于requestAnimationFrame()基本用法和概念可以参阅requestAnimationFrame()用法一章节。

下面再从更为底层的层面来对此方法进行一下介绍,感兴趣的朋友可以做一下参考。

回调函数列表:

每一个document对象都有一个动画帧请求回调函数列表;该列表中存储着由<handle, callback>组成的集合。

(1).callback:它就是传递给requestAnimationFrame()方法的回调函数;此函数没有返回值,参数(可以省略)是一个时间值(该值由浏览器传入,从1970年1月1日到当前所经过的毫秒数)。

(2).handle:是一个整数,唯一地标识了元组在列表中的位置,cancelAnimationFrame()可以通过它来停止动画。

浏览器UI线程:

此线程用于执行javascript或者界面的更新。

基于一个简单的队列系统,任务会被保存到队列中直到进程空闲。

一旦空闲,队列中的下一个任务就被重新提取出来并运行。

这些任务要么是运行javascript代码,要么执行UI更新,包括重绘和重排。

页面是否激活:

当页面被最小化或者被切换成非当前选项卡时,页面为不可见或者说没有激活,浏览器会触发visibilitychange事件,并设置document.hidden属性为true;切换到显示状态时,页面为可见,也同样触发一个 visibilitychange事件,设置document.hidden属性为false。

requestAnimationFrame:

此方法可以将回调函数追加到动画帧请求回调函数列表的末尾。

当执行requestAnimationFrame(callback)时候,不会立刻调用callback函数,只是将其放入队列。

每个回调函数都有一个布尔标识cancelled,该标识初始值为false,并且对外不可见;当浏览器再执行列表中的回调函数的时候,判断每个元组的callback的cancelled,如果为false,则执行callback。

cancelAnimationFrame:

方法能够取消一个动画帧更新的请求。

当调用cancelAnimationFrame(handle)时,浏览器会设置该handle指向的回调函数的cancelled为true。

无论该回调函数是否在动画帧请求回调函数列表中,它的cancelled都会被设置为true。

如果该handle没有指向任何回调函数,则调用cancelAnimationFrame 不会发生任何事情。

处理模型:

当页面可见并且动画帧请求回调函数列表不为空时,浏览器会定期奖这些回调函数加入到UI线程的队列中。

此处使用伪代码来说明“采样所有动画”任务的执行步骤:

var list = {};
var browsingContexts = 浏览器顶级上下文及其下属的浏览器上下文;
 
for (var browsingContext in browsingContexts) {
 
  //var time = 从1970年1月1日到当前所经过的毫秒数;
  var time = DOMHighResTimeStamp 
 
  var d = browsingContext的active document; //即当前浏览器上下文中的Document节点
  //如果该active document可见
  if (d.hidden !== true) {
    //拷贝active document的动画帧请求回调函数列表到list中,并清空该列表
    var doclist = d的动画帧请求回调函数列表
    doclist.appendTo(list);
    clear(doclist);
  }
 
  //遍历动画帧请求回调函数列表的元组中的回调函数
  for (var callback in list) {
    if (callback.cancelled !== true) {
      try {
        //每个browsingContext都有一个对应WindowProxy,WindowProxy对象会将callback指向active document关联的window。
        //传入时间值time
        callback.call(window, time);
      }
      //忽略异常
      catch (e) {}
    }
  }
}

看一段代码实例:

var id = null;
function func(time) {
  console.log("犀牛前端部落");
  window.cancelAnimationFrame(id);
  id = window.requestAnimationFrame(func);
}
func();

我们的目的是是函数只执行一次,但是结果却是会无限循环下去,下面做一下分析:

(1).func()调用之后,当执行到window.cancelAnimationFrame(id),id是null,所以它没有取消任何动画请求帧回调函数的执行。

(2).id = window.requestAnimationFrame(func),又将一个新的回调函数追加到队列。

(3).所以func又将会被加入UI线程的队列中,func()又将被执行;虽然window.cancelAnimationFrame(id)的参数这次不会null,但是与此id对应的回调函数已经执行了,代码修改如下:

var id = null;
function func(time) {
  console.log("犀牛前端部落");
  id = window.requestAnimationFrame(func);
  window.cancelAnimationFrame(id);
}
func();

只要将代码的顺序对调一下即可。

requestAnimationFrame()详解,这样的场景在实际项目中还是用的比较多的,关于requestAnimationFrame()详解就介绍到这了。

requestAnimationFrame()详解属于前端实例代码,有关更多实例代码大家可以查看

回复

我来回复
  • 暂无回复内容