Optimizing Complex Animations: Tips and Tricks

Animation optimization tips - illustration by Inna K

Animations become more and more complex every year, which affects their performance. The more objects are involved, the greater the load, resulting in lags and compromised smoothness.  The more we animate, the more there’s a need to do it the most fitting way without overloading the processor. 
In this article, we’ll show you 11 ways to improve the performance of your animations using the popular GreenSock library as an example. You can also use most of the tips with other animation libraries as well.

#1 Use will-change for complex transformations

The will-change CSS property lets browsers know in advance which element is going to be changed and in what way so that they can set up their optimizations before the element actually gets changed. These kinds of optimizations can improve page responsiveness by doing potentially expensive work before it’s needed.
To apply it, you only need to indicate what element parameters will change:

will-change: auto;
will-change: scroll-position;
will-change: contents;
will-change: transform;        /* Example of <custom-ident> */
will-change: opacity;          /* Example of <custom-ident> */
will-change: left, top;

Important: the will-change property is not for frequent use but rather as a last resort to try and resolve existing performance issues. Don’t use it “just in case”. The thing is, the default browser itself should normally perform optimization. If it doesn’t cope, then you can use will-change. Otherwise, if you use it many times on one page, the browser will not understand what to do first, since there’ll be a lot of things to optimize at once.

#2 Use the stagger parameter when animating a lot of elements at the same time

The stagger parameter starts your animations at the specified time interval. Even the smallest playback delay greatly affects the animation, making it slow and twitchy. 
Usage example: gsap.to(".item", {opacity:0, stagger: 0.01}); instead of gsap.to(".item", {opacity:0});.  
For example, let’s create 1000 elements and try to animate them simultaneously:

See the Pen
Greensock without stagger example
by Lokolin (@lokolin)
on CodePen.

and using the stagger parameter:

See the Pen
Greensock stagger example
by Lokolin (@lokolin)
on CodePen.

In the first example, the blocks don’t fall smoothly because those 1000 objects are simultaneously animated at once.

#3 Refrain from left/top CSS property transformations

When you animate the left/top properties, it triggers the CSS layout recalculation which takes up quite a lot of resources, since it happens in the main thread of the HTML page. While the X and Y transformation uses the GPU and does not recalculate the layout. 
For example, instead of gsap.to ("# item", {left: "150px", top: "150px", duration: 3,}); it’s better to use gsap.to ("# item", {x: "150px", y: "150px", duration: 3,}).

#4 If possible, better use from() or to() methods instead of fromTo()

We recommend not to use fromTo() often to not force the processor to make additional calculations of the initial state of the animation. Better set the initial/final state of the element in HTML/CSS, and use the GSAP library to set the final/initial state transformation. 
In the first example, we use the fromTo() method and set the initial and final state of gsap.fromTo (". Box", {opacity: 0}, {opacity: 1, duration: 2}) animation in GSAP:

See the Pen
Greensock fromTo() example
by Lokolin (@lokolin)
on CodePen.

In the second example, we set only the final state of the animation using gsap.to (".box", {opacity: 1, duration: 2}), and the initial one is specified in the CSS class .box opacity:0:

See the Pen
Greensock to() example
by Lokolin (@lokolin)
on CodePen.

#5 Don’t forget to remove animations with kill()

Calling animation.kill() immediately stops the animation, removes it from the timeline, and places it in the garbage collection, unloading the processor.
Tip: don’t delete animations that you are going to use in the future. Use pause() for those.

#6 Try to avoid width/height transformations

It’s best to use scaleX/scaleY for resizing where possible. As with the left/top case, when you animate the width/height properties it recalculates the layout, while scaleX and scaleY transformation uses the GPU and doesn’t trigger the layout recalculations. 
For example, if you need to hover an element, instead of changing the width/height:

See the Pen
Greensock width/height change
by Lokolin (@lokolin)
on CodePen.

use scaleX/scaleY:

See the Pen
Greensock scaleX/scaleY change
by Lokolin (@lokolin)
on CodePen.

#7 Try to avoid mix-blend-mode and other blending filters

They are too resource-intensive for optimized animations. Take a look at this example:

See the Pen
GSAP mix-blend-mode example
by Lokolin (@lokolin)
on CodePen.

#8 Don’t animate too many SVGs

SVGs are good for most animations because of the image quality, but if you have a lot of SVG images at once they can have a significant impact on the performance of your animations.
There’s no exact number though. It depends not only on the quantity but also on the weight of each piece. A lot of 16×16 px images don’t affect the performance as much as a single 800×800 px complex one could.

#9 Pay attention to what CSS properties you are animating

By animating the CSS property, you resize the element, which entails recalculating the Layout, redrawing the Paint, and composing the Composite. You can learn more about CSS properties on CSS Triggers website.

#10 Set the gsap.ticker.lagSmoothing() method

gsap.ticker.lagSmoothing(1000, 16); lets you avoid animation lags when the CPU starts to freeze. 
The first parameter is the threshold (in milliseconds). When the lag becomes greater than this threshold, the engine starts the internal delay clock. As a result, there are no lags at the beginning of the animation.

#11 Set the maximum number of FPS

In video game development, you may have come across a trick of limiting frames per second (FPS). Thus, the animation runs smoothly. Try doing it by adding gsap.ticker.fps(30).

Bonus: Several tips on using the MorphSVGPlugin with GreenSock library

When animating, this plugin allows you to transform the initial state of the first SVG picture into the final state of the second SVG picture. Note that fine-tuned transformation of the initial coordinates into the final coordinates requires a lot of calculations, so here are some tips to help you simplify the calculations:

  • Calculate and manually set the shapeIndex (the number of points by which the transformation will be performed). You can calculate the optimal number in advance using shapeIndex: "log" and then substitute this value, so the plugin does not calculate this number in real-time, but will immediately know how many shapes it has to transform.
  • Choose a simpler SVG map redrawing algorithm: "size" | "position" | "complexity", where complexity is the fastest way.
  • Precompile. If the animation does not change depending on user behavior, then you can use precompile:"log".
gsap.to("#id", 1, {morphSVG:{shape:"#otherID", precompile:"log"}});
//now you can grab the value from the console and drop it in...
gsap.to("#id", 1, {morphSVG:{shape:"#otherID", precompile:["M0,0 C100,200 120,500 300,145 34,245 560,46","M0,0 C200,300 100,400 230,400 100,456 400,300"]}});
*  *  *

Written by Dmitry Evsukov

July 12, 2021

Subscribe to Our Blog

Once a month we will send you blog updates