在最近一个项目中,由于初期没有作太好的规划与人员技术能力有限,在性能方面有不少问题,而我加入这个项目的主要任务就是进行各类性能优化。其中对于重排重绘以及硬件加速相关优化进行的比较多,这种优化方式成本比较低,风险小,在配置较差设备效果明显。此文章来之原文连接css
近些年,咱们老是听到硬件加速,以及它如何帮助咱们提高网页的动画性能,让网页动画变得更好,在移动端更流畅。可是我想一大部分经验少的工程师是不知道硬件加速是如何工做的以及咱们如何使用它来帮助咱们让动画变得更流畅。前端
硬件加速听起来很是复杂,像高等数学。在这篇文章中,我会简明的讲解如何在你的前端工程中使用这项技术。算法
让咱们看一个简单的动画例子,一些球叠加在一块儿。而后移动这一组球按照一个四边形轨迹移动。最简单的办法就是经过设置left
和top
来实现。咱们能够经过JavaScript
来实现,可是咱们会使用css
来实现。请注意,我没有使用任何辅助库,好比Autoprefixer
,可是建议你在项目中使用这种库来自动补充前缀canvas
.ball-running {
animation: run-around 4s infinite;
}
@keyframes run-around {
0%: {
top: 0;
left: 0;
}
25% {
top: 0;
left: 200px;
}
50% {
top: 200px;
left: 200px;
}
75% {
top: 200px;
left: 0;
}
}
复制代码
这是一个在线例子。经过按钮来运行JavaScript
启动动画:浏览器
CodePen Preview for Animating overlapping balls with top/left Properties性能优化
点击“Start Animation”,你会发现动画在任何桌面浏览器运行的都并非很顺畅。若是你在移动端运行这个动画网页,你会看到很严重的丢帧现象(译者注:其实)。为了解决这个问题,咱们可使用transform
的translate()
函数代替对left
和top
的改变。app
.ball-running {
animation: run-around 4s infinite;
}
@keyframes run-around {
0%: {
transform: translate(0, 0);
}
25% {
transform: translate(200px, 0);
}
50% {
transform: translate(200px, 200px);
}
75% {
transform: translate(0, 200px);
}
}
复制代码
在下面例子中尝试运行上面的代码:ide
Animating overlapping balls with CSS transforms函数
如今动画比之前流畅多了。很是好!那么,为何会这样呢?哈,css的transform
并无不像操做left
和top
属性那样致使重绘。让咱们看看Chrome
中的DevTools
里面Timeline
页面的执行结果(译者注:在Chrome
新版本中,该工具变成了performance
)。工具
在left
和top
这个例子中,咱们能够看到在每个步骤都有绿色柱状图。这是一个性能代价很高的操做。动画会产生丢帧,这也是咱们优化动画效果的标准。
项目看看css
的transforms
的时间线:
就像你看到的那样,几乎没有绿色的柱形图出现。
另外一个用于跟踪重绘处理的工具是Chrome
的DevTools
中rendering
里面的Enable paint flashing
选项。当该选项被选中,绿色的框会出如今重绘的区域。在left
和top
的例子中,当动画运行的时候,球就有一个绿色的框,所以球就发生了重绘。
在另外一个例子中,重绘仅仅发生在动画开始和结束的时候。
那么transform
是如何让动画不会致使重绘的呢?最直接的答案就是transform
会直接使用硬件加速,在GPU
中运行,绕开了软件渲染。
当浏览器接收到页面的信息,他会将页面解释成DOM
输。DOM
树和CSS
让浏览器构建渲染树。渲染书包含渲染对象 - 在页面中须要渲染的元素。每个渲染对象被分配到一个图层中。每个图层被更新到GPU
。这里的秘诀就在于经过transform
的层会使用GPU
渲染,所以不须要重绘,就像3D图形同样。这个转换是单独处理的。
在咱们的例子中,CSS
的transform
在GPU
直接建立一个新的层。Chrome
的DevTools
的“Show layer borders”选项能够帮助咱们查看那些是单独的层,开启这个选项之后单独的层会具备一个橙色的边框。
使用transform
样式的球会被一个橙色的边框所包围,所以它在一个独立的层中:
在此,你可能会问:何时浏览器会建立这种独立的层呢?
在如下状况会产生新的层:
CSS
的transform
属性<video>
和 <canvas>
元素CSS
的filter
属性z-index
提高层级你可能会想,‘等等,这个例子用的是2D转换,并非3D转换’。是的。这就是为何在开始和结束的时候会有两次重绘产生。
3D转换和2D转换的不一样在因而否提早生成新的层,若是是2D的话是在实行的时候。在动画开始的时候,一个新的层被建立,而且被传入GPU
处理。当动画结束,独立的层被移除,结果被从新绘制。
GPU
渲染元素并非全部的CSS
属性变化都会直接在GPU
处理。只有下面的属性会这样处理:
所以为了页面更加流畅,高性能的动画,咱们须要尽量的使用GPU
来处理。
GPU
渲染在某些状况下,它会在动画开始的时候尝试在GPU
渲染一个元素。这能够帮助咱们避免建立新层的时候致使重绘。所以,咱们须要使用transform hack
技术
.example1 {
transform: translateZ(0);
}
.example2 {
transform: rotateZ(360deg);
}
复制代码
这么作会让浏览器知道,咱们但愿采用3D的方式作转换,这会让浏览器在最开始的时候就使用GPU
处理,启动硬件加速。
这个技术也能够用于结构复杂的元素上。让咱们回到第一个例子,修改这个例子为包含一个球,还有使用filter
属性并一个具备一个背景图片的容器。球经过left
和top
实现动画效果。
Animating a ball with top/left properties
再一次,动画开始丢帧。由于每一次重绘都致使了大量的性能消耗。
如今让咱们加上transform hack
。
Animating left/top properties with hardware acceleration
如今就没以前那么糟糕了。为何?由于如今背景再一个独立的层中处理,所以重绘的代价变得很低。
天下没有免费的午饭。对于硬件加速,目前有几个问题。
大部分重要的问题都是关于内存。GPU
处理过多的内容会致使内存问题。这在移动端和移动端浏览器会致使崩溃。所以,一般不会对全部的元素使用硬件加速。
在GPU
渲染字体会致使抗锯齿无效。这是由于GPU
和CPU
的算法不一样。所以若是你不在动画结束的时候关闭硬件加速,会产生字体模糊。
有必要使用transform hack
的地方是提升性能。浏览器自身也提供了优化的功能,这也就是will-change
属性。这个功能容许你告诉浏览器这个属性会发生变化,所以浏览器会在开始以前对其进行优化。这里有一个例子:
.example {
will-change: transform;
}
复制代码
遗憾的是,并非全部浏览器都支持这个功能。
概述如下咱们都讲了什么:
GPU
渲染能够提升动画性能GPU
渲染会提升动画的渲染帧数GPU
渲染的CSS
属性GPU
渲染