I am currently faced with a situation, for which I need to constantly render a flickering animation within a web page. This is currently done by means of CSS, using animation: 0.05s step-end infinite keyframe_name. The keyframe changes the button's background-color at 0% and 50%. As can be expected, using Chrome's Dev tools, this area is constantly being re-painted by the browser, which is leading to an overall poor performance.
I am aware that the use of certain properties, such as the will-change property, or even using the transform: translate3d(0, 0, 0) hack, will enable hardware accelerated rendering. However, opting for either of these options, still hasn't resulted in the desired performance improvement.
The following shows some basic code for what I am trying to achieve (optimizations are not included in code snippet):
#keyframes keyframe_name
{
0% { background-color: red; }
50% { background-color: blue; }
}
button
{
animation: 0.05s step-end infinite keyframe_name;
}
According to a number of online resources, it seems that only the transform and opacity CSS properties make it possible for the browser to optimize compositing. Since neither of these properties can be used to achieve my desired goals, which other approach/es could possibly lead to a performance enhancement, when it comes to the in-browser rendering of such a resource-intensive graphic?
Related
I have realized that CSS animations with keyframes are really hungry for resources.
I've already eliminated the shadows, which increased performance by almost 50% in my case. I've also heard that I should include a rotateZ(360deg) to trick the browser into using GPU acceleration. But even with those "tricks" applied, my star background is really resource-hungry. As soon as you open my website, CPU and GPU usage goes up drastically and remains at a pretty high level.
I'm wondering if there is a better way to perform my desired animation without wasting so much CPU or GPU power.
I wanted a background of moving stars on my website. My solution to this was to create a div for each star, giving it a size, background colour and CSS animation with keyframes. The opacity, x and y position, as well as the speed of the animation are randomized.
The keyframes are the following:
#keyframes linear-translate {
0% {
left: -10%;
transform: rotateZ(360deg); /* rotateZ tricks the browser to use GPU acceleration for better performance */
}
100% {
left: 110%;
}
}
#keyframes linear-translate-initial {
0% {
transform: translateX(0) rotateZ(360deg);
}
100% {
left: 110%;
}
}
Here is an example for one of the div elements (with tailwind CSS classes):
<div class="absolute block bg-gray-200 rounded-full"
style="left: 1%; top: 8%; width: 3px; height: 3px; opacity: 0.381289;
animation: 90.0069s linear 0s 1 normal none running linear-translate-initial,
110.007s linear 90.0069s infinite normal none running linear-translate;"></div>
Link to codesandbox with a working example:
https://codesandbox.io/s/starbackgroundreacttailwindcss-wzers?file=/src/Starfield.tsx
Any ideas on why those animations are consuming so much power and on how to make them more efficient? It's just a horizontal translation after all...
Update:
I tried to layer the stars in div elements of different speeds as suggested by Marco.
My result can be found here:
https://codesandbox.io/s/layeredstarbackgroundreacttailwindcss-wj71q?file=/src/App.tsx:392-445
Unfortunately, the "improvement" is not that great. The CPU usage went a little down, but the GPU usage increased drastically.
I've played around a bit with your sandbox and yes the solution is that simple - there are just to many / different animations / items to handle for the CPU you can see that easily when opening the task manager and checkout the CPU usage while the sandbox is idle with your page. On my "small" machine Firefox uses up to 53% CPU. when i decrease the amount of stars in your script (down to 10) it goes down to around 18%.
I had a similar problem with a "moving div" background with shadows / gradient colors and rotations while moving from left to right...
Interesting project so far.
An idea might be to have a limited amount of layers with multiple stars fixed there and then to move the whole layer (even if you have 10 layers then there are just 10 objects that moves)...
Yes you will not have the same strong effect like calculating / moving each star by its own but with 10 layers as a background (i think its a Background right?) it should not make any markable difference to the viewer...
Say I have this main heading fade in just as a user visits my page
h1 {
animation: fadeInLeft 500ms ease backwards;
}
#keyframes fadeInLeft {
from {
opacity:0;
transform:translateX(-10%);
}
to {
opacity:1;
transform:translateX(0);
}
}
<h1>Heading</h1>
Now I am curious what would happen if we imagined that the user was on a bad internet connection. Could it happen that the user would see the heading immediately if the browser was slow to download the stylesheet and apply the first keyframe? If so, is there any way to counteract this?
There is an order how DOM is rendering and you can check it out here: Which is the load, rendering and execution order of elements in a HTML page?
CSS files are downloaded after parsing HTML file, so theoretically it is a case. However, CSS files are downloaded only once and they are stored on local machine until there will be some new changes to download from server. So if it would be really the case, it would happen only once.
Is using CSS transition to move text from off screen to a place on the screen ADA (Americans with Disability Act) compliant?
h1 {
text-align: center;
position: relative;
animation: heading;
animation-duration: 3s;
animation-fill-mode: forwards;
}
#keyframes heading {
0% {top: -50px;}
100% {top: 30vh;}
}
https://codepen.io/KuanaxBon/full/OJVLOmm
The parts of WCAG that are applicable here are SC 2.2.2 pause, stop, hide and SC 2.3.3 motion from interaction.
In your example provided, the animation is automatically started on page load, so it doesn't require interaction, and as such you can bypass 2.2.3 if you want.
Similarly, 2.2.2 specifies that it's only applicable if an animation lasts longer than 5 seconds, and your example is 3 seconds.
Non-sighted visitors shouldn't have a problem with the animations, as long as the DOM order is correct.
So, in my interpretation, your example will be compliant with WCAG 2.1 as-is, but if you want to make it more useful for real human visitors, I would recommend implementing the CSS reduce-motion query for visitors who may have issues with moving text. For some people, moving text on a webpage can cause vertigo, dizziness, and/or nausea.
In order to fill the whole height of the page, I use height: 100%; for html and body tags,
and it works fine until a browser would be closed and reopened.
(I don't use 100vh because of issues on mobile devices https://nicolas-hoizey.com/2015/02/viewport-height-is-taller-than-the-visible-part-of-the-document-in-some-mobile-browsers.html )
Steps to reproduce:
Open https://angelika94.github.io/rick/ in Google Chrome on iPhone
(you will see that navigation (Morty and Beer) is placed on the bottom of the page) screenshot of css Rick with navigation
close the browser and remove it from multitasking navigation:
https://support.apple.com/en-us/HT201330
open the browser again (you will see that bottom navigation moved off the "first screen" and now you need to scroll to see it)
screenshot of css Rick without navigation
the page will be fixed by itself in these cases:
update page
rotate the device to landscape
open and close browser's navigation by tabs
close and reopen browser without closing it in multitasking nav
Why does it happen? How can I fix this behavior?
Thank you in advance!
I had a very different issue, but I think the solution I worked out may work for your situation also, because you mentioned updating the page would fix it.
So I had issues with chrome on android where if you scroll very quickly (not uncommon on mobile), some elements would fail to get re/painted. Searched everywhere for a solution but couldn't find anything that would work.
Finally, I figured out a working fix:
.pagewrap {
transform: translateZ(0);
animation-name: 'repaint';
animation-duration: 3s;
animation-iteration-count: infinite;
animation-play-state: running;
animation-timing-function: linear;
}
#keyframes repaint {from { zoom: 99.99999%; } to { zoom: 99.99998%; }}
So what this does is forces the page to continually repaint on a 3 second cycle.
Maybe I should tweak it to only shift for a fraction of a second every 2 seconds, instead of continually:
.pagewrap {
transform: translateZ(0);
animation-name: 'repaint';
animation-duration: 2s;
animation-iteration-count: infinite;
animation-play-state: running;
animation-timing-function: linear;
}
#keyframes repaint {
0% {
zoom: 99.99999%;
}
99% {
zoom: 99.99999%;
}
100% {
zoom: 99.99998%;
}
}
I tried zoom: 99.99999; to 1 but certain elements that transitioned scale above 1 on some hover effects would show the zoom breathing. So 99.99999 to 99.99998 was what worked for me to make the effect invisible.
Slightly hacky solution that could present performance issues for very long pages, but maybe not, because the browser should only be rendering what's onscreen. The pages I used this on are graphically heavy with a lot of complex multi-layer effects, and this doesn't seem to have a noticeable performance impact.
Seems like many mobile browsers have excessively optimized rendering, which leads to quirky failures with few well documented fixes. Forcing repaints was the only working fix I found.
I tried other, slightly less aggressive, documented methods of forcing repaints. Like adding some text to the page (invisibly) after scrolling stops for 200ms, and such. Nothing worked though, thus my animate-the-entire-page-forever hack.
In your case, some of those other hacks may work better. This article outline all the various things that cause repaints/reflows so you could try doing some of these things via script.
Light way to fix an issue:
Firstly, a tiny js-code*:
function defineVisibleHeight (){
const h = window.innerHeight * 0.01;
document. documentElement.style.setProperty('--vh', `${h}px`);
}
defineVisibleHeight();
window. onresize = defineVisibleHeight; // not mandatory;
*code sample has extra whitespaces to prevent plain copy/paste
With a previous code we found out a true visible area of a screen and stored it as variable for stylesheet;
Finally, css:
.wrapper{
height: calc(var(--vh, 1vh) * 100);
}
I am trying some CSS3 keyframe animations in a web app I'm developing. I have tried both my own custom animations and some provided by animate.css. The result is the same: the animation works fine, but after a while, even if I leave the browser alone, the CPU usage goes up to around 100% and stays there. Killing that process kills the web app, removing the CSS animations gets rid of the problem, so there is no doubt what's causing it. I am mainly using Chrome, and that's where I have seen the problem so far.
Target platforms for the web app includes iOS and Android via Phonegap, Windows and OSX via node-webkit. The problems I describe do not seem to occur in mobile Safari, but what about other platforms? Is this a general problem with keyframe animations? Are there any tricks to avoid this?
EDIT: Added demo link, but unable to replicate the problem. In the full app I am using a number of libraries and frameworks, like AngularJS, Angular-UI, Angular-UI-router, Fastclick, Animate.css, etc. I guess they might interfere somehow? Hard to say, but removing my animations from the app also removed the delayed 100% CPU usage.
HTML:
<button id="start">Start</button> <button id="reset">Reset</button>
<br/>
<div id="ball" class="ball"></div>
Javascript:
document.getElementById('start').addEventListener('click', function(e) {
document.getElementById('ball').classList.add('remove');
});
document.getElementById('reset').addEventListener('click', function(e) {
document.getElementById('ball').classList.remove('remove');
});
CSS:
.ball {
width:100px;
height:100px;
border-radius:100px;
background-color:darkred;
position:absolute;
top:100px;
left:200px;
}
#-webkit-keyframes slide {
from { top:100px; left:200px; }
to { top:100px; left:-100px; }
}
.remove {
animation: slide 1s linear;
-webkit-animation: slide 1s linear;
-webkit-animation-iteration-count: 1;
-webkit-animation-fill-mode: forwards;
}
The cause of the 100% CPU usage is due to the -webkit-animation-fill-mode: forwards declaration, if my suspicion that you're doing your primary testing on Chrome is correct.
It looks like it's a current bug with Chrome (tested on version 30.0.1599.101), according to an article by Wercker and my own research:
For this animation we use animation keyframes and the
-webkit-animation-fill-mode: forwards; so that the sidebar retains its position from the last frame of the animation. An animation normally
would snap back to its original setting after it is finished playing.
This is great for us, but not for Chrome. The CSS animations using
-webkit-animation-fill-mode: forwards; are causing the 100% CPU load bug. It actually only occurs when the CSS animation ends and the tab
is inactive.
This seems to be corroborated by this issue on the Chromium project page.
As Lindsey Bateman says in the article, the bug is fixed in Chrome Beta and Chrome Canary (33.0.1706.0 canary), so we shouldn't be waiting for very long to get this into current Chrome.
If you're using 3d (eg even if not, some recommend tricking CSS into 3d to let the GPU help render), try this trick via mddw
translateZ(0) /* only needed if no CSS-3d commands are used to toggle GPU rendering */
-webkit-backface-visibility: hidden;
-webkit-perspective: 1000;
backface-visibility: hidden;
perspective: 1000;
I got rid of the problem by disabling all extensions. It remains to be investigated which one was responsible. These are the suspects:
AngularJS Batarang 0.4.3
avast! Online Security 8.0.1500
ColorZilla 0.5.4
HootSuite Hootlet 4.0.10
iGetter 2.9.2
IntelĀ® XDK 2.6.1
Ripple Emulator (Beta) 0.9.15
Xmarks Bookmark Sync