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>
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.
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.
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>
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:
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With