An experiment with CSS Animations
If you want performant animations, you go with CSS Transforms.– almost everyone, 2019
This is an experiment with CSS Animations that you can try out yourself at https://jayant.dev/experiments/animation-comparisons.
CSS Transforms are more performant than animating non-transform properties (like
height, etc.), that’s what most people say. This has been understood since a looong while now, and when I looked up articles on this, I found results from as far back as 2012 on this.
I personally thought the answer to this was simple, straight-forward, and limited to the fact that Transforms can leverage hardware-acceleration and offer subpixel rendering.
That means, that if you have a discrete GPU, your browser may leverage that to offer smoother animations so your CPU isn’t doing all the heavy lifting, and the subpixel rendering part means that you can have something rendered at say 2.5px. Of course you can’t render pixels between two physical pixels, but the linked wikipedia article tells you what it means (basically anti-aliasing), and it actually works pretty effectively.
Having subpixel rendering is great! Because when you’re adding small and subtle animations, like a button scaling up by like 5% on mouse-over, for most buttons of normal sizes this would result in a visible dimension change of a few pixels and some fractions. Subpixel rendering will make this little animation look noticeably more polished, than not having it.
Here’s an example of Subpixel Rendering at play:
Sorry about the frame-rate of the GIF being not at 60, but you can tell that the Transforms one is noticeably smoother, while the Height/Width one is not exactly choppy, but seems… steppy? You know what I mean. Feels like you’re running your fingers over gravel in the second one. You know what I mean. The first is what subpixel rendering gets you.
Now, this will largely work for finer/smaller-scale animations such as this. What about animations that take place over more of your screen space, are performed faster, etc.? In those you can’t really tell if subpixel rendering is doing anything. So is there no difference there then?
Nope, there is a difference, and a substantial one at that.
Let your GPU do what it’s supposed to do.
I’ve set up an experiment to render and animate 200 elements in three different ways.
- Framer Motion
This one is here just as an extra. In real-world use-cases, this isn’t why you’d use an animation library, but you’d use one because it makes complex animation orchestration and controlling far easier.
Let’s dive into demonstrations to see how much of a difference hardware acceleration makes.
(Ignore the choppiness in the GIFs and look at the FPS counter)
This doesn’t look too bad, right? Consistently over 50fps, not bad I guess!
That’s what I’m talking about. On my hardware, it’s a smooth, unadulterated, 60fps. It’s crazy!
Bonus: Framer Motion
Well, let’s just remember that this is not why you’d use something like Framer Motion in the first place.
This is all fine and good, but this was on MY hardware, and my laptop is no slouch by any means (not that it’s rocking an RTX 2080 Super), but it has a pretty solid set of hardware inside it (Core i7 and all), and this doesn’t tell me how most of my users would feel when running this experiment. Thankfully, Chrome has CPU throttling since v61, so lets throttle that to the max and see how it looks like.
Well, this is around 30fps now. That’s quite a drop isn’t it?
Look at that! It’s still at full 60! I expected this, yet I’m still amazed.
This basically halved its number of frames too.
What do you take away from this?
Your approach to animating something depends on. your use-case. Just because CSS Transforms offer great frame-rates even on weaker hardware, doesn’t mean that’s the best choice for every situation. In some situations, Transforms may not even solve the problem you’re trying to solve. But, when you are solving a problem that requires animations, you’ll know which ones to consider first.
Most kinds of basic or even keyframed animations can probably be done using Transforms.
Animations like collapsing accordions will probably limit you to using
width based animations depending on how you want the content to react. Transforms affect the visual output of the entire element regardless of what’s inside it, and you may not find that suitable for every use-case.
But what if you have something much fancier?
For example: A little hamburger icon, which when clicked animates into maybe a
X icon, and a sidebar animates into existence from a sphere silhouette of that hamburger icon, and then its children animate in in a staggered fashion.
You can do this with Transforms, or other CSS-only animations, but that’s a lot of carefully orchestrated code you have to write and maintain, that’s not going to be super easy to understand, not without much effort anyway. This is where animation libraries like Framer Motion, Anime.js, React Spring, etc. shine.