深入理解JS和CSS3动画性能问题和技术选择
本文对比了JS及其框架和CSS3的动画性能,并深入剖析了其内在原因。
技术结论大致如下:
1. jQuery出于设计原因,在动画性能上表现最差
2. CSS3由于把动画逻辑推给了浏览器,优化了内存消耗、DOM操作和默认利用了RAF,所以要比jQuery动画性能更好
3. CSS3可能会引起浏览器主线程和复合器线程之间过度数据交互,从而导致性能下降
4. 纯JS实现的动画,在利用RAF和注意布局摆动处理时,可以获得媲美CSS3的动画性能,而在浏览器兼容性上比CSS3更好
应用选型建议:
1. 对于简单页面动画,建议优先选择CSS3动画
原文链接: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:
jQuery cannot avoid layout thrashing due to its codebase that serves many purposes beyond animation.
jQuery's memory consumption frequently triggers garbage collections thatmomentarily freeze animations.
jQuery uses setInterval instead of requestAnimationFrame (RAF) in order to protect novices from themselves.
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:
最新评论
- 相关文章
3D感知和建模关键硬件技术:双目、3D结构光和TOF
无论VR、AR和3D打印,其核心技术包含3D成像和建模。而3D建模属于劳动密集型的工作,耗时耗力,凡这类工作都会是被新技术革命的地方,自动3D建模技术就是为了解决...
WebGL场景中多相机拍摄的原理和意义
一般而言,3D场景的渲染只需要一个相机,不过借助多相机可以获取一些单相机无法达到的特效。比如突显特定对象并模糊背景。
3D相机渲染的基本原理是依靠颜色...WebGL、Asm.js和WebAssembly概念简介
随着HTML技术的发展,网页要解决的问题已经远不止是简单的文本信息,而包括了更多的高性能图像处理和3D渲染方面。这正是要引入WebGL、Asm.js和WebAssembly这些技...
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...
NodeJS、Java和PHP性能考量和若干参考结论
首先需要说明的是,严格而言NodeJS和Java、PHP并非对等概念,NodeJS是基于JS的一个应用程序,而Java/PHP是语言。我们这里实际指的是分别使用node、java和php来实...
如何使用CSS3合成模式(blend-mode)和滤镜(filter)实现彩色蜡笔(时光机)照片特效
在之前的文章中我们已经详细讲解过CSS3滤镜(filter,也可称之为过滤器)的工作方式,本文将实现一个当下流行的时光机相片特效实例来说明其实际用途。
我们...Three.js 对象局部坐标转换为世界坐标
在Three.js中进行顶点几何计算时,一个需要注意的地方是,需要统一坐标系。比如你通过Three.js提供的API创建了一个球体网孔对象,那么默认情况下,各网孔顶点的...
Three.js入门教程6 - 创建全景图和纹理
全景图非常酷。使用Three.js做一个属于自己的全景图并不是那么困难。要做一个全景图,你需要一个软件用来做一张全景图片。我使用了iPhone上的Microsoft Photosyn...
S3TC(S3 Texture Compression)纹理压缩格式详解
使用S3TC格式存储的压缩纹理是以4X4的纹理单元块(texel blocks)为基本单位存储的,每纹理单元块(texel blocks)有64bit或者128bit的纹理单元数据(texel data)。这...
WebGL入门教程4 - 使用纹理贴图(Texture Map)
3D建模和纹理贴图的关系就好比人体和皮肤(或着装)的关系,3D建模用来处理空间属性,而贴图适合用来处理细腻的表面属性。如果不使用贴图,而想在表面达到足够的...
Canvas实例教程:图像移动、大小调整和裁剪
本文介绍如何使用JavaScript和HTML5 Canvas元素来移动、调整大小和...
如何使用CSS3实现一个平滑的3D文本标题
要实现3D文本,基本上有3种方法:1. 使用CSS3的投影滤镜(filter: drop-shadow)2. 使用3d建模和CSS3 3d变换来实现(最真实)3. 使用CSS3 text-shadow属性来实现...
SVG过滤器feColorMatrix矩阵变换效果用法详解
在计算机图形学(数学)中,矩阵乘法可用于把空间向量进行几何变换。我们可以把颜色的值(RGBA)表示成一个四维空间向量:color = (r, g, b, a);那么就可以应用...
更多...