然后,这个 css3 3d 行星运转动画的制作过程不再详细赘述,本篇的重点放在 web 动画介绍及性能优化方面。详细的 css3 3d 可以回看上一篇博客:【css3进阶】酷炫的3d旋转透视。简单的思路:
2. 每一个球体的制作,想了许多方法,最终使用了这种折中的方式,每一个球体本身也是一个 css3 3d 图形。然后在制作过程中使用 sass 编写 css 可以减少很多繁琐的编写 css 动画的过程;
小标题起得有点大,我们知道,不同浏览器的内核(渲染引擎,rendering engine)是不一样的,例如现在最主流的 chrome 浏览器的内核是 blink 内核(在chrome(28及往后版本)、opera(15及往后版本)和yandex浏览器中使用),火狐是 gecko,ie 是 trident ,浏览器内核负责对网页语法的解释并渲染(显示)网页,不同浏览器内核的工作原理并不完全一致。
所以其实下面将主要讨论的是 chrome 浏览器下的渲染原理。因为 chrome 内核渲染可查证的资料较多,对于其他内核的浏览器不敢妄下定论,所以下面展开的讨论默认是针对 chrome 浏览器的。
当页面加载并解析完毕后,它在浏览器内代表了一个大家十分熟悉的结构:dom(document object model,文档对象模型)。在浏览器渲染一个页面时,它使用了许多没有暴露给开发者的中间表现形式,其中最重要的结构便是层(layer)。
这里的纹理指的是 gpu 的一个术语:可以把它想象成一个从主存储器(例如 ram)移动到图像存储器(例如 gpu 中的 vram)的位图图像(bitmap image)。一旦它被移动到 gpu 中,你可以将它匹配成一个网格几何体(mesh geometry),在 chrome 中使用纹理来从 gpu 上获得大块的页面内容。通过将纹理应用到一个非常简单的矩形网格就能很容易匹配不同的位置(position)和变形(transformation),这也就是 3d css 的工作原理。
说起来很难懂,直接看例子,在 chrome 中,我们是可以看到上文所述的 graphicslayer -- 层的概念。在开发者工具中,我们进行如下选择调出 show layer borders 选项:
在一个极简单的页面,我们可以看到如下所示,这个页面只有一个层。蓝色网格表示瓦片(tile),你可以把它们当作是层的单元(并不是层),chrome 可以将它们作为一个大层的部分上传给 gpu:
上面示意图中黄色边框框住的层,就是 graphicslayer ,它对于我们的 web 动画而言非常重要,通常,chrome 会将一个层的内容在作为纹理上传到 gpu 前先绘制(paint)进一个位图中。如果内容不会改变,那么就没有必要重绘(repaint)层。
3d 或透视变换(perspective、transform) css 属性 使用加速视频解码的 元素 拥有 3d (webgl) 上下文或加速的 2d 上下文的 元素 混合插件(如 flash) 对自己的 opacity 做 css 动画或使用一个动画变换的元素 拥有加速 css 过滤器的元素 元素有一个包含复合层的后代节点(换句话说,就是一个元素拥有一个子元素,该子元素在自己的层里) 元素有一个 z-index 较低且包含一个复合层的兄弟元素(换句话说就是该元素在复合层上面渲染) 层的重绘
对于静态 web 页面而言,层在第一次被绘制出来之后将不会被改变,但对于 web 动画,页面的 dom 元素是在不断变换的,如果层的内容在变换过程中发生了改变,那么层将会被重绘(repaint)。
按照道理,页面发生这么多动画,重绘应该很频繁才对,但是上图我的行星动画中我只看到了寥寥绿色重绘框,我的个人理解是,一是 gpu 优化,二是如果整个动画页面只有一个层,那么运用了 transform 进行变换,页面必然需要重绘,但是采用分层(graphicslayer )技术,也就是上面说符合情况的元素各自创建层,那么一个元素所创建的层运用 transform 变换,譬如 rotate 旋转,这个时候该层的旋转变换并没有影响到其他层,那么该层不一定需要被重绘。(个人之见,还请提出指正)。
浏览器解析 html 获取 dom 后分割为多个图层(graphicslayer) 对每个图层的节点计算样式结果(recalculate style--样式重计算) 为每个节点生成图形和位置(layout--回流和重布局) 将每个节点绘制填充到图层位图中(paint setup和paint--重绘) 图层作为纹理(texture)上传至 gpu 符合多个图层到页面上生成最终屏幕图像(composite layers--图层重组) web 动画很大一部分开销在于层的重绘,以层为基础的复合模型对渲染性能有着深远的影响。当不需要绘制时,复合操作的开销可以忽略不计,因此在试着调试渲染性能问题时,首要目标就是要避免层的重绘。那么这就给动画的性能优化提供了方向,减少元素的重绘与回流。
每个页面至少需要一次回流,就是在页面第一次加载的时候。在回流的时候,浏览器会使渲染树中受到影响的部分失效,并重新构造这部分渲染树,完成回流后,浏览器会重新绘制受影响的部分到屏幕中,该过程成为重绘。
其实浏览器自身是有优化策略的,如果每句 javascript 都去操作 dom 使之进行回流重绘的话,浏览器可能就会受不了。所以很多浏览器都会优化这些操作,浏览器会维护 1 个队列,把所有会引起回流、重绘的操作放入这个队列,等队列中的操作到了一定的数量或者到了一定的时间间隔,浏览器就会 flush 队列,进行一个批处理。这样就会让多次的回流、重绘变成一次回流重绘。
display:none 隐藏后的元素不占据任何空间。它的宽度、高度等各种属性值都将“丢失” visibility:hidden 隐藏的元素空间依旧存在。它仍具有高度、宽度等属性值 从性能的角度而言,即是回流与重绘的方面,
display:none 一旦父节点元素应用了 display:none,父节点及其子孙节点元素全部不可见,而且无论其子孙元素如何设置 display 值都无法显示; visibility:hidden 一旦父节点元素应用了 visibility:hidden,则其子孙后代也都会全部不可见。不过存在隐藏“失效”的情况。当其子孙元素应用了 visibility:visible,那么这个子孙元素又会显现出来。
不同样式在消耗性能方面是不同的,譬如 box-shadow 从渲染角度来讲十分耗性能,原因就是与其他样式相比,它们的绘制代码执行时间过长。这就是说,如果一个耗性能严重的样式经常需要重绘,那么你就会遇到性能问题。其次你要知道,没有不变的事情,在今天性能很差的样式,可能明天就被优化,并且浏览器之间也存在差异。
好在 chrome 浏览器提供了许多强大的功能,让我们可以检测我们的动画性能,除了上面提到的,我们还可以通过勾选下面这个 show fps meter 显示页面的 fps 信息,以及 gpu 的使用率:
will-change 为 web 开发者提供了一种告知浏览器该元素会有哪些变化的方法,这样浏览器可以在元素属性真正发生变化之前提前做好对应的优化准备工作。 这种优化可以将一部分复杂的计算工作提前准备好,使页面的反应更为快速灵敏。
不要将 will-change 应用到太多元素上:浏览器已经尽力尝试去优化一切可以优化的东西了。有一些更强力的优化,如果与 will-change 结合在一起的话,有可能会消耗很多机器资源,如果过度使用的话,可能导致页面响应缓慢或者消耗非常多的资源。
有节制地使用:通常,当元素恢复到初始状态时,浏览器会丢弃掉之前做的优化工作。但是如果直接在样式表中显式声明了 will-change 属性,则表示目标元素可能会经常变化,浏览器会将优化工作保存得比之前更久。所以最佳实践是当元素变化之前和之后通过脚本来切换 will-change 的值。
不要过早应用 will-change 优化:如果你的页面在性能方面没什么问题,则不要添加 will-change 属性来榨取一丁点的速度。 will-change 的设计初衷是作为最后的优化手段,用来尝试解决现有的性能问题。它不应该被用来预防性能问题。过度使用 will-change 会导致大量的内存占用,并会导致更复杂的渲染过程,因为浏览器会试图准备可能存在的变化过程。这会导致更严重的性能问题。
给它足够的工作时间:这个属性是用来让页面开发者告知浏览器哪些属性可能会变化的。然后浏览器可以选择在变化发生前提前去做一些优化工作。所以给浏览器一点时间去真正做这些优化工作是非常重要的。使用时需要尝试去找到一些方法提前一定时间获知元素可能发生的变化,然后为它加上 will-change 属性。
3d transform 会启用gpu加速,例如 translate3d, scalez 之类,当然我们的页面可能并没有 3d 变换,但是不代表我们不能启用 gpu 加速,在非 3d 变换的页面也使用 3d transform 来操作,算是一种 hack 加速法。我们实际上不需要z轴的变化,但是还是假模假样地声明了,去欺骗浏览器。
