文章目录
如果你曾经注意到过基于WebKit的浏览器在执行某些CSS操作(尤其是transform
和animation
)时出现的“闪烁”,那么你很可能接触过“硬件加速”这个术语。
CPU、GPU和硬件加速
简而言之:硬件加速意味着图形处理单元(GPU)会协助你的浏览器渲染网页,做一些繁重的工作,而不是把这些工作全部丢给中央处理单元(CPU)来完成。当一个CSS操作被硬件加速时,它的速度也会因为页面的渲染速度变快而得到提升。
正如它们的名字所示,CPU和GPU都是处理单元。CPU位于计算机的主板上,它被称为计算机的🧠。GPU位于计算机的显卡上,负责处理和渲染图像。此外,GPU是专门为执行复杂的数学和几何计算而设计的,这些计算也是图形渲染的必要条件。因此,将操作丢给GPU处理可以产生巨大的性能提升,也可以减少移动设备上的CPU争用。
硬件加速(又称GPU加速)依赖于浏览器渲染页面时使用的分层模型。当对页面上的元素进行某些操作(比如3D变换)时,该元素会被移动到属于它自己的“图层”,在那里它可以独立于页面的其他部分进行渲染,并在之后被合成(画到屏幕上)。这样就隔离了内容的渲染,如果该元素的变换是帧之间唯一的变化,那么页面的其他部分就不必重新渲染,这样往往能够提供明显的速度优势。不过这里需要说明一下:只有3D变换拥有属于自己的图层,2D变换没有。
CSS的animation
、transform
和transition
不会自动进行GPU加速,一般是由浏览器缓慢的软件渲染引擎执行。然而有些浏览器通过某些属性提供硬件加速,以获得更好的渲染性能。
例如,opacity
不透明度就是少数可以适当加速的CSS属性之一,因为GPU可以很容易地操纵它。基本上,如果你在transition
或animation
里淡化不透明度,浏览器会智能地把它丢给GPU操作,速度会非常快。不透明度是所有CSS属性里性能最好的之一,你使用它不会出现任何问题。其他常见的硬件加速操作是CSS的3D变换。
The Old:translateZ()(or translate3d())Hack
很长时间以来,我们一直在使用 translateZ()
(or translate3d()
) 这种黑客行为(有时也称为null transform hack)来欺骗浏览器将animation
和transform
推给硬件加速。
例如一个二维空间的元素可以添加这个简单的规则来进行硬件加速:
transform: translate3d(0, 0, 0);
硬件加速操作会创建一个所谓的合成渲染层(compositor layer),并上传到GPU进行合成。然而,强行创造一个图层并不能解决某些页面上的性能瓶颈。
虽然图层创建技术可以提升页面速度,但有一定代价:它们会占用系统RAM和GPU上的内存(在移动设备上很有限),而且拥有大量的图层会产生不好的影响(尤其是在移动设备上),所以必须明智地使用它们,确保其可以真正帮助页面性能,而且性能瓶颈不是由你页面上的其他操作造成的。
为了避免图层创建的黑客行为,我们引入了一个新的CSS属性will-change
,它允许我们提前告知浏览器我们可能会对一个元素做出什么样的改变,从而使浏览器可以提前为元素做一些适当的优化。例如,在动画实际开始之前进行一些潜在的大量的准备工作。
The New:值得称道的will-change
will-change
属性允许你提前告知浏览器你可能会对一个元素进行什么样的改变,这样它就可以提前设置适当的优化,避免了可能会对页面的响应性产生负面影响的启动成本。这些元素可以更快地被改变和渲染,页面将能够迅速地更新,从而带来更流畅的体验。
例如,当在一个元素上使用CSS三维变换时,该元素及其内容可能会升到一个新的图层,就像前面提到的那样,之后才会被合成(绘制到屏幕上)。然而,在一个新的图层中设置元素是一个代价相对昂贵的操作,这可能会使变换动画的开始时间延迟几分之一秒,导致明显的“闪烁”。
为了避免这种延迟,你可以在变化实际发生前的一段时间通知浏览器。这样,浏览器就会有一些时间为这些变化做准备,当这些变化发生时,元素的图层就会准备好,变换动画就可以执行,然后元素就可以被渲染,页面就会迅速更新。
使用will-change
,向浏览器提示即将发生的变换,可以在你期望被变换的元素上添加这个简单的规则:
will-change: transform;
你也可以向浏览器声明,你打算改变一个元素的滚动位置(该元素在可见滚动窗口中的位置,以及在该窗口中的可见程度)、或者