Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Improve CSS3 background-position animation's performance

I'm trying to improve a CSS3 animation as it seems the current code is causing some excessive CPU load and the browser seems to be laggy.

What can I do? I've got all the vendor prefixes etc. I'm not sure I can improve the code or refactor it to use it as best code practices.

Fiddle Demo

.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>
like image 312
user2513846 Avatar asked Mar 10 '16 01:03

user2513846


People also ask

Do CSS animations affect performance?

TL;DR # Take care that your animations don't cause performance issues; ensure that you know the impact of animating a given CSS property. Animating properties that change the geometry of the page (layout) or cause painting are particularly expensive. Where you can, stick to changing transforms and opacity.

Can you animate background position?

Your backgrounds can animate in any direction, up, down, left, right, or even diagonally. We will be using the CSS keyframes rule and the background-position property to create the effect.


1 Answers

Reason

Animating the background-position of an element is always going to be resource intensive and it has a high probability of causing laggy animations in almost all browsers. This is because, a change to the background-position results in a repaint + a composition in all browsers (+ it also results in re-layout in Webkit). Because of the need to perform so many costly operations, the result is always laggy.

Snippet with problem:

The below snippet is the same as your fiddle (without vendor prefixes). Run this snippet and inspect it using Chrome Dev tools after enabling "Show Paint Rects" option. You would see a red or green color box on top of the element (this is the paint rect) and the box will either keep blinking constantly or stay colored for the entire duration of the animation. It indicates that a repaint is happening often and thus it impacts performance.

In Firefox, the paint rects can be seen by enabling nglayout.debug.paint_flashing in about:config page (set it to 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>

Solution

It is always better to avoid animating the background-* properties (all of which are visual properties) and use properties like transform. Using transform produces better performance atleast in Blink (Chrome) and EdgeHTML as Blink only does a re-composition while EdgeHTML triggers a re-layout only for the first time (1st update within animation).

Snippet without problem: (or atleast much lesser impact on performance in Blink and EdgeHTML)

The below snippet uses transform property (translateX and translateY) to achieve what is very similar to your expected output (but not the same). If you inspect this snippet with dev tools, you'd see that the green box (paint rect) appears only once at the start of the animation. Post that, the browsers only perform compositing and hence the performance is much better.

.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>

What about Gecko and Webkit?

Unfortunately there doesn't to be a solution for browsers using these rendering engines as at the time of writing. The only option seems to be to reduce the animation-duration. A reduction in animation's duration means that the no. of re-paint + re-layout + re-composition cycles required is lesser and thus the animation's performance is better.

The below snippet looks less laggy in Firefox as the duration is only 20s.

.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>

Useful Links:

  • CSS Triggers - Lists out which properties result in which operations being triggered.
  • HTML5 Rocks - Accelerated Rendering in Chrome - Explains how accelerated rendering works in Chrome (and how to enable "Show Paint Rects" option)

Note: As I had already stated above, the animation is not 100% the same as what you had in question but in my opinion this is about the closest you could get.

like image 194
Harry Avatar answered Oct 14 '22 17:10

Harry