如果你简历说你精通个js动效,但事实又不了解requestAnimationFrame,,那么你的面试结果是很难看的。
实现动画效果的方法比较多,Javascript 中可以通过定时器 setTimeout 来实现,css3 可以使用 transition 和 animation 来实现,html5 中的 canvas 也可以实现。除此之外,html5 还提供一个专门用于请求动画的API,那就是 requestAnimationFrame,顾名思义就是请求动画帧。
requestAnimationFrame其用法跟setTimeout差不多,与setTimeout相比,最大的优势是由浏览器来决定函数的执行时机。形象一点的解释就是:告诉浏览器说 “我这里有一个函数要执行,你有空了帮忙执行一下”,然后浏览器相对比较空闲的时候就给执行了。
用法一:动画
requestAnimationFrame api本身的设计就是用来解决js动画的性能问题。那么,为什么requestAnimationFrame做动画性能会更好呢?主要原因在于requestAnimationFrame更加智能,它并非加快执行速度,而是适当时候降帧,防止并解决丢帧问题。当它发现无法维持60fps的频率时,它会把频率降低到30fps来保持帧数的稳定。也就是说如果上一次raf的回调执行时间过长,那么触发下一次raf回调的时间就会缩短,反之亦然,这也是为什么说由浏览器来决定执行时机性能会更好。
(function animloop(){
window.requestAnimFrame(animloop);
render();
})();
用法二: 函数节流
在高频率事件中,为了防止16ms内发生多次函数执行,使用raf可保证16ms内只触发一次,这既能保证流畅性也能更好的节省函数执行的开销。16ms内函数执行多次没有意义,因为显示器16ms刷新一次,多次执行并不会在界面上有任何显示。
举个例子:
var $box = $('#J_box'),
$point = $box.find('i');
$box.on('mousemove',function(e){
requestAnimationFrame(function(){
$point.css({
top : e.pageY,
left : e.pageX
})
})
})用法三:CPU节能
requestAnimationFrame的另一个特性是:如果页面不是激活状态下的话,函数会自动暂停,有效节省了CPU开销。在移动端,如果页面中有自动播放的轮播图、倒计时或使用setTimeout/setInterval来执行任务的定时器。那么当app进到后台或是锁屏后,webviewcorethread仍然持续占用CPU,导致耗电。而使用raf可以很简单的解决此类问题。
PS:复杂情况下,可配合window.onblur & window.focus、document.onvisibilitychange、document.onpause & document.onresume等相关的方法进行实现,但这篇文章里不做深入探讨。
(function(){
var timer;
var $txt = $('#J_num2'),
num = 0;
function play(){
timer = setTimeout(function(){
//使用raf实现非激活状态下不运行
requestAnimationFrame(function(){
stop();
next();
});
},1000)
}
function stop(){
clearTimeout(timer)
}
function next(){
$txt.text(num++);
play();
}
play();
})();
用法四:优雅降级
由于兼容性问题,需要降级对接口进行封装,优先使用高级特性,再根据浏览器不同情况进行回退,直到只能使用settimeout。参考[GitHub](github.com/darius/requ…)
用法五:分帧初始化
都知道,requestAnimationFrame的执行时间约为16.7ms,即为一帧。那么可以使用它将页面初始化的函数进行打散到每一帧里,这样可以在初始化时降低CPU及内存开销。
很多页面,初始化加载时,CPU都会有很明显的波动,就是因为大量的操作都集中到了一点上。
举个例子:
页面中有4个模块,A、B、C、D,在页面加载时进行实例化,一般的写法类似于:
$(function(){
new A();
new B();
new C();
new D();
})而使用raf可将每个模块分别初始化,即每个模块都有16ms的初始化时间
$(function(){
var lazyLoadList = [A,B,C,D];
$.each(lazyloadList, function(index, module){
window.requestAnimationFrame(function(){new module()});
});
})用法六:异步化
requestAnimationFrame实际是一种异步化的操作,曾经setTimeout(function(){},0)一度成为解决了很多前端疑难杂症的法宝。而现在,可以用它来代替。
参考资料
[1]developer.mozilla.org/zh-CN/docs/…
[2]简书 糕糕AA www.jianshu.com/p/f6d933670…z