关于requestAnimationFrame

这两个礼拜做图文h5的时候看到同组大神一个关于渲染的神奇写法,用了requestAnimationFrame这个之前我从来没有使用过的属性。
看了hax的知乎live,其中也提到了浏览器的渲染问题,特意查了paper做个总结。

一个简单的需求

setBtnStatus这个函数用来设置按钮状态,这个按钮主要做submit作用,也包含对表单提交的检验,当用户输入没有经过校验时,按钮置灰,用户输入满足时,按钮可以点击(即用户点击之后可以提交表单)。
macsen110大神指导我写的一段代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
setBtnStatus: function () {
var that = this;
var addressSave = $('#addressSave');
if (that.isReady()) {
addressSave.removeAttr('disabled')
}else{
addressSave.attr('disabled',true)
}
that.reqId=requestAnimationFrame(that.setBtnStatus.bind(that));
},

var requestAnimationFrame = window.requestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function(callback){var id = window.setTimeout(callback, 1000 / 60);return id;};//IE9- 兼容

var cancelAnimationFrame = window.cancelAnimationFrame ||
window.mozCancelAnimationFrame ||
function(id){clearTimeout(id);};

调用requestAnimationFrameAPI可以让开发者写出更流畅的动画,以时间戳为单位(帧间隔),代码如下:

1
var handle = window.requestAnimationFrame(callback);

调用requestAnimationFrame这个函数需要一个回调函数作为参数,当下一个画面(frame)到来之前执行。这样实现在帧间隔给按钮平滑变色。

浏览器的反馈—用户已访问

浏览器有一个数据库专门用来存储用户已经访问过的URLs,有些浏览器可以决定如何展现用户已经点击过的链接,比如加深颜色等等,这些都是浏览器的默认行为。
IE和firefox的数据库操作行为是异步的,当操作结果还没返回时,表现为unvisited,返回时将触发页面的重绘操作,给这个链接添加访问过的标记。Chrome的数据库操作是同步的,只有全部操作完成才会渲染整个页面。
当链接中的地址被js改变时,IE中不会发生visited->unvisited的改变,这个改变会在firefox中发生。而在Chrome中只有改变链接并手动点击才会触发渲染,所以需要用js控制强制重绘。

1
2
3
4
5
6
7
<a href="http://www.guoningyan.com" id="link">过宁妍</a> 
<script>
var el = document.getElementById('link');
el.href = 'http://www.google.com';
el.style.color = 'red';
el.style.color = '';
</script>

浏览器的重绘(redraw)事件是可以被嗅探的

众所周知,帧渲染时间最佳为每秒60次(60fps,渲染一帧的时间为1000ms/60=16.7ms,这样人眼不会察觉到卡顿),requestAnimationFrame时间间隔为帧间隔,渲染时间可能会比16.7ms更短,而阴影shadow透明度transparency或者是大量复杂的样式改变会影响浏览器的渲染时间(painting step),导致渲染时间变长,这段时间可能经历了很多帧,所以通过requestAnimationFrame可以计算出画面切换之间的时间。
恶意的网站可以通过浏览器对网页的渲染时间差窃取用户的敏感数据,即用户访问的历史记录。

1
2
3
4
5
6
7
8
9
10
var lastTime = 0;
function loop(time) {
var delay = time – lastTime;
var fps = 1000/delay;
console.log(delay + 'ms' fps);
updateAnimation();
requestAnimationFrame(loop);
lastTime = time;
}
requestAnimationFrame(loop);

总结

requestAnimationFrame可以进行帧变化操作,可以使动画更流畅。阴影颜色等重绘耗时长的操作在对用户已访问的标记上是应该禁止使用的,浏览器的不同操作会导致用户浏览信息泄露。

参考资料
Browser_Timing_Attacks.pdf
张鑫旭的博客