requestAnimationFrame 刨根问底

开头引用一段 Google Developer Rendering Performance:

当屏幕正在发生视觉变化时,您希望在适合浏览器的时间执行您的工作,也就是正好在帧的开头。保证 JavaScript 在帧开始时运行的唯一方式是使用 requestAnimationFrame。

框架或示例可能使用 setTimeout 或 setInterval 来执行动画之类的视觉变化,但这种做法的问题是,回调将在帧中的某个时点运行,可能刚好在末尾,而这可能经常会使我们丢失帧,导致卡顿。(事实上,jQuery 目前的默认 animate 行为是使用 setTimeout!)

什么是渲染帧?

这得从显示器的刷新频率说起,目前主流的LCD液晶显示器,刷新频率规格大多在60Hz。

60Hz什么概念呢,就是大约每16.66毫秒刷新一次屏幕,叫做一个渲染帧。

你现在看到的屏幕,就是用这种高速在不断的做一次又一次的渲染。

在这个渲染帧到下个渲染帧期间,加上JS线程和GUI线程之间的通信等损耗,你的代码必须在10ms左右完成才能保证不掉帧。

是不是看高速世界看得有些懵?

没关系,我们换一个老式CRT显示器

CRT显示器是靠电子束激发屏幕内表面的荧光粉来显示图像的,由于荧光粉被点亮后很快会熄灭,所以电子枪必须循环地不断激发这些点,电子束在屏幕上一行紧接一行从左到右的逐行扫描。

现在我们来放慢它的速度,假装它扫描整个屏幕要用10秒,够长了吧~现在再来看刚刚的操作。

我们一个动画小球在屏幕左边,接着我们执行了一行代码,它右移了一个像素。但是它没有马上呈现在画面中,而是等到逐行扫描过后,才出现。(还得自己画gif  〒▽〒)

同理,回到现代设备,60Hz的刷新频率也是如此处理。

这么短的时间,代码能执行完吗?

回答这个问题之前,我们来看看现代的CPU(拿i3举例)

1GHz是多少次脉冲呢?大概是1秒10亿次~吧~

1GHz的CPU如果只做加法运算,进行一次完整的加法运算需要读2个数据,8个周期+运算16个周期+写入6个周期总共需要30个时钟周期(注意,不同CPU需要的周期是不同的,这里只是举列),大概也还能1秒做个100万次。

所以这里还得再看看我们的代码算法难易度

如何查看我们的代码执行时间?

打开我们Chrome的开发者工具,选择JavaScript Profiler就可以看见了(可以用下面的示例代码跑一跑,感受一下)

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title></title>
    <style type="text/css">
      .ball {
        width: 50px;
        height: 50px;
        position: absolute;
        top: 0px;
        left: 40%;
        background: orange;
        border-radius: 50%;
      }
    </style>
  </head>
  <body>
    <div class="ball"></div>
    <script>
      function move() {
        document.querySelector('.ball').style.top = 100 * Math.random() + 100 + 'px';
        requestAnimationFrame(move);
        // 可以打开setTimeout注释对比性能
        // setTimeout(() => { move() })
      }
      move()
    </script>
  </body>
</html>

优势与兼容性

requestAnimationFrame还有以下两个优势:

  • CPU节能:使用setTimeout实现的动画,当页面被隐藏或最小化时,setTimeout 仍然在后台执行动画任务,由于此时页面处于不可见或不可用状态,刷新动画是没有意义的,完全是浪费CPU资源。而requestAnimationFrame则完全不同,当页面处理未激活的状态下,该页面的屏幕刷新任务也会被系统暂停,因此跟着系统步伐走的requestAnimationFrame也会停止渲染,当页面被激活时,动画就从上次停留的地方继续执行,有效节省了CPU开销。

  • 函数节流:在高频率事件(resize,scroll等)中,为了防止在一个刷新间隔内发生多次函数执行,使用requestAnimationFrame可保证每个刷新间隔内,函数只被执行一次,这样既能保证流畅性,也能更好的节省函数执行的开销。一个刷新间隔内函数执行多次时没有意义的,因为显示器每16.7ms刷新一次,多次绘制并不会在屏幕上体现出来。

从图中也看出除了IE外的其他浏览器兼容性还是很不错的,大家也可以看看这篇优雅降级方案:

https://github.com/darius/requestAnimationFrame

总结

在写相关动画效果的时候,因当格外注意动画的代码,尽量在10ms内执行完成。与setTimeout相比,requestAnimationFrame最大的优势是由系统来决定回调函数的执行时机,在上个渲染帧结束后开始执行代码,规避出现掉帧的情况。

对技术感兴趣的同学可以Github互相关注一波~

https://github.com/cmyh100

--完--

(0)

相关推荐

  • 浏览器的 Event Loop

    前端技术优选 昨天 以下文章来源于掘金开发者社区 ,作者小蘑菇哥哥 掘金开发者社区 掘金,一个帮助开发者成长的技术社区 前端技术优选 为你精选前端领域优质技术博文,欢迎关注. 57篇原创内容 公众号 ...

  • 自学转行成前端工程师,三面拿下字节跳动offer

    我是一名前端工程师,通俗地说,就是一只程序猿.虽然大学专业也的确是IT相关专业,但求学期间划水四年,侥幸未挂科并顺利毕业,找的第一份工作是软件公司的销售类职位,想着多少也算专业相关愉快入职,却无论如何 ...

  • JavaScript的BOM相关内容

    BOM(浏览器对象模型) BOM(浏览器对象模型)简介 BOM是Browser Object Model的简写,即是浏览器对象模型. BOM由一系列对象组成,是访问.控制.修改客户端浏览器的属性的方法 ...

  • 刨根问底︱要不要给孩子吃钙片?

    可以给孩子吃钙片吗?这是很多妈妈关心的问题. 这里告诉大家,对于孩子来说,一定要坚持一个原则:只要能食补就不要药补. 原因有两个: 第一.利于培养孩子从小喝牛奶的良好习惯: 第二.钙片中的钙是浓缩的, ...

  • 刨根问底︱吃腰子能壮阳吗?

    被称为"腰花"的猪肾,是很多人心中的"壮阳"食物.在吃啥补啥的想法之下,这种食物就变得受欢迎起来.不少男性朋友喜欢在烧烤时来上几串"腰子", ...

  • 刨根问底︱减肥后身体健康状况可能变更差?

    最新的调查显示,减肥后,人的身体健康状况,不一定比没有减肥的人更好,相反,可能造成死亡率增高. 这是因为,一些人采用了不科学的减肥方法,或者减掉的不是多余的脂肪,而是水或瘦体组织(如蛋白质等),这都是 ...

  • 刨根问底︱吃什么能补B族维生素?

    要学会吃粗粮.粗粮在一天的主食中大约要占到三分之一左右. 要吃大豆.中国营养学会推荐一般成人每天吃大豆25克及以上,25克大豆相当于72克北豆腐,140克南豆腐,365毫升豆浆,175克内酯豆腐.55 ...

  • 酱香到底是个什么香?一定要刨根问底?

    作者  赵爱民 凡是对白酒略知一二的人,一定听说过白酒分酱香型.浓香型.清香型.兼香型--那么问题来了,酱香型白酒到底有什么特点?酱香是个什么香? 酱香型亦称茅香型,以茅台.习酒.郎酒等数十种蜚声中外 ...

  • 刨根问底︱吃晚饭还有最佳时间?

    晚饭和午饭合理间隔时间约为6小时,晚饭距离夜间睡眠的合理间隔时间约为4小时. 如果晚饭过早,晚饭至第二天早饭的间隔时间会过长,人在夜里易发生低血糖反应. 晚饭过晚,一是容易导致睡眠不安,二是人体排尿高 ...

  • 刨根问底︱哪些人最容易缺钙?

    在人的一生中,"两个头儿和一个中段期"最容易缺钙. "两个头":一个是在新生儿期.婴幼儿期.青春期的孩子们因生长发育的需要,对钙的摄入需求较大:另一个是老年期, ...

  • 刨根问底︱哪些食物有排铅作用?

    一些食物有抑制铅吸收和促进铅排出的作用,可以适当食用. 1.含钙.铁.锌丰富的食物:钙.铁.锌对铅的吸收起拮抗作用,从而减少铅吸收.含钙丰富的食物有牛奶.奶粉.豆腐等.含铁丰富的食物有瘦肉.肝脏.血豆 ...

  • 刨根问底︱需要给孩子补充蛋白粉吗?

    孩子的身高和智力发育受多种因素影响,蛋白质只是其中一部分,不能替代其他营养素.过量的蛋白粉,可能对孩子稚嫩的肾脏和肝脏造成严重负担. 因此,儿童和青少年只要能够正常科学进食,则建议以自然膳食为主,不需 ...