Keeping animation smoothly with CSS3

Keeping animation smoothly with CSS3

All of the major browsers today supports CSS3 rendering on GPU’s which helps a lot to keep high framerate. But availability of hardware power isn’t always enought. We still need to follow few key rules to prevent gliches and lags. Let’s take a look of it.

How we will measure animation performance?

I don’t know what is your favorite browser, but in this case, it doesn’t matter. Why? Because only one of them contain needed tool (if I’m wrong, please let me know about it in comments). And yes, it’s Chrome with Timline tool. How we will use it? Firstly, open dev tools (Windows – F12, Mac – Alt + Cmd + I ) and navigate to the “Timeline” tab.  From list on the left select “Frames” – voilà. We also can enable FPS counter, which is another helpfull feature. To do that, open dev tools settings (icon in right-bottom corner), in “General” section find “Rendering” paragraph and check two options “Force accelerated compositing” and after that “Show FPS meter”.

Keep in mind to set absolute position on moving element

It’s one of the most important tricks. If you animating your element, doesn’t matter if it’s div or other, best way to keep good performance is to set position to absolute (relative should be on parent container). Just look at difference between two examples. In first, element which is animated, has property position set to relative. This force many repaints, even nodes that are outside moving element need to be recalculated, resized (if are images) and redrawn. Now open second example. Animation of scrolling is much smoother, right? Yep, position absolute do the magic. To see what happen and recognize where is the problem open window with first example and after that open Timeline tab in dev tools, change to Frames part and click on record icon to start capture rendering processes.


Back to window with example and scroll mouse wheel above container with emulated scrolling. On timeline (click same icon to stop recording) you should see something like that:


Repeat same actions for new opened tab with second example. What we’ve got?


It’s obvious that scrolling in second example is much smoother. Why? Where is the problem? Just take a look at timeline. In first case, scrolling forced recalculation and repainting on whole document, but in second painting time was equal nearly nothing and none of images was resized. So remember, animated element === position: absolute;

Replace not scaled images in scrolled containers by canvas

I know, it’s sounds a little weird… But it works, especially in WebKit based browsers in which some repainting of image force resizing (even cached takes a lot of ms). So case is simply, we resizing each of photo to proper width and height only once, on start and after that we put it into the canvas. We can feel the difference by playing with this two examples, first, with plain images, second with images replaced by canvas.

Keep depth of animating element on low level

The bigger number of nodes in animated element is equal higher execution time. So keep your moving container as simply as you can. If you can’t, try to think about caching strategy. For example display only visible elements, rest of them remove or append only if you need it. Some small DOM manipulating still will be less time consuming than recalculating and repainting of whole document.

Use transform instead of top/left and transitions/keyframes where you can

Another important tip is to use transform (translate/translate3d) instead of top/left property. The reason of it is because transform use hardware acceleration when top/left are calculated in main thread on CPU. The biggest impact of this solution is when you control your animation with transitions/keyframes, unfortunately if you are using requestAnimationFrame you will not see big performance difference. But in this particular case we have another advantage of using translate, animation is calculated on subpixels, so it’s a lot more accurate than top/left property. Example here.


Leave a Reply

Please fill all required fields

Drag circle to the rectangle on the right!