Dig into some ways to make JavaScript animations smooth

  • 2020-06-22 23:46:43
  • OfStack

How is it possible that Javascript-based animations are as stealthily as CSS transitions, or even faster? How is it possible that the rich media mobile sites that Adobe and Google continue to publish can perform as well as native apps?

This article takes a one-by-one look at DOM animation libraries based on Javascript, such as ES9en.js and GSAP, to see how they perform better than jQuery and CSS animations.
jQuery

Let's start with the basics: JavaScript and jQuery are mistakenly mixed into 1. JavaScript animation is fast. jQuery slows it down. Why is that? Because -- as powerful as jQuery is -- being a powerful animation engine was never the design goal of jQuery:

jQuery doesn't avoid layout bumps, thanks to the fact that its code base provides multiple USES beyond animation. The memory consumption of the jQuery often triggers garbage collection, which periodically freezes the animation. jQuery USES setInterval instead of requestAnimationFrame (RAF) to protect the new technology from itself.

It should be noted that layout bumps are the primary cause of the animation's unsmoothness, garbage collection is the primary cause of the animation's unsmoothness, and failure to use RAF results in low frame rates.

Implementation examples

DOM query and update combinations to avoid layout bumps:


var currentTop,
  currentLeft;
 
/* With layout thrashing. */
currentTop = element.style.top; /* QUERY */
element.style.top = currentTop + 1; /* UPDATE */
 
currentLeft = element.style.left; /* QUERY */
element.style.left = currentLeft + 1; /* UPDATE */
 
/* Without layout thrashing. */
currentTop = element.style.top; /* QUERY */
currentLeft = element.style.left; /* QUERY */
 
element.style.top = currentTop + 1; /* UPDATE */
element.style.left = currentLeft + 1; /* UPDATE */

Queries that occur after an update force the browser to recalculate the computed data of the page (taking into account the effect of the new update). This incurs significant overhead for the animation, which is a tiny 16-millisecond runtime timeout.

Similarly, implementing RAF does not have to be a significant rework of your existing code base.


var startingTop = 0;
 
/* setInterval: Runs every 16ms to achieve 60fps (1000ms/60 ~= 16ms). */
setInterval(function() {
  /* Since this ticks 60 times a second, we divide the top property's increment of 1 unit per 1 second by 60. */
  element.style.top = (startingTop += 1/60);
}, 16);
 
/* requestAnimationFrame: Attempts to run at 60fps based on whether the browser is in an optimal state. */
function tick () {
  element.style.top = (startingTop += 1/60);
}
 
window.requestAnimationFrame(tick);

RAF provides the greatest possible boost to animation performance, allowing you to make single 1 changes to your code.

CSS conversion

The CSS transformation, which goes beyond jQuery by dumping animation logic on the browser itself, is effective in (1) optimizing DOM interaction and memory consumption to avoid delays (bumps), (2) leveraging the engine's RAF principles, and (3) forcing hardware acceleration (leveraging GPU's capabilities to improve animation performance).

The reality, however, is that these optimizations can also be performed directly in JavaScript. GSAP has been doing this for years. Velocity. js, a new animation engine, not only USES the same technology, but also takes a few more steps forward -- which we'll discuss shortly.

Facing the fact that JavaScript animation can compete with CSS conversion is only the first step in our rehabilitation plan. Step 2 is to implement "JavaScript animations can actually convert faster than CSS".


Now let's talk about the weaknesses of the CSS transform:

transition forced hardware acceleration will increase THE CONSUMPTION of GPU and will lead to unsmooth operation under high load. This is especially true for mobile devices. (Special cases, such as bottlenecks when data is passed between the browser's main thread and the typesetting thread, can also lead to poor flow). Some CSS attributes, such as transform and opacity, are not affected by these bottlenecks. Adobe summarizes these issues in detail here. transition did not work below IE10, and the desktop site usability problems since IE8 and IE9 are still widespread. Since transition is not controlled native to JavaScript (it is only triggered by JavaScript), the browser has no way of knowing how to optimize these transition in sync with the JavaScript code that controls them.

In contrast, JavaScript-based animation libraries can decide for themselves which hardware to turn on. They have native support for various versions of the IE browser, and they are particularly well suited for batch animation optimization.

My advice is to use native CSS transformations only if you are developing solely for mobile and only implementing simple animations. In this environment, transition is a native and efficient solution that lets you implement all the animation logic in your stylesheets without having to add additional JavaScript libraries to keep your pages from becoming bloated. However, when you are designing complex UI, or developing App with different states of UI, you should use an animation library to keep the animation flowing and the workflow manageable. Transit is a library that does a particularly good job of managing CSS transformations.

JavaScript animation

Ok, so JavaScript has the upper hand in terms of performance. But how much faster is Javascript? Well - initially - it's fast enough to build a real 3D animation example, usually you'll only see using WebGL in the build. It's also good enough to build a multimedia mini-animation, usually you'll see only using Flash or After Effects.

For a direct comparison of the leading animation libraries, including Transit (which USES CSS gradients), go back to the Velocity documentation on ES127en.org.

The question remains: how exactly does JavaScript achieve its high level of performance? Here is a short list of optimizations that Javascript animations can perform for this 1 goal:

Synchronize DOM → push throughout the animation chain to minimize layout jitter. Cache property values for the entire chain call to minimize the occurrence of DOM queries (these are the pits of high performance DOM animations). Cache the unit conversion rate for the entire level of elements in the same call (px to %, em, etc.). Skip style updates when updates may not be visually visible.


Recalling what we learned earlier about layout bumps, ES141en.js USES these best practices to cache the end value of an animation to reuse as the start value of a subsequent animation, thereby avoiding the need to re-query DOM for the start value of an element:


$element
  /* Slide the element down into view. */
  .velocity({ opacity: 1, top: "50%" })
  /* After a delay of 1000ms, slide the element out of view. */
  .velocity({ opacity: 0, top: "-50%" }, { delay: 1000 });

In the above example, the second Velocity call knows that it should automatically start with opacity at 1 and top at 50%.

The browser itself will eventually be able to perform many of these same optimizations, but doing so will significantly reduce the number of ways in which developers can animate code. Therefore, by the same token, since jQuery does not use RAF (as described above), the browser does not force it to be optimized, even giving a small chance of breaking the specification or deviating from the expected behavior.

Finally, we compare the two JavaScript animation libraries (ES159en.js and GSAP) with each other for 1 second.


GSAP is the first animation library used to demonstrate the impressive animation performance of JavaScript DOM. It is, but with some drawbacks:

In medium to high load animations, the DOM interaction overhead of GSAP causes the animation to be out of frame at the beginning and during the animation. In contrast to ES171en. js, which was released under the ultra-loose MIT license, GSAP is closed-source and requires an annual license fee for many types of commercial use. GSAP is a complete animation suite, three times the size of Velocity. However, the GSAP has such rich features that it helps to become the animated Swiss Army Knife.

I recommend GSAP when you need precise control over timing (such as redraw, pause/resume) and motion (such as Bessel curve paths). These features are critical in game development and some specific applications, but they are generally not needed in UI for web applications.

Velocity.js

Citing the rich features of GSAP does not mean that Velocity itself is lightweight in terms of features. On the other hand, in the compressed 7kb, Velocity not only copies all the features of jQuery $.animate (), it also packs in color animation, conversion, loops, easing effects, class animation, and scrolling.

In summary, Velocity is the best combination of jQuery, jQuery UI, and CSS gradients.

In addition, from a convenience point of view, Velocity USES jQuery's $.queue () method under hood (cover), which allows seamless interoperability with jQuery's $.animate (), $.fade (), and $.delay () functions, and since Velocity has the same syntax as $.animate (), you don't need to change any code on the page.

Let's take a quick look at 1. Velocity.js. At the fundamental level, Velocity behaves the same as $.animate ()1:


 
$element
  .delay(1000)
  /* Use Velocity to animate the element's top property over a duration of 2000ms. */
  .velocity({ top: "50%" }, 2000)
  /* Use a standard jQuery method to fade the element out once Velocity is done animating top. */
  .fadeOut(1000);

At its most advanced level, it is possible to create complex scrolling scenes with 3D animation - almost in two simple lines of code:


$element
  /* Scroll the browser to the top of this element over a duration of 1000ms. */
  .velocity("scroll", 1000)
  /* Then rotate the element around its Y axis by 360 degrees. */
  .velocity({ rotateY: "360deg" }, 1000);


Related articles: