避免无用的渲染绘制(Avoiding Unnecessary Paints)
本文翻译自html5rock上的文章,文章英文原版地址在最后给出。
文中的Paints我翻译成渲染绘制,我自己是这么理解。
开始
绘制(渲染)一个网站或者一个应用的元素对浏览器来说开销是很大的,它会对运行时的效率产生负面影响,在这篇文章中我们将快速的过一遍,哪些情况会导致导致绘制事件在浏览器中产生,以及如何在你今后的工作中尽量避免没必要的绘制。
绘制(渲染):一场超快的旅行
浏览器的主要任务之一就是将DOM和CSS转换成像素并绘制到屏目上,这是一系列复杂的处理过程。浏览器先读取HTML标签并由此创建DOM树,它对CSS也是进行了类似的工作,读取CSS并创建CSSOM树,然后将两者合并,最终我们就可以根据合并后的结构渲染成像素到屏目了
绘制过程本身就挺有趣。在Chrome浏览器中,合并后的DOM树与CSS树被一款名叫Skia的软件进行栅格化。如果你曾使用过CANVAS的API,那么你会觉得它们的API非常相似。
不光是moveTo, lineTo等这些API,另外的一些高级API也一样非常相似。本质上来讲,所有需要被绘制的元素都将被提取到能被Skia执行的集合中,执行后输出的是一堆位图。这些位图将被上传到GPU中,GPU将帮他们组合在一起并最终输出到屏目上。
Skia的工作量是直接受你在元素上所应用的样式引响的。如果你在元素应用的样式算法很复杂那么,会大大加重Skia的工作。你可以参考这篇文章article on how CSS affects page render weight,以更深的理解CSS如何影响页面渲染。
总而言之,绘制工作费时间,如果我们不减少绘制,那么可能会掉侦。用户可能会注意到掉帧,看起来会闪一下。这对于我们的APP上来说是从根本上伤害了用户体验。我们不希望看到这样的结果吧,so让我们来看看哪些东西会产生必要的绘制(渲染)工作,以及我们能对这些做些啥优化。
Scrolling(滚动)
无论你是往上还是往下滚动,内容在显示到屏目上之前浏览器都需要进行重绘。如果一切顺利的话,可能产生重绘的区域会是很小一块,但是即使是很小一块,那一小块元素可能就是应用了复杂的CSS样式。所以不会因为绘制的区域小而绘制的速度会快。
为了看清哪些区域被重绘了,你可以使用Chrome中自带的调试工具,点击右上角的设置按钮,选中 “Show Paint Rectangles”后,在你的页面中做一些简单的交互,你便会看到一些会闪动的矩形框,那就是重绘的区域了。
滚动的性能表现,是你网站成功的关键。用户真的会关心你的站点或应用滚动的是不是顺畅,他们可不会喜欢滚动的不流畅的网站。我们在滚动时保证轻量的绘制工作,因此而得到的一个好处就是用户看不到闪动或掉帧这类事情了。
我之前已经写过一篇文章是关于滚动性能的,如果你想了解更我,可以参考这篇文章 article on scrolling performance
Interactions(交互)
交互是另一个产生绘制工作的原因,如:hovers, click,touches drags无论用户执行了其中的哪一样操作都会引发绘制或重绘。让我们以hover举个栗子,当hover某个元素时,Chrome不得不重绘那个被hover影响的元素。如果滚动时有一个很大很复杂的重绘工作,那么你将会看到绘制帧频的下降。
大家都想要好的、流畅的交互动画,这咱又得关心一下动画样式在变化时的花费时间及性能成本
An unfortunate combination(不幸的组合)
当我滚动的同时移动鼠标时会发生什么 ?在无意中是完全有可能触发昂贵的重绘开销的,这将导致我的帧频率小于16.7ms(我们应该保持在每秒60帧的频率)。我已经写了一个demo来更直观的表达我所说的情况created a demo 希望你在滚动的同时移动鼠标能看到hover效果。但是让我们看看Chrome调试工具告诉了我们什么
在上图中你可以看到,当我在某一个块元素上(blocks)上hover时调试工具已捕获了绘制工作。为了看效果,我在demo中疯狂的加重了样式效果(缩放,阴间等动画效果)。这导致帧频逼近或偶尔间超过了帧频的临界值。最后我想要的是减少不必要的绘制工作,尤其是在滚动时。
我们如何实现呢,实现其实很简单,技巧是添加一个scroll回调handler处理函数,在这个函数内禁止hover效果,同时设置一个计时器,用以恢复hover效果。这意谓着我们保证在滚动时禁止一些开销昂贵的交互效果,并且在停止滚动足够的时间后恢复那些效果
实际应用时注意!
改变此项会影响你应用程序用户体验,所以明智的对待它,延时恢复效果要在你和的团队能接受的时间范围内
这是对应的代码
// Used to track the enabling of hover effects var enableTimer = 0; /* * Listen for a scroll and use that to remove * the possibility of hover effects */ window.addEventListener('scroll', function() { clearTimeout(enableTimer); removeHoverClass(); // enable after 1 second, choose your own value here! enableTimer = setTimeout(addHoverClass, 1000); }, false); /** * Removes the hover class from the body. Hover styles * are reliant on this class being present */ function removeHoverClass() { document.body.classList.remove('hover'); } /** * Adds the hover class to the body. Hover styles * are reliant on this class being present */ function addHoverClass() { document.body.classList.add('hover'); }
如你所见,我们用了一个class来决定hover效果是否被允许使用,下面就是这个class的css表达式
/* Expect the hover class to be on the body before doing any hover effects */ .hover .block:hover { … }
这就是所有的内容了
Conclusion(结束语)
渲染的性能表现对于用户是否喜欢你的应用至关重要,你需要的是尽量保证绘制工作量保持在16ms帧频率以下,为了帮助实现这一目标,你得使调式工具一直贯穿于你的开发过程中,确保帧频保持在正常范围内,并随时修复上升的帧频。
被忽略的交互过程,特别是重试绘制元素,是渲染性能的杀手,正如你所见,我们可以用一些简单的代码解决掉这些问题。
看一看你的站点和应用,能不能做一些渲染绘制上面的优化
本文翻译自:
http://www.html5rocks.com/en/tutorials/speed/unnecessary-paints/
我英文水平有限,凑合着翻,全当自我学习欢迎交流学习
========================================================
转载处请注明:博客园(王二狗)willian12345@126.com