高性能Web动画和渲染原理系列(2)——渲染管线和CPU渲染 目录
目录
示例代码托管在:http://www.github.com/dashnowords/blogs
博客园地址:《大史住在大前端》原创博文目录
华为云社区地址:【你要的前端打怪升级指南】
一. 高性能动画
动画的流畅程度通常是以
FPS
(Frame Per Second,每秒帧率)作为衡量的。在摄像机录制视频时每一帧实际上包含了一段时间内的画面记录(长曝光摄影的道理相同的),如果画面里的事物在运动,那么暂停播放时看到的画面通常都是模糊的,这样的画面也被称为“模糊帧”,加上双眼“视觉暂留”效果的影响,影视作品一般只要达到24FPS
就可以展示出看起来连续运动的画面;而在页面的渲染中,每一帧都是由计算机计算渲染出来的精确画面,帧和帧之间并不存在模糊过渡,所以通常认为需要达到50FPS~60FPS
的帧率,才能够得到较好的观看体验。为了达到尽可能接近
60FPS
以上的帧率,浏览器每一帧的计算和绘制所花费的时间就需要控制在1000/60≈16.6ms以内,根据Google开发者社区提供的资料,开发者最好能够将所有的工作控制在10ms
左右,以便给浏览器一些处理内部工作的时间,否则就无法在限定的时间内完成画面更新,动态的内容就会表现出卡顿,对用户体验造成负面影响。下一节就来看一下,在这16ms
的时间里,浏览器都需要完成哪些任务。二. 像素渲染管线
基本渲染流程
谈起浏览器的工作流程,你可能会在大多数文章中见过下面这张图:
它直观地描述了浏览器如何将
HTML
文件和CSS
样式文件通过逐步处理最终合成渲染树并展示在页面上的过程,当然其中每一步都是非常复杂的,如果你对此还不熟悉,可以通过编写在
JavaScript
代码中的那些事件监听器、定时任务等等异步触发的代码就会在橙色的部分执行,这部分代码运行在主线程中,如果有问题的代码或是执行时间较长的代码在其中造成了阻塞,后续的几个步骤就只能等着,这会直接延缓页面的渲染甚至导致页面直接崩溃,当JavaScript
执行完一个宏任务并清空了当前的微任务队列后,就会开始UI渲染流程,进入下一个环节。在
Style
阶段需要找出发生变更的样式并重新计算相关的尺寸,当然在首屏渲染之前第一次处理CSS
样式时,浏览器肯定已经对计算结果进行了缓存,以便在这像素渲染管线处理时节省时间。计算完样式本身后,就需要进入
Layout
阶段,重新来计算发生样式变动的元素应该以怎样的盒模型尺寸绘制在画面上的哪个位置,网页中的基本排版遵循正常文档流的规则,所以一个元素尺寸变化后,就有可能需要重新计算其父子元素或临近元素的位置,不难想象这是一个极容易引发蝴蝶效应的环节。完成了Layout
布局后,可以看到图中使用的颜色也发生了变化,因为相对而言它们的开销就比较轻量了。Paint
阶段就是生成像素数据的过程,它会将元素的背景、边框、阴影等等可见的部分绘制出来,它们可能会被绘制在多个层上。Composite
阶段,由于绘制阶段生成的画面可能分布于多个层,那么最终渲染的结果就需要将它们按照一定的顺序完成画面的重叠,这就是浏览在合成阶段主要的工作,当然这个过程并不一定是由CPU
独自完成的,后面还会讲到。当动画执行时,浏览器会不断创建帧,上面的过程就会反复发生,从而实现帧画面的不断变动:回流和重绘
不同的
CSS
样式的性能开销和造成的影响是不同的,所以上面的像素渲染管路的各个阶段并不一定都有工作要做,如果发生变更的元素样式不会造成布局变化,那么layout
阶段就不需要做什么工作,如果发生变更的