cssvs.jsanimation:哪个更快

前端小老虎 前端小老虎     2022-10-07     313

关键词:

CSS vs. JS Animation: 哪个更快?

CSS vs. JS Animation: 哪个更快?

基于JavaScript的动画竟然已经默默地比CSS的transition动画快了?而且,Adobe和 Google竟然一直在发布可以媲美原生应用的富媒体移动站点?

这篇文章将会逐点讲解基于JavaScript的DOM动画库,比如Velocity.js和GSAP,是如何比jQuery和基于CSS的动画库高效的。

jQuery

让我们先从这个事实开始:JavaScript和jQuery被错误的混淆了。JavaScript的动画是快的,但是jQuery的动画慢。为什么?因为虽然jQuery很强大,但是它的目标从来不是为了成为一个高效的动画引擎。

  • jQuery不能避免布局震荡因为它的代码除了动画还提供了很多功能。

  • jQuery的内存消耗经常触发垃圾回收,导致动画卡住

  • jQuery使用setInterval而不是requestAnimationFrame (RAF)为了避免一些bug

注意,布局震荡引起了动画开始处的卡顿,垃圾回收导致了动画进行中的卡顿,RAF的缺席导致了帧率低。

实现的例子

避免布局震荡,包括简单地合并DOM查询和DOM更新:

var currentTop,
  currentLeft;

/* 有布局震荡 */
currentTop = element.style.top; /* QUERY */
element.style.top = currentTop + 1; /* UPDATE */

currentLeft = element.style.left; /* QUERY */
element.style.left = currentLeft + 1; /* UPDATE */

/* 没有布局震荡 */
currentTop = element.style.top; /* QUERY */
currentLeft = element.style.left; /* QUERY */

element.style.top = currentTop + 1; /* UPDATE */
element.style.left = currentLeft + 1; /* UPDATE */

发生在更新之后的查询会强制浏览器立马重新布局,并计算给出页面样式的计算值(把更新的影响考虑在内)。这对于运行于16ms间隔的动画来讲,会产生巨大的开销。

同样,实现RAF并不需要对既有代码改动很大。让我们来对比一下RAF的实现和setInterval的实现:

var startingTop = 0;

/* setInterval: 每16ms运行一次来达到60fps (1000ms/60 ~= 16ms). */
setInterval(function() {
  /* 由于这里的代码会在1s内执行60次,所以我们把top属性每秒1单位的增长分成60份 */
    element.style.top = (startingTop += 1/60);
}, 16);

/* requestAnimationFrame: 不管浏览器是否处于最优状态,都试图运行在60fps */
function tick () {
    element.style.top = (startingTop += 1/60);
}

window.requestAnimationFrame(tick);

RAF极大限度地提高了动画的性能。而您只需要修改为数不多的代码。

CSS Transitions

CSS transitions的动画性能优于jQuery,它把动画的逻辑交给了浏览器本身。这会有助于:1)优化DOM交互和内存消耗以避免卡顿,2)在底层借助RAF的特性,3)强制硬件加速(借助GPU的能力来提高动画性能)。

然而,实际情况是,这些优化可以直接通过JavaScript来实现,GSAP已经致力于此多年。Velocity.js,一个新的动画引擎,不止借助于上述技术,还应用了其他方法--我们将很快探讨。

明白JavaScript动画可以媲美CSS动画库这一事实,只是我们计划的第一步。第二步是我们要明白JavaScript动画可以比CSS动画还快。

让我们从检查CSS动画库的缺陷开始:

  • Transitions的强制硬件加速是使GPU加速,然而这反而会导致GPU强压状况下动画的卡顿。这些影响在移动设备上更为严重。(特别地,这个卡顿是由于数据在浏览器的主线程和排序线程间传递的开销导致的。一些CSS属性,比如transforms和opacity,是不受这个开销影响的。)Adobe在这里阐述了这个问题。

  • Transitions在IE10以下有兼容问题, 这在PC端站点会很容易导致问题发生,因为IE8和IE9依然很流行

  • 因为transitions并不是被JavaScript控制(它们只是被JavaScript触发),浏览器并不知道如何同步地使用JavaScript代码来操控优化transitions。

相反地:基于JavaScript的动画库,可以自己决定什么时候使用硬件加速,可以兼容所有版本的IE,并且它们非常适合批量动画优化。

我的建议是,当您只是开发移动站点,并且您的动画只包含简单的状态变化时,可以使用原生CSS transitions。在这种情况下,transitions算是一种高效并且原生的解决方案,并且可以把所有的动画逻辑只放在css中,避免了因为引入JavaScript库而导致页面臃肿。但是,如果您正在设计复杂的UI,或者正在开发具有状态UI的应用程序,请使用JavaScript动画库,它可以使您的动画保持高性能,使您的工作流程保持可控。特别是在管理CSStransitions方面做得很棒的一个库是 Transit

JavaScript Animation

Okay,所以JavaScript在性能上可以占上风。但是JavaScript究竟可以快多少呢?其实,它已经快到可以创建复杂的,通常只能用WebGL构建的3D animation demo。已经快到可以创建通常只能用Flash或者影效处理做到的multimedia teaser。已经快到可以创建通常只能用canvas构建的virtual world

为了直观比较动画库的领先性能,包括Transit(内部使用CSS transitions),请查阅Velocity的文档,在VelocityJS.org

依然存在问题:JavaScript究竟如何达到高性能?下面是基于JavaScript的动画库能实现的优化列表:

  • 为了减小布局震荡,将整个动画中涉及到DOM同步化到堆栈中。

  • 缓存链式调用中的属性值,以尽量减少DOM查询(它是影响DOM动画性能的致命弱点)的发生。

  • 在同一个跨同级元素调用中缓存单位转换比率(例如PX到%、em等)。

  • 当样式更新在视觉上不明显时,跳过更新。

回顾之前讲的布局震荡,Velocity.js利用这些最佳实践来缓存动画的结束值,这些值会被重用为之后动画的开始值,从而避免再次查询DOM元素的初始值:

$element
  /* 将元素向下滑动到视图中。 */
  .velocity({ opacity: 1, top: "50%" })
  /* 延迟1000ms,元素滑动出视图 */
  .velocity({ opacity: 0, top: "-50%" }, { delay: 1000 });

在上面的例子中,第二个Velocity自动知道它应该从opacity为1,top为50%开始。

浏览器最终可以自己执行很多相同的优化,但这样做将需要极大地限制开发人员编写动画代码的方式。因此,同样的原因,jQuery不使用RAF(见上文),浏览器也永远不会强加优化,即使这些优化只有非常小的可能会打破规范或偏离预期的行为。

最后,让我们来比较一下这两个JavaScript动画库(Velocity.js和GSAP)。

  • GSAP是一种快速、功能丰富的动画平台。Velocit是一个轻量级工具,可以极大地提高UI动画性能和工作流程。

  • GSAP需要许可费。Velocity是通过许MIT开源的。

  • 性能都很优异,GSAP和Velocity在真实项目中没有区别。

我的建议是:当您需要精确的控制(例如重映,暂停/恢复/搜索)、运动(例如Bezier曲线路径),或复杂的分组/排序时,使用GSAP。这些特性对于游戏开发和某些niche应用非常重要,但在Web应用程序的UI中并不常见。

Velocity.js

定位GSAP功能丰富,并不意味着Velocity功能单一。相反地,在压缩后只有7Kb的文件中,Velocity不仅提供了jQuery$.animate()的所有功能,而且提供了color animation,transforms,loops,easings,class animation和scrolling。

简而言之,Velocity是jQuery、jQuery UI和CSStransitions的最佳组合。

进一步,从方便的角度,Velocity在底层使用jQuery的$.queue()方法,因此可以无缝地与jQuery的$.animate(), $.fade()$.delay()函数交互。并且,由于Velocity的语法和$.animate()一致,您页面的代码不需要修改

让我们快速看一下Velocity.js。在基础动画上,Velocity和$.animate()一样:

$element
  .delay(1000)
  /* 使用Velocity的2000ms内改变元素top属性的动画*/
  .velocity({ top: "50%" }, 2000)
  /* 当上面Velocity动画执行完时,使用标准的jQuery方法来使元素淡出*/
  .fadeOut(1000);

在高级动画上,复杂的滚动场景和三维动画都可以创建——只需要两行简单的代码:

$element
  /* 在1000ms内,浏览器滚动到这个元素的顶部 */
  .velocity("scroll", 1000)
  /* 之后使元素绕着它的Y轴旋转360度。 */
  .velocity({ rotateY: "360deg" }, 1000);

结束语

Velocity的目标是保持领先的DOM动画性能和便捷。本文的重点是前者。请去VelocityJS.org学习更多关于后者的知识。

在我们结束之前,记得_*一个高性能的UI不仅仅是选择合适的动画库_。页面的其余部分也应该优化。从下面这些奇妙的Google话题中学习更多:

哪个更快?常量、变量或变量数组

】哪个更快?常量、变量或变量数组【英文标题】:Whichisfaster?Constants,VariablesorVariableArrays【发布时间】:2011-12-0715:58:06【问题描述】:我当前的Web应用程序使用大约30个Contants(DEFINE())。我正在阅读变量更快的东西。如果有一个命... 查看详情

哪个 SQL 查询更快,为啥?

】哪个SQL查询更快,为啥?【英文标题】:WhichSQLqueryisfasterandwhy?哪个SQL查询更快,为什么?【发布时间】:2011-07-0419:40:50【问题描述】:最近,有人要求我编写一个查询,以便从包含最大数量的此类实体的组中选择实体的属性... 查看详情

RegisterStartupScript 或 RegisterClientScriptBlock 哪个更快?

】RegisterStartupScript或RegisterClientScriptBlock哪个更快?【英文标题】:WhichisfasterRegisterStartupScriptorRegisterClientScriptBlock?【发布时间】:2012-10-0302:29:40【问题描述】:我从here看到了RegisterStartupScript和RegisterClientScriptBlock之间的区别。有... 查看详情

在 MYSQL 中哪个更快?

】在MYSQL中哪个更快?【英文标题】:whichisfasterinMYSQL?【发布时间】:2014-05-0613:57:33【问题描述】:在MYSQL中速度更快:DATE_SUB(CURDATE(),INTERVAL30DAY)<=ENT_DATE或\'2014-04-0600:00:00\'<=ENT_DATE?在管理员中DATE_SUB(CURDATE(),INTERVAL30DAY)给了我... 查看详情

PHP,单引号或双引号中哪个更快? [复制]

】PHP,单引号或双引号中哪个更快?[复制]【英文标题】:WhatisfasterinPHP,singleordoublequotes?[duplicate]【发布时间】:2011-06-1918:07:19【问题描述】:可能重复:Isthereaperformancebenefitsinglequotevsdoublequoteinphp?单引号或双引号哪个更快,为什... 查看详情

哪个版本的“查找出现”功能更快?

】哪个版本的“查找出现”功能更快?【英文标题】:Whichversionof"findanoccurence"functionisfaster?【发布时间】:2014-03-2712:20:26【问题描述】:我的算法不是很强。有两个版本的函数,如果传递的字符串中有任何大写字母,则... 查看详情

哪个更快,OpenTSDB 还是 KairosDB?

】哪个更快,OpenTSDB还是KairosDB?【英文标题】:Whichoneisfaster,OpenTSDBorKairosDB?【发布时间】:2015-07-0106:26:24【问题描述】:OpenTSDB超级快。KairosDB被称为OpenTSDB的重写,并声称它甚至比OpenTSDB(seehere)更快。但是,我在我的VirtualBox(5G... 查看详情

哪个更快: glob() 或 opendir()

】哪个更快:glob()或opendir()【英文标题】:Whichisfaster:glob()oropendir()【发布时间】:2011-02-1508:06:27【问题描述】:对于读取大约1-2K的文件,glob()和opendir()之间哪个更快?【问题讨论】:它们没有可比性,您应该将glob()与做同样事... 查看详情

哪个sql查询更快?

】哪个sql查询更快?【英文标题】:Whichsqlqueryisfaster?【发布时间】:2014-12-2511:29:56【问题描述】:我正在使用postgresql数据库并希望改进查询(第一个)。我将其重写为第二个。但是我读了一篇文章,上面说“NOTIN”是非常缓慢... 查看详情

哪个更快:多行还是多列?

】哪个更快:多行还是多列?【英文标题】:Whichisfaster:Manyrowsormanycolumns?【发布时间】:2010-11-1503:38:33【问题描述】:在MySQL中,返回100行3列还是1行100列通常更快/更高效/可扩展?换句话说,当存储与记录相关的许多key=>value... 查看详情

indexOfObjectsPassingTest 或 filteredArrayUsingPredicate 哪个性能更快?

】indexOfObjectsPassingTest或filteredArrayUsingPredicate哪个性能更快?【英文标题】:WhichhasfasterperformanceindexesOfObjectsPassingTestorfilteredArrayUsingPredicate?【发布时间】:2014-02-0502:01:04【问题描述】:当需要过滤NSArray以获取返回的数组中项目的... 查看详情

哪个更快?组合查询还是多个查询?

】哪个更快?组合查询还是多个查询?【英文标题】:WhichisFaster?CombinedorMultipleQueries?【发布时间】:2017-12-2320:56:45【问题描述】:这些查询在1millionrecord的表中的性能方面有什么不同吗?id是PRIMARYuserid是INDEXEDname是UNIQUE组合查询... 查看详情

哪个选择查询将运行得更快[重复]

】哪个选择查询将运行得更快[重复]【英文标题】:WhichSelectquerywillrunfaster[duplicate]【发布时间】:2014-12-0207:38:55【问题描述】:您好,我正在尝试从包含大约100万条记录的表中获取最大ID。请建议我在这些查询中哪一个执行得更... 查看详情

String.Join 与 StringBuilder:哪个更快?

】String.Join与StringBuilder:哪个更快?【英文标题】:String.Joinvs.StringBuilder:whichisfaster?【发布时间】:2010-10-0919:24:54【问题描述】:在previousquestion中关于将double[][]格式化为CSV格式,itwassuggested使用StringBuilder会比String.Join更快。这... 查看详情

按存储库排序与按列表排序哪个更快?

】按存储库排序与按列表排序哪个更快?【英文标题】:SortedbyrepositoryvsSortedbylistwhichoneisfaster?【发布时间】:2021-12-2902:12:53【问题描述】:所以我想知道哪个更快。按jpa存储库排序还是按列表排序?//sortedbykotlinlistfunctionpersonRepo... 查看详情

哪个是更快的 XML 解析器? [关闭]

】哪个是更快的XML解析器?[关闭]【英文标题】:WhichisafasterparserforXML?[closed]【发布时间】:2013-07-1309:33:18【问题描述】:我正在尝试解析一个简单的文件,我想知道:XML::SimpleXML::SmartLibXML哪个更快?【问题讨论】:简单:使用XM... 查看详情

类型未定义。哪个更快更好? [复制]

】类型未定义。哪个更快更好?[复制]【英文标题】:typeofundefined.Whichisfasterandbetter?[duplicate]【发布时间】:2014-01-2822:58:30【问题描述】:这里vara=什么时候if(typeofa.b=="undefined")console.log("undefined");if(!a.b)console.log("undefined");两者都返... 查看详情

哪个Oracle查询更快

】哪个Oracle查询更快【英文标题】:WhichOraclequeryisfaster【发布时间】:2018-11-2415:08:50【问题描述】:我正在尝试使用C#WPF视图显示员工属性。我的数据库中有“2”个不同的oracle表中的数据:那些高层次的表结构是……员工表(EMP)-... 查看详情