在 CSS 动画中使用硬件加速(翻译)

在最近一个项目中,由于初期没有作太好的规划与人员技术能力有限,在性能方面有不少问题,而我加入这个项目的主要任务就是进行各类性能优化。其中对于重排重绘以及硬件加速相关优化进行的比较多,这种优化方式成本比较低,风险小,在配置较差设备效果明显。此文章来之原文连接css

近些年,咱们老是听到硬件加速,以及它如何帮助咱们提高网页的动画性能,让网页动画变得更好,在移动端更流畅。可是我想一大部分经验少的工程师是不知道硬件加速是如何工做的以及咱们如何使用它来帮助咱们让动画变得更流畅。前端

硬件加速听起来很是复杂,像高等数学。在这篇文章中,我会简明的讲解如何在你的前端工程中使用这项技术。算法

为何须要它?

让咱们看一个简单的动画例子,一些球叠加在一块儿。而后移动这一组球按照一个四边形轨迹移动。最简单的办法就是经过设置lefttop来实现。咱们能够经过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”,你会发现动画在任何桌面浏览器运行的都并非很顺畅。若是你在移动端运行这个动画网页,你会看到很严重的丢帧现象(译者注:其实)。为了解决这个问题,咱们可使用transformtranslate()函数代替对lefttop的改变。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并无不像操做lefttop属性那样致使重绘。让咱们看看Chrome中的DevTools里面Timeline页面的执行结果(译者注:在Chrome新版本中,该工具变成了performance)。工具

lefttop这个例子中,咱们能够看到在每个步骤都有绿色柱状图。这是一个性能代价很高的操做。动画会产生丢帧,这也是咱们优化动画效果的标准。

项目看看csstransforms的时间线:

就像你看到的那样,几乎没有绿色的柱形图出现。

另外一个用于跟踪重绘处理的工具是ChromeDevToolsrendering里面的Enable paint flashing选项。当该选项被选中,绿色的框会出如今重绘的区域。在lefttop的例子中,当动画运行的时候,球就有一个绿色的框,所以球就发生了重绘。

在另外一个例子中,重绘仅仅发生在动画开始和结束的时候。

那么transform是如何让动画不会致使重绘的呢?最直接的答案就是transform会直接使用硬件加速,在GPU中运行,绕开了软件渲染。

硬件加速如何工做的

当浏览器接收到页面的信息,他会将页面解释成DOM输。DOM树和CSS让浏览器构建渲染树。渲染书包含渲染对象 - 在页面中须要渲染的元素。每个渲染对象被分配到一个图层中。每个图层被更新到GPU。这里的秘诀就在于经过transform的层会使用GPU渲染,所以不须要重绘,就像3D图形同样。这个转换是单独处理的。

在咱们的例子中,CSStransformGPU直接建立一个新的层。ChromeDevTools的“Show layer borders”选项能够帮助咱们查看那些是单独的层,开启这个选项之后单独的层会具备一个橙色的边框。

使用transform样式的球会被一个橙色的边框所包围,所以它在一个独立的层中:

在此,你可能会问:何时浏览器会建立这种独立的层呢?

在如下状况会产生新的层:

  • 3D 或者 CSStransform属性
  • <video><canvas> 元素
  • CSSfilter属性
  • 覆盖在其它元素之上的元素,好比经过z-index提高层级

你可能会想,‘等等,这个例子用的是2D转换,并非3D转换’。是的。这就是为何在开始和结束的时候会有两次重绘产生。

3D转换和2D转换的不一样在因而否提早生成新的层,若是是2D的话是在实行的时候。在动画开始的时候,一个新的层被建立,而且被传入GPU处理。当动画结束,独立的层被移除,结果被从新绘制。

GPU渲染元素

并非全部的CSS属性变化都会直接在GPU处理。只有下面的属性会这样处理:

  • transform
  • opacity
  • filter

所以为了页面更加流畅,高性能的动画,咱们须要尽量的使用GPU来处理。

强制在GPU渲染

在某些状况下,它会在动画开始的时候尝试在GPU渲染一个元素。这能够帮助咱们避免建立新层的时候致使重绘。所以,咱们须要使用transform hack技术

.example1 {
  transform: translateZ(0);
}

.example2 {
  transform: rotateZ(360deg);
}
复制代码

这么作会让浏览器知道,咱们但愿采用3D的方式作转换,这会让浏览器在最开始的时候就使用GPU处理,启动硬件加速。

这个技术也能够用于结构复杂的元素上。让咱们回到第一个例子,修改这个例子为包含一个球,还有使用filter属性并一个具备一个背景图片的容器。球经过lefttop实现动画效果。

Animating a ball with top/left properties

再一次,动画开始丢帧。由于每一次重绘都致使了大量的性能消耗。

如今让咱们加上transform hack

Animating left/top properties with hardware acceleration

如今就没以前那么糟糕了。为何?由于如今背景再一个独立的层中处理,所以重绘的代价变得很低。

使用硬件加速须要注意的地方

天下没有免费的午饭。对于硬件加速,目前有几个问题。

Memory

大部分重要的问题都是关于内存。GPU处理过多的内容会致使内存问题。这在移动端和移动端浏览器会致使崩溃。所以,一般不会对全部的元素使用硬件加速。

Font rendering

GPU渲染字体会致使抗锯齿无效。这是由于GPUCPU的算法不一样。所以若是你不在动画结束的时候关闭硬件加速,会产生字体模糊。

The Near Future

有必要使用transform hack的地方是提升性能。浏览器自身也提供了优化的功能,这也就是will-change属性。这个功能容许你告诉浏览器这个属性会发生变化,所以浏览器会在开始以前对其进行优化。这里有一个例子:

.example {
  will-change: transform;
}
复制代码

遗憾的是,并非全部浏览器都支持这个功能。

文末

概述如下咱们都讲了什么:

  • GPU渲染能够提升动画性能
  • GPU渲染会提升动画的渲染帧数
  • 使用会致使GPU渲染的CSS属性
  • 理解如何经过“transform hack”强制让一个元素在GPU渲染
相关文章
相关标签/搜索