Making the web fast: Reaching 60FPS
Yesterday I shared a teaser of a side project I’m working on. I was optimizing animations to run at 60 frames per second, when I found some interesting tricks to achieve it. In this article, I’ll answer some questions about it.
Before we continue: this wasn’t Preact X, WebGL, Flutter or some other magic. It was CSS.
The primary animation we’ll focus on is opening the menu which contains four separate parts that transition all at once:
- The menu icon has a three elements that scale, rotate and move while the container itself also rotates.
- The surface moves down based on the height of the backdrop.
- The content in the surface moves to a lower opacity to shift focus to the backdrop.
- The backdrop becomes visible by going to full opacity.
At first I was happy when everything worked. It looked fine, but didn’t feel quite as smooth as I wanted it to be. In these cases it’s important to test on lower-end hardware (like my 70$ android one device) as high-end laptops and phones are not the average.
To improve the performance I enabled Paint flashing in Chrome DevTools. This allows you to see when specific elements are (re)rendered.
In this case it didn’t feel like elements had to re-render as they just moved around or became visible through opacity. Here’s how I worked around it:
Tip 1: Make sure every transform happens on its own GPU layer.
When using a transform, in my case translate and rotation, the elements can be placed on their own GPU layer. For this we can add will-change: transform; to our elements.
Be careful with this feature as applying it to too many elements can actually make your experience slower. It’s best to use it when an element is about to transition.
Tip 2: Transition opacity without the paint step.
Two of the four elements transition their opacity: from 0 to 1 and 1 to 0.4. With Paint flashing enabled it became noticeable that when opacity reaches 1, paint happens. The optimization here is to not move it to that point: go to a maximum of 0.99 and paint won’t happen.
When a transition to 1 opacity is done, paint happens at the end so it’s not that bad. However, when you go back to a lower opacity the paint happens at the start.
Tip 3: Transition values consistently.
Often when I use modals they are fixed to the bottom center and moved out of view by translating y by 100%. When needed, I toggle that value to 0% which will make it completely visible regardless of height.
For the surface element it was different as it moves a fixed amount of pixels based on the height of the content in the backdrop.
The transition from 0% to 168px worked, but it didn’t appear as smooth as they usually are. When that value switched over to pixels instead of percentages it improved (and also made more sense to me).
Transition pixels to pixels and percentages to percentages!
The web can be really fast and small animations on interaction, like the one described above, can improve the overall quality of your experience.
Optimizing the speed of your site in general and its animations is always a good idea! These tips might not make everything fast, but can be used to enhance the performance. Please share any performance tips you have too!