在网上看到一个这样的问题: transform与position:absolute 有什么区别?查阅资料后发现这道题目其实不简单,涉及到重排、重绘、硬件加速等网页优化的知识。
一、问题背景
商业转载请联系作者获得授权,非商业转载请注明出处。
原文: https://www.w3cplus.com/css3/introduction-to-hardware-acceleration-css-animations.html © w3cplus.com
商业转载请联系作者获得授权,非商业转载请注明出处。
原文: https://www.w3cplus.com/css3/introduction-to-hardware-acceleration-css-animations.html © w3cplus.com
过去几年,我们常常会听说硬件加速给移动端带来了巨大的体验提升,但是即使对于很多经验丰富的开发者来说,恐怕对其背后的工作原理也是模棱两可,更不要合理地将其运用到网页的动画效果中了。
1、position + top/left 的效果
下面让我们来看一个动画效果,在该动画中包含了几个堆叠在一起的球并让它们沿相同路径移动。最简单的方式就是实时调整它们的 left 和 top 属性,使用 css 动画实现。
<style>
html,
body {
width: 100%;
height: 100%;
}
.ball-running {
animation: run-around 4s infinite;
width: 100px;
height: 100px;
background-color: red;
position: absolute;
}
@keyframes run-around {
0%: {
top: 0;
left: 0;
}
25% {
top: 0;
left: 200px;
}
50% {
top: 200px;
left: 200px;
}
75% {
top: 200px;
left: 0;
}
}
</style>
<body>
<div class="ball-running"></div>
</body>
在运行的时候,即使是在电脑浏览器上也会隐约觉得动画的运行并不流畅,动画有些停顿的感觉,更不要提在移动端达到 60fps 的流畅效果了。这是因为top和left的改变会触发浏览器的 reflow 和 repaint ,整个动画过程都在不断触发浏览器的重新渲染,这个过程是很影响性能的。
2、transform 的效果
为了解决这个问题,我们使用 transform 中的 translate() 来替换 top 和 left ,重写一下这个动画效果。
<style>
html,
body {
width: 100%;
height: 100%;
}
.ball-running {
animation: run-around2 4s infinite;
width: 100px;
height: 100px;
background-color: red;
position: absolute;
}
@keyframes run-around2 {
0%: {
transform: translate(0, 0);
}
25% {
transform: translate(200px, 0);
}
50% {
transform: translate(200px, 200px);
}
75% {
transform: translate(0, 200px);
}
}
</style>
<body>
<div class="ball-running"></div>
</body>
这时候会发现整个动画效果流畅了很多,在动画移动的过程中也没有发生repaint和reflow。
那么,为什么 transform
没有触发 repaint 呢?原因就是:transform
动画由GPU控制,支持硬件加速,并不需要软件方面的渲染。
二、硬件加速工作原理
浏览器接收到页面文档后,会将文档中的标记语言解析为DOM树,DOM树和CSS结合后形成浏览器构建页面的渲染树,渲染树中包含了大量的渲染元素,每一个渲染元素会被分到一个图层中,每个图层又会被加载到GPU形成渲染纹理,而图层在GPU中 transform 是不会触发 repaint 的,这一点非常类似3D绘图功能,最终这些使用transform的图层都会使用独立的合成器进程进行处理。
在我们的示例中,CSS transform 创建了一个新的复合图层,可以被GPU直接用来执行 transform 操作。在chrome开发者工具中开启“show layer borders”选项后,每个复合图层就会显示一条黄色的边界。示例中的球就处于一个独立的复合图层,移动时的变化也是独立的。
此时,你也许会问:浏览器什么时候会创建一个独立的复合图层呢?事实上一般是在以下几种情况下:
(1)3D 或者 CSS transform
(2)video或canvas标签
(3)CSS filters
(4)元素覆盖时,比如使用了 z-index 属性
等一下,上面的示例使用的是 2D transition 而不是 3D 的 transforms 啊?这个说法没错,所以在timeline中我们可以看到:动画开始和结束的时候发生了两次 repaint 操作。
3D 和 2D transform 的区别就在于,浏览器在页面渲染前为3D动画创建独立的复合图层,而在运行期间为2D动画创建。
动画开始时,生成新的复合图层并加载为GPU的纹理用于初始化 repaint,然后由GPU的复合器操纵整个动画的执行,最后当动画结束时,再次执行 repaint 操作删除复合图层。
三、使用 GPU 渲染元素
1、能触发GPU渲染的属性
并不是所有的CSS属性都能触发GPU的硬件加速,实际上只有少数属性可以,比如下面的这些:
(1)transform
(2)opacity
(3)filter
2、强制使用GPU渲染
为了避免 2D transform 动画在开始和结束时发生的 repaint 操作,我们可以硬编码一些样式来解决这个问题:
.exam1{
transform: translateZ(0);
}
.exam2{
transform: rotateZ(360deg);
}
这段代码的作用就是让浏览器执行 3D transform,浏览器通过该样式创建了一个独立图层,图层中的动画则有GPU进行预处理并且触发了硬件加速。
3、使用硬件加速需要注意的事项
使用硬件加速并不是十全十美的事情,比如:
(1)内存。如果GPU加载了大量的纹理,那么很容易就会发生内存问题,这一点在移动端浏览器上尤为明显,所以,一定要牢记不要让页面的每个元素都使用硬件加速。
(2)使用GPU渲染会影响字体的抗锯齿效果。这是因为GPU和CPU具有不同的渲染机制,即使最终硬件加速停止了,文本还是会在动画期间显示得很模糊。
4、will-change
浏览器还提出了一个 will-change 属性,该属性允许开发者告知浏览器哪一个属性即将发生变化,从而为浏览器对该属性进行优化提供了时间。下面是一个使用 will-change 的示例
.exam3{
will-change: transform;
}
缺点在于其兼容性不大好。
总结:
1、transform 会使用 GPU 硬件加速,性能更好;position + top/left 会触发大量的重绘和回流,性能影响较大。
2、硬件加速的工作原理是创建一个新的复合图层,然后使用合成线程进行渲染。
3、3D 动画 与 2D 动画的区别;2D动画会在动画开始和动画结束时触发2次重新渲染。
4、使用GPU可以优化动画效果,但是不要滥用,会有内存问题。
5、理解强制触发硬件加速的 transform 技巧,使用对GPU友好的CSS属性。
英文原文:http://www.sitepoint.com/introduction-to-hardware-acceleration-css-animations/