In order to achieve smooth transitions and animations, a browser needs to avoid doing extra work on its main thread, the part that’s in charge of handling tasks like JavaScript, style calculations, layout, painting and compositing (more on these later). Fortunately, modern browsers are able to offload certain tasks from the main thread by delegating them to the GPU to handle compositing.
This optimized compositing process is where all the individual textures of a website are uploaded to the GPU then drawn out to create the final picture we see on our screen; it all happens behind the scenes before a single frame of content is presented.
In this article, we’re going to focus on the rendering performance of animations and transitions, as they can be one of the biggest offenders in performance bottlenecks.
Contents
How Animations and Transitions Affect Performance
When we animate an element, the browser has to re-evaluate the page — frame by frame — to determine if and how the element has affected the flow of nearby elements and the overall page, then recalculate the positions and geometries of those elements. This process is called reflow.
Free trial on Treehouse: Are you interested in learning more about animations and transitions? Click here to try a free trial on Treehouse.
The browser also looks for areas that need to be re-repainted due to changes in an element’s color or background properties. Every time a repaint or reflow happens, the browser has to composite those areas all over again. These reflows and repaints can be “expensive,” making our animations and transitions appear janky.
The key to great animation performance is keeping the number of areas being reflowed and repainted as low as possible. But how do we know if the browser is doing more repainting and reflowing than it needs to?
Testing Animations and Transitions
It’s always important to test the performance of an animation or transition in order to see exactly how much work the browser is doing. Using the Timeline panel and rendering settings of Chrome DevTools, we’ll test four transition/animation methods to determine which one will always give us the smoothest results.
The goal is to keep the browser’s paint times as low as possible and frame rates at or near 60 frames per second (FPS).
Note: To learn more about Chrome DevTools, watch our course on Website Optimization.
Top/Left Margin Transition
We’ll start with a test that transitions the top and left margins of the logo:
Results: The average paint time is somewhere around 19ms, but the frame rates are dropping down to nearly 30fps at times, which usually results in janky transitions. Animating or transitioning properties like margin and padding cause reflows since they’re animated on the browser’s main thread, so it’s best to avoid animating or transitioning margins.
Top/Left Offset Animation
Next, we’ll position the logo absolute, then animate its top and left position with a keyframe animation sequence.
Results: The paint times are higher, averaging 23ms, and the frame rates are still much lower than 60fps.
It’s also worth pointing out that absolutely-positioned elements will not cause reflows on other elements. They’re separated from the normal document flow, which minimizes the reflow calculations done by the browsers.
Transform, with Translate3D
Let’s see what happens when we transition the logo using the translate3d
CSS transform function.
Results: Once the transition starts, the paint times are at 0.0ms and the frame rates are steadily above 30fps — that’s exactly what we want! Notice the outline around the logo at 00:13. That indicates it’s being composited on a separate layer by the GPU, making paint times and frame rates faster.
When an element is animated or transitioned on a separate GPU layer, everything on that layer moves as one big block of pixels, so the browser doesn’t need to calculate every single pixel on every frame.
Other CSS properties that trigger that type of acceleration are translateZ()
and opacity
.
What About jQuery Animations?
Many jQuery effects, like the .animate()
method, are also commonly used to animate elements. Let’s see what happens when we run a similar animation using jQuery.
Results: The paint time average is ~20ms — on par with the “main thread” CSS transition and animation. But the frame rates are really low, dropping below 30fps at times — notice the spikes in FPS (big yellow bars) once the hover event is fired and the animation starts.
A disadvantage to using jQuery (or pure JavaScript) for animations is that the browser is not able to optimize them like it can with CSS. The animations run on the browser’s main thread, which increases the chances of skipping animation frames.
But we shouldn’t entirely write-off JavaScript for animations. The real power behind using JavaScript is that it gives us more control over animations. For example, we can start, pause, reverse or cancel an animation sequence.
With CSS, once we start an animation or transition, there’s no way to stop them. But because they have a definitive start and stop point, the browser can optimize them with more ease. So depending on your animation, it might be best to use a combination of both: JavaScript to trigger a CSS animation or transition.
Conclusion
Although we don’t have complete control over CSS animations and transitions, the main benefit to using them is that the browser can optimize them by compositing them on a separate layer. However, forcing too many compositing layers with translateZ()
or translate3d()
may also result in poor rendering performance.
For example, giving every child element in an animation transform: translateZ(0)
, when adding it to the parent element is all that’s necessary. Too many layers force longer composite times, which leads to choppy animations.
Animating or transitioning properties like margin, padding, border, position, font-size — even width and height will affect layout and cause reflows. And properties like visibility, border-style, border-radius and background will affect painting.
So it’s always best to animate or transition the two properties that affect compositing: transform
and opacity
. Doing so prevents the browser from having to do a repaint or reflow on every frame and, because the site’s textures are already on the GPU, the browser does less work to recomposite and draw pixels to the screen.
So,when it comes to choosing CSS vs GSAP for animation,what criteria should we consider for taking a decision?Are the cases where should we choose CSS over GSAP and vice versa.
Your detailed blog is very attractive and useful. It really helps other to get such information. Keep doing work. Let me know any new and interesting stuff, will put a trackback to this post!
thanks, useful information
Actually, CSS animations CAN be paused, using -animation-play-state: https://developer.mozilla.org/en-US/docs/Web/CSS/animation-play-state. But Jack’s right, there are many other downsides to going CSS-only, most notably for me, not being able to “seek”. I’ve used Jack’s GSAP on some projects where I needed these features and I can vouch for it’s awesomeness. Timelines FTW!
CSS transitions can actually be slower than JavaScript in many cases (even transforms), and there are several other down sides to using CSS that few people talk about, as discussed in this article: http://css-tricks.com/myth-busting-css-animations-vs-javascript/. Might be worth a read.
While ubiquitous, jQuery is probably the slowest JavaScript library for animation, so if the goal is to get smooth animations it may be worth considering other options. Of course I recommend GreenSock’s GSAP which is literally up to 20x faster and far more capable, but I’m completely biased.
Thanks for helping folks get smoother animations. The web needs that. Cheers!
Great post Guil!
Guil Hernandez you have done a deep research and share this good informative article for designers Keep going the Good work .