总结之优化动画卡顿:卡顿原因分析及优化方案 | 您所在的位置:网站首页 › 无锡最高的山峰是什么山啊 › 总结之优化动画卡顿:卡顿原因分析及优化方案 |
目录
一、动画卡顿分析
动画卡顿的原因
页面渲染流程
二、优化方法
JavaScript:优化JavaScript的执行效率
Style:降低样式计算复杂度和范围
Layout:避免大规模、复杂的布局
Paint/Composite:GPU加速
三、参考
一、动画卡顿分析
动画卡顿的原因
大多数设备的刷新频率是60次/秒,也就是1秒钟的动画是由60个画面连在一起生成的,所以要求浏览器对每一帧画面的渲染工作要在16ms内完成。当渲染时间超出16ms时,1秒钟内少于60个画面生成,就会有不连贯、卡顿的感觉,影响用户体验。 页面渲染流程一个页面帧在客户端的渲染分为以下几步:
在网上可以看到很多的优化方案总结,大佬们都写的很好。 Talk is cheap. Show me the code. 结合页面渲染流程,这里将结合一些测试代码,分析动画的各种优化方案和效果: JavaScript:优化JavaScript的执行效率 requestAnimationFrame代替setTimeout和setInterval 可并行的DOM元素更新划分为多个小任务 DOM无关的耗时操作放到Web Workers中 Style:降低样式计算复杂度和范围 降低样式选择器的复杂度 减少需要执行样式计算的元素个数 Layout:避免大规模、复杂的布局 避免频繁改变布局 用flexbox布局替代老的布局模型 避免强制同步布局事件 Paint/Composite:GPU加速 将移动或渐变元素由渲染层(RenderLayer)提升为合成层(Compositing Layer) 避免提升合成层的陷阱 JavaScript:优化JavaScript的执行效率 1. requestAnimationFrame代替setTimeout和setInterval为什么setTimeout和setInterval不好? 由于js是单线程执行,所以为了防止某个任务执行时间过长而导致进程阻塞,js中存在异步队列的概念,对于如setTimeout和ajax请求都是把进程放到了异步队列中,当主进程为空时才执行异步队列中的任务。所以 setTimeout和setInterval无法保证回调函数的执行时机,可能会在一帧之内执行多次导致多次页面渲染,浪费CPU资源甚至产生卡顿,或者是在一帧即将结束时执行导致重新渲染,出现掉帧的情况。 requestAnimationFrame是怎么优化的? CPU节能,当页面被隐藏或最小化时,暂停渲染。 函数节流,其循环间隔是由屏幕刷新频率决定的,保证回调函数在屏幕的每一次刷新间隔中只执行一次。优化效果具体如何?DEMO
通过chrome的performance面板查看具体表现的差别。
通过setTimeout进行了3次渲染,而且有长时间帧出现:
Web Worker的好处是什么? JavaScript是单线程的,如果频繁的进行耗时操作(如实时更新数据),就会造成拥堵,影响用户交互体验。Web Worker的作用在于为JavaScript创建了多线程环境,worker线程在后台运行,受主线程控制,两者互不干扰。worker线程负担高延迟且UI无关的任务,主线程负责UI交互就会相对流畅。 需要注意 Web Worker无法操作DOM,本质上只是将数据刷新和页面渲染拆开执行。 Web Worker遵循同源策略且限制本地访问。 用一次多余的网络请求和浏览器线程资源来换取高效执行。优化效果具体如何?DEMO
可以通过chrome的performance面板查看具体表现的差别:
不使用web worker,减少了一次网络请求,但是出现了长时间帧,有卡帧的风险。
可考虑的应用场景 轮询服务器获取数据 频繁的数据上报 耗时的数据处理 Style:降低样式计算复杂度和范围 1. 降低样式选择器的复杂度?降低样式选择器的复杂度是常常被提出的一个优化方法,实际上这个方法的效果比较微弱,根据Ivan Curic的文章[5]的测试方法(DEMO),在一个拥有50000个节点的页面中,不同选择器复杂度对于性能的影响不会超过20ms,而一般情况下,页面的节点数都不会达到这个数量。 优化效果微弱的原因在于浏览器引擎对选择器速度进行了优化,不同引擎的性能优化方案不同,所以开发者的优化是否有效是难以预测的,至少对于静态元素的优化性价比是极低的。 通过测试可以确认的一点是,应当减少伪类选择器和过长的选择器的使用。推荐按照如OOCSS、BEM等命名规范来组织CSS,优点是在微弱优化性能的同时也提高了代码可维护性。 2. 减少需要执行样式计算的元素个数这一点是针对较早的浏览器而言,较早的浏览器如改变了body元素上的一个类,则其子元素都需要重新计算样式。 现代浏览器都进行了优化,所以优化效果要视具体应用场景而言。目前尚未挖掘到应用例子,后期如有发现回来填坑。 Layout:避免大规模、复杂的布局 1. 避免频繁触发布局不同的属性导致的渲染成本不尽相同,这一点在css动画时对比尤其明显。触发layout或者paint的动画属性尤其消耗性能,所以应当尽量使用transform和opacity作为动画属性,如果无法实现则考虑采用JavaScript实现动画。
性能差别有多大?
以width和transform为例,分别实现动画的性能差别:DEMO
通过width实现动画,帧率较低且曲线抖动明显,右下角也给出了一帧的渲染过程,触发了样式计算,布局,绘制和渲染层合并:
常用的经典布局方案有基于浮动的布局、基于绝对定位的布局,flexbox布局相较而言更加高效。在能用flexbox布局的项目中,尽量用flexbox布局。以下DEMO尝试用三种布局方式渲染一样的界面效果来测试性能:
绝对布局:对于每一个元素都需要唯一的定位坐标,当元素较多时,CSS文件偏大,导致在样式计算上花费了较多的时间。
什么是强制同步布局?
前面提到了页面渲染流程是JavaScript->Style->Layout->Paint->Composite,强制同步布局就是强制浏览器在执行JavaScript脚本前先执行布局。
什么情况会导致强制同步布局?
JavaScript运行时,获取到的元素属性样式都是上一帧的数值,所以如果在当前帧的渲染流程中,获取当前帧的某个元素属性之前对该元素进行了修改,浏览器就必须先应用属性再执行JavaScript逻辑,简而言之就是DOM先写后读操作,尤其是连续的读写操作,对浏览器的性能影响更大。
对性能影响有多大?DEMO
DEMO通过改变1000个节点的属性,测试强制同步布局事件对性能的影响,具体参照下图。可以发现性能的损耗是极大的,连续的读写操作导致连续的强制同步事件触发,JavaScript执行时间变得很长:
注:可在Chrome的开发者工具的layers面板查看合成层,layers面板打开方法command+shift+p(mac)/ctrl+shift+p(windows) -> show layers 将复杂/频繁变化的元素提升到合成层,这样的好处是该元素绘制的时候不会触发其他元素的绘制。渲染层提升为合成层的原因如下(注意以下原因是在渲染层的基础之上): 有3D transform属性 有perspective属性 3D canvas或硬件加速的2D canvas 硬件加速的iframe元素(如iframe嵌入的页面有合成层,合成层需要硬件加速) 使用了硬件加速的插件,如flash/iframe 对opacity/transform属性应用了animation/transition(当animation/transition为active) will-change属性为opacity、transform、top、left、bottom、right 子元素是compositing layer 兄弟元素是compositing layer,与当前的非composting layer有重叠,composting layer的层级低于非composting layer层为什么会有性能提升? 只重绘需要重绘的部分 GPU加速:合成层的位图直接由GPU合成,比CPU处理速度更快性能提升有多少? DEMO
通过demo可以看到,提升为合成层之后,paint所需的时间大大减少。
提升合成层是不是越多越好?
可以看到提升合成层后,paint时间大大下降。但是合成层的创建需要消耗额外的内存和管理资源,过多的合成层给页面带来的内存开销很大,DEMO创建了5000个元素,全部元素都提升为合成层与不提升时的内存消耗进行对比。这一点在移动端尤其需要注意,相比较于PC,移动设备的内存资源更加紧张。
只提升动画元素的渲染层 基于提升为合成层来提升性能的原理,当页面其他部分绘制比较复杂且相对静态时,我们可以考虑将动画元素单独提升为合成层,减少动画元素对页面其他元素的影响。 2. 避免提升合成层的陷阱回顾一下提升为合成层的最后一个原因:兄弟元素是compositing layer,与当前的非composting layer有重叠,composting layer的层级低于非composting layer层。
这种情况下导致的提升合成层一般都是预期外的。其原因与屏幕的渲染流程有关,我们回忆一下页面映射的最后一步,每一个Compositing Layer对应一张位图,合成器最后将这些位图根据层级关系合并起来最终输出到屏幕。此时我们假设A是已知的合成层,而B理想中应当是普通渲染层,其层级关系如图所示:
当发现页面明明没有什么内容却比较卡的时候可以检查一下是不是这个原因,以下给出常见的层压缩解决不了的情况: 带transform动画的元素,其后的元素为relative/absolute定位 原因:relative元素和relative下的absolute元素由于assumedOverlap原因都被被提升为合成层,又由于设置了overflow:hidden,基于前面提到的squashingClippingContainerMismatch,渲染层与合成层的裁剪容器不同,导致无法层压缩,出现过多的合成层。 解决方法:为动画的元素设置z-index扰乱compositing layer的排序。DEMO 三、参考本文结构主要参照文章[1],对其中的一些优化点进行了实际测试和扩展,也算是一篇读后感吧~ 关于层压缩部分情况过于复杂,没找到什么资料,感觉还没有完全吃透,后面有机会再重新整理一下。感恩以下大佬! 深度剖析浏览器渲染性能原理,你到底知道多少? www.jianshu.com/p/a32b890c2… Optimizing CSS: ID Selectors and Other Myths www.sitepoint.com/optimizing-… GPU Accelerated Compositing in Chrome www.chromium.org/developers/… GPU加速是什么 aotu.io/notes/2017/… Blink Compositing Update: Recap and Squashing docs.google.com/presentatio… 无线性能优化:Composite taobaofed.org/blog/2016/0…撒花完结欢迎指教 广告时间飞书是字节跳动旗下办公套件产品,其将即时沟通、在线协作、音视频会议、日历、云盘、工作台等功能进行了深度整合,为用户提供一站式协作体验。目前,飞书服务的客户已经覆盖了科技互联网、信息技术、制造、建筑地产、企业服务、教育、媒体等多个领域。欢迎投递字节跳动飞书团队,有海量前端后端HC~扫描二维码或者点击链接投递,认准飞书团队👍~
【校招】内推码: HZNVPHS,投递链接: job.toutiao.com/s/JaeUCoc
【社招】投递链接: job.toutiao.com/s/JaevUNo
|
CopyRight 2018-2019 实验室设备网 版权所有 |