深入理解JS和CSS3动画性能问题和技术选择

iefreer 发表于 2016-11-13 12:38:23

标签: animation, js, css3, performance

- +

本文对比了JS及其框架和CSS3的动画性能,并深入剖析了其内在原因。

技术结论大致如下:

1. jQuery出于设计原因,在动画性能上表现最差

2. CSS3由于把动画逻辑推给了浏览器,优化了内存消耗、DOM操作和默认利用了RAF,所以要比jQuery动画性能更好

3. CSS3可能会引起浏览器主线程和复合器线程之间过度数据交互,从而导致性能下降

4. 纯JS实现的动画,在利用RAF和注意布局摆动处理时,可以获得媲美CSS3的动画性能,而在浏览器兼容性上比CSS3更好

应用选型建议:

1. 对于简单页面动画,建议优先选择CSS3动画

2. 对于复杂动画,建议使用GSAPVelocity

原文链接:https://davidwalsh.name/css-js-animation

jQuery

Let's start with the basics: JavaScript and jQuery are falsely conflated. JavaScript animation is fast. jQuery slows it down. Why? Because — despite jQuery being tremendously powerful — it was never jQuery's design goal to be a performant animation engine:

Note that layout thrashing is what causes stuttering at the start of animations, garbage collection is what causes stuttering during animations, and the absence of RAF is what generally produces low frame rates.

Implementation Examples

Avoiding layout thrashing consists of simply batching together DOM queries and DOM updates:

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 take place after an update force the browser to recalculate the page's computed style data (while taking the new update's effects into consideration). This produces significant overhead for animations that are running over tiny intervals of just 16ms.

Similarly, implementing RAF doesn't necessitate a significant reworking of your existing codebase. Let's compare the basic implementation of RAF against that of setInterval:

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 produces the biggest possible boost to animation performance that you could make with a single change to your code.

CSS Transitions

CSS transitions outperform jQuery by offloading animation logic to the browser itself, which is efficient at 1) optimizing DOM interaction and memory consumption to avoid stuttering, 2) leveraging the principles of RAF under the hood and 3) forcing hardware acceleration (leveraging the power of the GPU to improve animation performance).

The reality, however, is that these optimizations can also be performed directly within JavaScript. GSAP has been doing it for years. Velocity.js, a new animation engine, not only leverages these same techniques but also goes several steps beyond -- as we'll explore shortly.

Coming to terms with the fact that JavaScript animation can rival CSS animation libraries is only step one in our rehab program. Step two is realizing that JavaScript animation can actually be faster than them.

Let's start by examining the weaknesses of CSS animation libraries:

  • Transitions' forced hardware acceleration taxes GPU's, resulting in stuttering and banding in high-stress situations. These effects are exacerbated on mobile devices. (Specifically, the stuttering is a result of the overhead that occurs when data is transferred between the browser's main thread and its compositor thread. Some CSS properties, like transforms and opacity, are immune to this overhead.) Adobe elaborates on this issue here.

  • Transitions do not work below Internet Explorer 10, causing accessibility problems for desktop sites since IE8 and IE9 remain very popular.

  • Because transitions aren't natively controlled by JavaScript (they are merely triggered by JavaScript), the browser does not know how to optimize transitions in sync with the JavaScript code that manipulates them.

Conversely: JavaScript-based animation libraries can decide for themselves when to enable hardware acceleration, they inherently work across all versions of IE, and they're perfectly suited for batched animation optimizations.

My recommendation is to use raw CSS transitions when you're exclusively developing for mobile and your animations consist solely of simple state changes. In such circumstances, transitions are a performant and native solution that allow you to retain all animation logic inside your stylesheets and avoid bloating your page with JavaScript libraries. However, if you're designing intricate UI flourishes or are developing an app with a stateful UI, always use an animation library so that your animations remain performant and your workflow remains manageable. One library in particular that does a fantastic job at managing CSS transitions is Transit.

JavaScript Animation

Okay, so JavaScript can have the upper hand when it comes to performance. But exactly how much faster can JavaScript be? Well — to start — fast enough to build an intense 3D animation demo that you typically only see built with WebGL. And fast enough to build amultimedia teaser that you typically only see built with Flash or After Effects. And fast enough to build a virtual world that you typically only see built with canvas.

To directly compare the performance of leading animation libraries, including Transit (which uses CSS transitions), head on over to Velocity's documentation at VelocityJS.org.

The question remains: How exactly does JavaScript reach its high levels of performance? Below is a short list of optimizations that JavaScript-based animation is capable of performing:

  • Synchronizing the DOM → tween stack across the entirety of the animation chain in order to minimize layout thrashing.

  • Caching property values across chained calls in order to minimize the occurrence of DOM querying (which is the Achilles' heel of performant DOM animation).

  • Caching unit conversion ratios (e.g. px to %, em, etc.) across sibling elements in the same call.

  • Skipping style updating when updates would be visually imperceptible.

Revisiting what we learned earlier about layout thrashing, Velocity.js leverages these best practices to cache the end values of an animation to be reused as the start values of the ensuing animation — thus avoiding requerying the DOM for the element's start values:

$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 an opacity value of 1 and a top value of 50%.

The browser could ultimately perform many of these same optimizations itself, but doing so would entail aggressively narrowing the ways in which animation code could be crafted by the developer. Accordingly, for the same reason that jQuery doesn't use RAF (see above), browsers would never impose optimizations that have even a tiny chance of breaking spec or deviating from expected behavior.

Finally, let's compare the two JavaScript animation libraries (Velocity.js and GSAP) against one another.

  • GSAP is a fast, richly-featured animation platform. Velocity is a lightweight tool for drastically improving UI animation performance and workflow.

  • GSAP requires a licensing fee for various types of businesses. Velocity is freely open-sourced via the ultra-permissive MIT license.

  • Performance-wise, GSAP and Velocity are indistinguishable in real-world projects.

My recommendation is to use GSAP when you require precise control over timing (e.g. remapping, pause/resume/seek), motion (e.g. bezier curve paths), or complex grouping/sequencing. These features are crucial for game development and certain niche applications, but are less common in web app UI's.

Velocity.js

Referencing GSAP's rich feature set is not to imply that Velocity itself is light on features. To the contrary. In just 7Kb when zipped, Velocity not only replicates all the functionality of jQuery's $.animate(), but it also packs in color animation, transforms, loops, easings, class animation, and scrolling.

In short, Velocity is the best of jQuery, jQuery UI, and CSS transitions combined.

Further, from a convenience viewpoint, Velocity uses jQuery's $.queue() method under the hood, and thus interoperates seamlessly with jQuery's $.animate()$.fade(), and $.delay()functions. And, since Velocity's syntax is identical to $.animate()'s, none of your page's code needs to change.

Let's take a quick look at Velocity.js. At a basic level, Velocity behaves identically to$.animate():

$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, complex scrolling scenes with 3D animations can be created — with merely 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);

Wrapping Up

Velocity's goal is to remain a leader in DOM animation performance and convenience. This article has focused on the former. Head on over to VelocityJS.org to learn more about the latter.

Before we conclude, remember that a performant UI is about more than just choosing the right animation library. The rest of your page should also be optimized. Learn more from these fantastic Google talks:


possitive(1) negative(0) views2803 comments0
私信 收藏 分享
分享到

发送私信

最新评论

请先 登录 再评论.
相关文章
  • 踏得网精选2016年度10大最佳HTML5动画

    踏得网精选2016年度最酷最新的HTML5动画集,评选标准为:创意新颖度+实现技术难度+趣味程度。使用一些在线H5生成工具的作品,因其主要使用图片和CSS3套路动画,...

  • WebGL Roadmap

    Unity 5.0 shipped with a working preview of our WebGL technology in March this year. Since then, Google has disabled (by default) NPAPI support in the...

  • CSS3人行走动作图解和动画实现

    对于人类而言,行走是一种很自然的想要前进并防止跌倒的一组动作重复。大部分人1岁就学会了走路,但至此以后的几十年间,或许我们从来没留意过自己行走姿势。当...

  • 函数式JavaScript编程基础概念:Curry和Partial Application

    本文介绍JS函数式编程中的两个概念:柯里(Curry)和部分应用程序(Partial Application)。什么是应用程序(Application)将函数应用于其参数以产生返回值的过...

  • 常见面试题JavaScript闭包(ES5语法)

    JavaScript闭包(Closure)是常见的JS面试题,是否理解闭包是一个简单的区分JS初级和高级程序员的判例。几乎每个JS程序员都在使用闭包,有意或无意间。比如编写一个jQuery鼠标点击处理函数:$(function()

  • HTML5、Hybrid APP、Native APP对比和技术选型

    HTML5和Native APP都很容易理解。为了获得HTML5的移植性和移动本地应用的高性能,搞出来一些混合APP的解决方案。比如Apache的Cordova(也就是以前的PhoneGap),...

  • Babylon.js入门教程和开发实例

    Babylon.js是一款WebGL开发框架。和Three.js类似。主要的技术区别是Three.js还试图回退兼容CSS 3D。Three.js是完全社区推动的,比Babylon.js要成熟些,而Babylon...

  • 深度贴图(depth map)概念简介和生成流程

    Depth map 深度图是一张2D图片,每个像素都记录了从视点(viewpoint)到遮挡物表面(遮挡物就是阴影生成物体)的距离,这些像素对应的顶点对于观察者而言是“可...

  • Three.js入门教程5 - 10个必须知道的编程技巧

    作者为Google的Paul,关于如何写出好的WebGL代码的文章。和很多开发者一样,我通过实践学习,但同时我也向其他更有经验的开发者们学习。在过去的几个月中,我在c...

  • 如何使用WebGL创建一个逼真的下雨动画

    之前写过文章来分别讲解如何使用CSS3和Canvas2D实现过雨滴和下雨动画。通过背景处理看起来也有视觉上的3D效果,但并非真正的3D场景,如果要加入用户交互,进行36...

  • 浏览器控制台报JS脚本执行错误:Module is not defined

    现在JS分成了两个分支,一部分在服务器端发展如NodeJS,一部分是传统的浏览器运行环境。
    有些插件在编写JS代码时,是针对Node编写的,所以直接在浏览器中使...

  • Processing.js和P5.js的功能简介和区别

    什么是ProcessingProcessing是关于数字艺术的编程语言,支持跨平台,语言本身是一个类Java语言,程序文件的后缀为.pde。
    什么是Processing.js为了能让Proce...

  • 如何使用纯CSS3实现一个3D泡沫

    要实现一个逼真的泡沫,涉及到比较复杂的光学/物理学知识。我们这里先简化下问题,实现一个相对简单而足够实用的泡沫元素。我们可以把基础的泡沫元素应用在很多场景中,比如水景、泡咖啡、啤酒甚至火焰特效中。泡沫首先是一个圆形元素.bubble

  • 在PHP网页程序中执行Sass/Compass命令

    我们需要在wow云开发平台支持sass/compass等预编译样式语言,为此我们首先尝试了scssphp扩展,但是在支持最新语法上,经常会出现异常。所以我们采用了代理的方式...

  • 更多...