提高CSS3背景位置动画的性能

14

我正在尝试改进CSS3动画,因为当前代码似乎导致CPU负载过高,并且浏览器似乎很卡顿。

我该怎么办?我已经使用了所有的厂商前缀等。我不确定是否可以改进代码或重构它以使用最佳的代码实践。

Fiddle演示

.wrapper {
  width: 960px;
  height: 140px;
  margin-top: 80px;
  position: relative;
}
.content:before {
  position: absolute;
  z-index: 1;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  content: "";
  -webkit-transform: translateZ(0);
  transform: translateZ(0);
  -webkit-transform-origin: 50% 50% 0;
  -ms-transform-origin: 50% 50% 0;
  transform-origin: 50% 50% 0;
  v -webkit-animation-name: sideupscroll;
  animation-name: sideupscroll;
  /*animation-duration*/
  -webkit-animation-duration: 80s;
  animation-duration: 80s;
  /*animation-timing-function*/
  -webkit-animation-timing-function: linear;
  animation-timing-function: linear;
  /*animation-iteration-count*/
  -webkit-animation-iteration-count: infinite;
  animation-iteration-count: infinite;
  background: url("http://i.imgur.com/wNna7D3.png") repeat fixed 0 0 indigo;
  -webkit-animation-fill-mode: both;
  animation-fill-mode: both;
}
/* Safari and Chrome */
@-webkit-keyframes sideupscroll {
  0% {
    background-position: 0 0;
  }
  50% {
    background-position: -50% -100%;
  }
  100% {
    background-position: -100% -200%;
  }
}
@keyframes sideupscroll {
  0% {
    background-position: 0 0;
  }
  50% {
    background-position: -50% -100%;
  }
  100% {
    background-position: -100% -200%;
  }
}
<div class="wrapper">
  <div class="content"></div>
</div>

1个回答

33

原因

对于一个元素的 background-position 进行动画处理总是会消耗很多资源,并且几乎在所有浏览器上都有可能导致动画卡顿。这是因为,改变 background-position 会导致所有浏览器中的重绘+合成(在 Webkit 中还会导致重新布局)。由于需要执行如此多的昂贵操作,结果总是会出现卡顿。

存在问题的片段:

下面的片段与您的 fiddle 相同(不包含供应商前缀)。运行此片段并使用 Chrome Dev 工具进行检查,在启用“显示涂漆矩形”选项后。您会看到一个红色或绿色的盒子位于元素顶部(这是涂漆矩形),该盒子将始终闪烁或保持彩色状态,直到动画结束。它表示经常发生重绘,从而影响性能。

在 Firefox 中,可以通过在 about:config 页面中启用 nglayout.debug.paint_flashing (将其设置为 true)来查看涂漆矩形。

.wrapper {
  width: 960px;
  height: 140px;
  margin-top: 80px;
  position: relative;
}
.content:before {
  position: absolute;
  z-index: 1;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  content: "";
  transform: translateZ(0);
  transform-origin: 50% 50% 0;
  animation-name: sideupscroll;
  animation-duration: 80s;
  animation-timing-function: linear;
  animation-iteration-count: infinite;
  background: url("http://i.imgur.com/wNna7D3.png") repeat fixed 0 0 indigo;
  animation-fill-mode: both;
}
@keyframes sideupscroll {
  0% {
    background-position: 0 0;
  }
  50% {
    background-position: -50% -100%;
  }
  100% {
    background-position: -100% -200%;
  }
}
<div class="wrapper">
  <div class="content"></div>
</div>


解决方案

尽量避免使用会引起视觉变化的background-*属性,而应该使用transform等其他属性。在Blink(Chrome)和EdgeHTML中,使用transform能够获得更好的性能表现,因为Blink只进行重新合成,而EdgeHTML仅在动画的第一次更新时触发重新布局。

没有问题的代码片段:(或者至少在Blink和EdgeHTML中对性能影响较小)

下面的代码片段使用transform属性(translateXtranslateY)来实现与期望输出非常相似(但不完全相同)的效果。如果您使用开发工具检查此片段,您会发现绿色框(绘制矩形)仅在动画开始时出现一次。之后,浏览器仅执行合成操作,因此性能更好。

.wrapper {
  width: 960px;
  height: 140px;
  margin-top: 80px;
  position: relative;
  overflow: hidden;
}
.content:before {
  position: absolute;
  z-index: 1;
  top: 0;
  left: 0;
  width: 200%;
  height: 400%;
  content: "";
  background: url("http://i.imgur.com/wNna7D3.png") 0 0 indigo;
  background-repeat: repeat;
}
.content {
  position: relative;
  height: 100%;
  width: 100%;
  animation-name: sideupscroll;
  animation-duration: 80s;
  animation-timing-function: linear;
  animation-iteration-count: infinite;
  animation-fill-mode: both;
}
@keyframes sideupscroll {
  0% {
    transform: translateX(0%) translateY(0%);
  }
  50% {
    transform: translateX(-50%) translateY(-100%);
  }
  100% {
    transform: translateX(-100%) translateY(-200%);
  }
}
<div class="wrapper">
  <div class="content"></div>
</div>

Gecko和Webkit怎么办?

很遗憾,目前在使用这些渲染引擎的浏览器上似乎没有解决方案。唯一的选择似乎是减少animation-duration。减少动画的持续时间意味着需要重绘、重排和重组合的次数更少,从而动画的性能更好。

由于持续时间只有20秒,下面的代码片段在Firefox中看起来不那么卡顿。

.wrapper {
  width: 960px;
  height: 140px;
  margin-top: 80px;
  position: relative;
  overflow: hidden;
}
.content:before {
  position: absolute;
  z-index: 1;
  top: 0;
  left: 0;
  width: 200%;
  height: 400%;
  content: "";
  background: url("http://i.imgur.com/wNna7D3.png") 0 0 indigo;
  background-repeat: repeat;
}
.content {
  position: relative;
  height: 100%;
  width: 100%;
  animation-name: sideupscroll;
  animation-duration: 20s;
  animation-timing-function: linear;
  animation-iteration-count: infinite;
  animation-fill-mode: both;
}
@keyframes sideupscroll {
  0% {
    transform: translateX(0%) translateY(0%);
  }
  50% {
    transform: translateX(-50%) translateY(-100%);
  }
  100% {
    transform: translateX(-100%) translateY(-200%);
  }
}
<div class="wrapper">
  <div class="content"></div>
</div>

有用的链接:

注意:正如我之前已经说过的那样,本动画与您在问题中提到的动画不完全相同,但在我看来,这已经是最接近的了。


2
我从中学到了很多东西。非常感谢你抽出时间向我展示这些差异。我之前毫不知情。 - user2513846
但这甚至会使div中的内容动画化。有没有办法只动画背景? - UzUmAkI_NaRuTo
1
@SumeetDarade:将您的内容放在一个单独的div或伪元素中,而不是.content div中,并使用定位使它们重叠在一起。 - Harry
谢谢,非常好用。但是我正在使用缩放而不是平移。在Chrome和Mozilla中效果很好,但在Edge/IE中不行。首先我使用背景位置进行动画处理,在Mozilla和IE/Edge中效果非常好。但在Chrome上不行。有什么建议吗? - UzUmAkI_NaRuTo
该问题也适用于定位、顶部和左侧。 - Ratata Tata

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接