深入理解HTML5定时器requestAnimationFrame的使用
前言
计时器一直是javascript动画的核心技术。而编写动画循环的关键是要知道延迟时间多长合适。一方面,循环间隔必须足够短,这样才能让不同的动画效果显得平滑流畅;另一方面,循环间隔还要足够长,这样才能确保浏览器有能力渲染产生的变化。
大多数电脑显示器的刷新频率是60Hz,大概相当于每秒钟重绘60次。大多数浏览器都会对重绘操作加以限制,不超过显示器的重绘频率,因为即使超过那个频率用户体验也不会有提升。因此,最平滑动画的最佳循环间隔是1000ms/60,约等于16.6ms。
而setTimeout和setInterval的问题是,它们都不精确。它们的内在运行机制决定了时间间隔参数实际上只是指定了把动画代码添加到浏览器UI线程队列中以等待执行的时间。如果队列前面已经加入了其他任务,那动画代码就要等前面的任务完成后再执行。
requestAnimationFrame采用系统时间间隔,保持最佳绘制效率,不会因为间隔时间过短,造成过度绘制,增加开销;也不会因为间隔时间太长,使用动画卡顿不流畅,让各种网页动画效果能够有一个统一的刷新机制,从而节省系统资源,提高系统性能,改善视觉效果。
特点
- requestAnimationFrame会把每一帧中的所有DOM操作集中起来,在一次重绘或回流中就完成,并且重绘或回流的时间间隔紧紧跟随浏览器的刷新频率。
- 在隐藏或不可见的元素中,requestAnimationFrame将不会进行重绘或回流,这当然就意味着更少的CPU、GPU和内存使用量。
- requestAnimationFrame是由浏览器专门为动画提供的API,在运行时浏览器会自动优化方法的调用,并且如果页面不是激活状态下的话,动画会自动暂停,有效节省了CPU开销。
用法
requestAnimationFrame方法将一个callback作为参数,回调函数会被传入一个参数,DOMHighResTimeStamp,指示当前被 requestAnimationFrame() 排序的回调函数被触发的时间。返回值是一个请求 ID,表示回调列表中唯一的标识。可以传这个值给 window.cancelAnimationFrame() 以取消回调函数。
requestID = window.requestAnimationFrame(callback);
利用这个api,可以将某些代码放到下一次重新渲染时执行,避免短时间内触发大量reflow。
例如页面滚动事件(scroll)的回调函数就很适合使用这个api, 将回调操作推迟到下一次重新渲染。但需要注意的是requestAnimationFrame 不管理回调函数,即在回调被执行前,多次调用带有同一回调函数的 requestAnimationFrame,会导致回调在同一帧中执行多次。最简单的,可以用节流函数来解决这个问题,也可以想办法让requestAnimationFrame的队列里同样的回调函数只有一个:
let scheduledAnimationFrame = false; document.body.onscroll = () => { if (scheduledAnimationFrame) return; scheduledAnimationFrame = true; window.requestAnimationFrame(() => { scheduledAnimationFrame = false; // do something }); };
当然,最佳的应用场景还是在帧动画里,可以大幅优化性能。
面试题
如何渲染几万条数据并不卡住界面
这道题考察了如何在不卡住页面的情况下渲染数据,也就是说不能一次性将几万条都渲染出来,而应该一次渲染部分 DOM,那么就可以通过 requestAnimationFrame 来每 16 ms 刷新一次。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <ul>控件</ul> <script> setTimeout(() => { // 插入十万条数据 const total = 100000 // 一次插入 20 条,如果觉得性能不好就减少 const once = 20 // 渲染数据总共需要几次 const loopCount = total / once let countOfRender = 0 let ul = document.querySelector("ul"); function add() { // 优化性能,插入不会造成回流 const fragment = document.createDocumentFragment(); for (let i = 0; i < once; i++) { const li = document.createElement("li"); li.innerText = Math.floor(Math.random() * total); fragment.appendChild(li); } ul.appendChild(fragment); countOfRender += 1; loop(); } function loop() { if (countOfRender < loopCount) { window.requestAnimationFrame(add); } } loop(); }, 0); </script> </body> </html>
兼容性
一些老的浏览器是不支持这个api的,为了也能使用这个api, 可以自定义这个方法,挂载到window下面:
(function() { var lastTime = 0; var vendors = ['webkit', 'moz']; 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) { 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); }; }());
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。
相关文章
HTML5探秘:用requestAnimationFrame优化Web动画
今天看到一段js中出现了requestAnimationFrame,一直没怎么关注html5的东西,今天又涨见识了,开始学习了2018-06-03html5新增的定时器requestAnimationFrame实现进度条功能
html5为什么新增一个requestAnimationFrame,他的出现是为了解决什么问题?带着这些问题跟随脚本之家小编一起学习吧2018-12-13- mp3播放器 网页音乐播放器 播放器 html5音乐播放器 html5音频播放器2018-10-09
- css3绘制黑色的磁带转动播放动画特效,通过鼠标放置四个角可以调节倾斜方向。有需要的朋友可以直接下载使用2018-05-09
- jQuery ui和css3制作一款精致圆形音乐播放器,内嵌背景图片非常漂亮,带播放进度条,可以控制播放暂停、调整音量等播放器常用的功能。 本段代码可以在各个网页使用,有需要2018-01-11
- 是一款基于HTML5+CSS3实现的音乐播放器,带有列表播放,音量控制,播放进度,播放时间以及播放模式,上一曲,下一曲等功能。本段代码适应于所有网页使用,有兴趣的朋友们可2015-07-13
- 高版本浏览器才支持的烟花播放效果,其中使用了高版本的jQuery插件库以及html5的canvas属性 鼠标在图片上任意单击,便会有烟花发射到此处并爆炸的效果2015-02-03
- 今天我们来分享一款HTML5+CSS3实现的旋转图片播放焦点图特效源码,该焦点图利用CSS3的属性实现了图片旋转的动画效果,既可以顺时针旋转,也可以逆时针旋转,并且图片旋转也2014-10-29
- 这是一款CSS3实现的带播放按钮鼠标悬停放大特效源码,在鼠标悬停于图片上时自动放大图片,该源码兼容主流浏览器2014-07-24
CSS3动画之利用requestAnimationFrame触发重新播放功能
这篇文章主要介绍了利用requestAnimationFrame重新播放(触发)CSS3动画,代码简单易懂,非常不错,具有一定的参考借鉴价值,需要的朋友可以参考下2019-09-11
最新评论