性能影响一切。 明显或真实的性能提升可改善用户体验。 反过来,改善的用户体验会提高利润。
几项主要研究证明,延迟增加会大大降低收入。 必应(Bing) 报告说,延迟增加2,000毫秒会导致每用户收入下降2%。 同样,Google发现500毫秒的延迟会导致流量下降20% 。
因此,在构建高性能UI引擎的过程中 ,我同时构建了一个用户体验引擎。 本文旨在将当前的Web性能前景与实际情况进行关联,并深入研究Velocity.js(一种动画引擎,可显着提高所有浏览器和设备的UI性能和工作流程)的性能优化。
在深入研究Velocity之前,让我们回答紧迫的问题。 多年来,该浏览器如何可能暗中具有巨大的性能潜能,但前端开发人员仍未充分利用它?
简短的答案:开发人员中根本缺乏Web性能意识。 让我们来探索。
网络性能概况
从UI设计的角度来看,不乏文章赞扬构建移动优先的响应式网站的优点。 到目前为止,开发人员已经掌握了它。 相反,从UI性能的角度来看,大多数开发人员都会承认他们不知道自己在做什么。 尽管来自Google,Mozilla和Microsoft的拥护者撰写了无数有关性能最佳实践的文章,但大多数开发人员根本没有阅读它们。
缺乏这种认识的是动态的,通过UI设计,可以在多年的经验中自信地迭代艺术技巧。 但是,尽管性能原理(数据结构,延迟和渲染管道)受相同的迭代过程约束,但其前端实现的细节可能会定期更改。 坦率地说,注重性能的开发人员经常被浏览器怪癖和设备功能所束缚。 这种情况需要开发人员敏锐地意识到Web的基础体系结构层(渲染堆栈,垃圾收集和网络),以便他们可以广泛地抽象出解决性能问题的方法。
但是,由于开发人员已经掌握了工作量,因此目前的观念表明,普通开发人员精通该领域是不合理的。 为此,网络的主要性能倡导者Google的Ilya Grigorik最近对浏览器和网络性能的神话进行了逐点分析: 高性能浏览器网络 。 (其他Web性能资源可在本文底部找到。)
当前的网络性能状况类似于紧跟IE8的怪癖–一段时间后,您投入了精力,只是提高了站点对旧版浏览器支持的起点。
移动设备上的情况几乎相同:开发人员告诉自己:“嗯,设备越来越快。 因此,在接下来的几个月中,随着用户继续升级其设备,我的网站自然会变得更加高效。”
不幸的是,事实恰恰相反。首先,发展中国家正在采用的智能手机无法满足我们口袋中iPhone的性能要求–您是否真的要放弃为下一个20亿上网人数提供产品的机会? 如果您的直觉是“这不是我的问题”,请放心,您邪恶的Web开发人员孪生兄弟坐在一千英里之外,正在着手开发一种将大胆创新的解决方案,然后才想进入市场即使在低功率设备上也能快速完成。
即将到来的Firefox OS计划准备将功能强大的智能手机带给亿万人民。 未来已经来临。 我们不是在说假说。 爱立信报告说,未来五年,全球智能手机用户数量将从19亿增加到59亿-几乎完全由发展中国家推动。
设置后遗忘心态对网络性能的第二个危险是,开发人员系统地犯了错误,即在承受理想性能负载的设备上测试其移动页面。 但是,尝试打开更多的应用程序和网页。 现在,重新测试您的网站。 Yikes,您刚刚人工创建了一个相对“古老”的Android 2.3设备。 另外,您已经陷入第二个问题的核心:基于浏览器的应用程序对设备负载(CPU,GPU和内存使用情况)敏感。 加上设备硬件的可变性,您便开始接近移动性能的现实:您应该始终开发最快的网站,而不仅仅是在iPhone上可以正常运行的网站。
性能很复杂,性能很重要。 那很清楚。 但是,我们实际上可以做什么? 这就是我打算在三个月内深入研究开源开发的答案。
网络动画景观
jQuery是Web的主要动画工具,它在2006年开始开发,而它又是Web上主要的动画工具,而Velocity则是在2014年开发的。
简而言之,Velocity是一个轻量级的CSS操作库,其动画层位于顶部。 它完全由JavaScript驱动,而不是CSS过渡。 它公开了与jQuery的$.animate()
相同的API,以简化从$.animate()
到$.velocity()
的过渡。
在Velocity之前,DOM动画领域主要由jQuery,Transit(用于通过JavaScript控制CSS转换的转到库)和GSAP(第一个高性能的JavaScript动画库)组成。
这些库的缺点如下:
- jQuery的本机
$.animate()
缓慢且UI动画设计功能相对较轻-即使与jQuery UI配对也是如此。 - Transit比jQuery快得多,但功能甚至更轻巧,由于其通过JavaScript填充CSS过渡的特性而偶尔出现故障,并且不支持IE8和IE9(它们在全球浏览器中仍然占有很大份额) 。
- GSAP是功能强大的成熟动画平台。 它的功能几乎是无限的。 它可以对从DOM到WebGL的所有内容进行动画处理。 (相比之下,Velocity只专注于作为一种轻量级工具来极大地改善UI动画性能和工作流程。)尽管GSAP要求为各种类型的企业支付许可费,但Velocity是通过超许可的MIT许可证免费开源的。
在所有压力级别下,速度都大大超过了jQuery,而从中等压力级别开始,Transit的运行速度超过了jQuery。 GSAP的表现类似于Velocity。 有关头对UI性能的比较,请参阅Velocity的文档 。
计时器优化
我们准备深入探讨多汁的性能细节。 您如何快速制作动画引擎? 是微优化吗? 不。
速度微优化为零。 这是我确定要逆转的趋势。 Stack Overflow充满了jsPerf.com的比较,这些比较好意的开发人员使用它们来确定哪种基于JavaScript的实现是性能最高的。 但是,开发人员常常不考虑它们的上下文而喜欢这些面值比较。 如果一个实现每秒已经可以完成几百万次操作,那么它的替代实现有多快无关紧要。 您的JavaScript代码可能永远不会以每秒数百万次操作的规模运行。
DOM性能的真正瓶颈主要是计时器创建和DOM操作。
让我们从分析计时器创建开始。 在使用setInterval()
, setTimeout()
和requestAnimationFrame()
时创建计时器。 计时器创建存在两个性能问题:1)由于浏览器维护计时器的过多开销,一次触发太多的计时器会降低帧速率,以及2)不正确地标记动画开始的时间会导致丢帧。
Velocity解决第一个问题的方法是维持一个全局滴答循环,该循环可以一次循环遍历所有活动的Velocity动画。 不会为每个Velocity动画创建单独的计时器。 简而言之,Velocity将调度优先于中断。
第二个问题是标记动画的开始时间,以便循环可以跟踪经过了多少时间,这可以通过直接在第一个动画滴答本身内部设置开始时间来解决。
相反,开发人员通常在触发动画时设置动画的开始时间。 但是,在用户单击按钮以触发UI动画的时间与实际的动画循环开始的时间之间,第三方因素(相关的JavaScript逻辑,附加的UI交互,系统范围的负载,等)可能会导致延迟。 随后,当动画循环最终开始时(例如,大约16-85毫秒后),大多数动画循环的实现方式将导致丢弃大量前帧以弥补时间差异。
“速度”设置了第一个动画循环刻度内的开始时间,而不是实际触发动画时的结果,是动画有可能在其触发的起点之后约16-85ms开始播放。 但是,这种延迟实际上是无法察觉的,并且最终是无关紧要的,除非您要创建游戏(通常需要时间精确的碰撞检测),否则您就不会这样做。
总之,对于UI动画,应始终将平滑度放在时间精度之上。
最小化DOM操作
计时器优化只是实现Velocity(即DOM操作的最小化)背后的实际性能优化的垫脚石。 Velocity背后的核心性能原则是,虽然您可以提供的帧速率有一个上限(大脑只能每秒感知这么多帧),但是避免如何巧妙地避免DOM操作却没有上限。
DOM当然是网页HTML底层的层次表示。 自然,DOM操作包括设置和获取。 修改元素上CSS属性的值时,就是在设置(更新)DOM。 相反,当您向DOM查询元素的CSS属性的当前值时,您正在获取(查询)。 这些DOM操作会产生性能开销。 设置DOM后,浏览器必须计算更改的效果。 毕竟,当您更改一个元素的宽度时,它会触发连锁反应,从而导致其父元素,同级元素和子元素的宽度发生变化。
这种由交替的DOM集和获取导致的帧速率降低现象被称为“布局抖动”。
浏览器已经过优化,可以快速执行DOM布局的重新计算,但是当动画循环同时运行时,每毫秒都是宝贵的时间,而DOM操作是触发浏览器开销的最快方法,其触发时间在几毫秒的范围内(而大多数JavaScript操作在一秒钟之内完成)一毫秒)。 为了说明动画循环对时间的敏感程度,为了达到每秒60帧(眼睛感知黄油平滑运动的速率),动画循环中的每个刻度必须在16毫秒内完成(1s / 60 = 16.666毫秒) 。
速度要竭尽全力,以最大程度地减少布局抖动和DOM操作。
首先,作为Velocity的唯一开发人员,我花时间在Velocity的整个源代码中放置注释,以突出显示操纵DOM的所有有害代码行。 简单地说,我在适当的地方撒上/* GET */
和/* SET */
。 坚持这种做法,使我能够快速关注我的代码,以确保新功能或错误修复不会引起布局混乱。 我遵循代码路径,看看是否/* GET */
后跟/* SET */
。 如果是这样,我要么重新设计批处理SET和GET的路径(以最大程度地减少颠簸的发生),要么避免完全实现该功能。
其次,Velocity会尽可能对缓存的值进行操作,这样就不必在每个动画开始时都重新查询DOM。 例如,Velocity检测何时将多个动画链接在一起,并重用先前的Velocity调用的动画结束值作为后续调用的开始值。 这是一个微妙的过程,因为必须不惜一切代价避免对过时的值进行操作,否则动画可能会崩溃。 Velocity通过标记每个Velocity发起的动画调用来解决此不确定性,然后在检测到链中的先前调用不是由Velocity发起时避免随后进行值缓存(例如,jQuery的$.queue()
或$.fade()
函数是在速度调用之间注入)。
Velocity使用的第三个也是最后一个主要的DOM最小化技术是其“真空”方法转换单位转换率。 单位转换逻辑是确定以像素为单位的百分之一的代码。 在将元素的宽度设置为例如“ + 25%”的动画时,这是必要的-动画引擎必须确定该值以像素为单位,以便可以使用相同单位类型的两个值执行增量数学运算。 为什么特别是像素? 因为浏览器在查询时会返回CSS属性值(以像素为单位),而与用于设置属性的单位类型无关。
单位转换过程需要将目标元素的宽度和高度临时设置为1%,然后计算随后的DOM查询针对该元素的尺寸返回什么。 返回的值(以像素为单位)将提供介于1%和像素之间的比率。
Velocity在此单位转换过程中进行了三个关键的优化:首先,它在通过测试的元素之间缓存单位转换比率,以确定它们是否共享相同的比率(即,它们具有相同的父元素和相同的CSS位置值)。 当一组元素同时被动画化时,这一点至关重要。
其次,Velocity在不需要时完全跳过单位转换。 例如,当属性的起始值为0时,每种单位类型的零都为零-无需转换。
第三,Velocity唯一地选择在DOM树修改上进行布局调整。 前一种技术通过将动画元素暂时放置在虚拟CSS属性“ vacuum”中,从而产生了数量不等的交替DOM获取和设置,在那里剥夺了CSS属性会影响尺寸计算(例如,框大小,溢出)。 剥离属性,然后进行临时值设置和获取,然后再将元素重置为其初始值,需要进行几轮布局调整。
但是,我在Velocity的开发过程中发现的是,布局修改比迄今为止使用的技术更具性能:通过克隆动画元素将DOM树连根拔起,将克隆插入到原始对象旁边,然后在克隆,然后完全删除克隆。 此方法是可取的,因为它避免了创建CSS真空的非平凡过程,但是却导致DOM树的重构(通过插入和删除元素来影响其层次结构),最终导致浏览器的开销比交替显示更大CSS属性值可以。 (我使用jsPerf.com在所有浏览器和设备上确认了这些结果;正如其维护人员会告诉您的,这是jsPerf变得异常强大的地方-当您需要比较许多环境中的真实瓶颈情况时。)
放在一起
那么,您可以利用Velocity.js的所有潜在功能做什么呢?
- 你可以设计排版激烈动画,竞争者影片信用序列(桌面只): 演示#1通过@rachsmithtweets , 演示#2的@legomushroom 。
- 您可以将一系列标准div元素转换为3D粒子演示 (所有设备)。
这两个演示都完全在DOM中运行。 没有WebGL。 没有画布。
就日常的网页设计而言,可以在Everlane.com上找到Velocity UI性能的最新著名示例 。 浏览; 体验令人难以置信的流畅和响应能力。
如您所见,DOM和JavaScript非常快。 您只需要考虑性能最佳实践。
查看以下资源以了解更多信息。
网络性能资源
From: https://www.sitepoint.com/incredibly-fast-ui-animation-using-velocity-js/