Transform Animation, Inline Transform and Overlap

Transform Animation, Inline Transform and Overlap


这篇文章的目的在于说明在 Chrome 里面 Transform Animation 和 Inline Transform 对 Layer Overlap 计算的影响,而这个影响又会对一个 Render Layer 是否生成 Composited Layer(合成图层)产生难以察觉的微妙影响,而最终可能导致网页的渲染性能的明显变化。文章的末尾会考察一个手机凤凰网的例子并给出一些页端优化的建议。

首先我们来考察一个这样的问题 —— 假设我们有两个 Render Layer A 和 B,B 排在 A 的后面并且是 A 的兄弟节点,而 A 本身已经生成了一个 Composited Layer(比方说 A 有一个 translateZ 属性),B 本身没有什么特殊属性,那么 B 到底需不需要也生成一个 Composited Layer?

我们实际上有两个选择:

  1. 通过 Overlap 计算决定 B 是否生成 Composited Layer
  2. 不考虑 Overlap,直接假定 B 也需要生成 Composited Layer

对 Chrome 早期版本来说,一般来说就是 1,但是在 A 运行一个 Transform Animation 时会选择了 2;但是 Chrome 在 3x 版本期间引入了一些改动,在 A 当前正在运行一个 Transform Animation 或者具有 Inline Transform 属性的条件下,Chrome 都会选择 2,就是忽略 Overlap 计算,直接为 B 生成 Composited Layer。

<html>
<head>
<style type="text/css">
div {
  width: 500px;
  height: 500px;
}
div.red {
  background-color: #ffb3b3;
  transform:translate(100px, 100px) translateZ(0);
}
div.blue {
  background-color: lightblue;
  position: relative;
  top:200px;
  left:100px;
}
</style>
<script type="text/javascript">
setTimeout("inlinetransform()", 5000)
function inlinetransform() {
  var c = document.getElementById("inlinetransform");
  c.style.transform="translate(100px,100px) translateZ(0)"
}
</script>
</head>
<body>
<div id="inlinetransform" class="red"></div>
<div class="blue"></div>
</body>
</html>

inlinetransformoverlap_1.png-2.4kB inlinetransform_overlap_2.png-2.2kB

上面是一个简单的示例代码,可以在 Chrome 40+ 版本上验证:

  1. 红色的 DIV A 因为有 translateZ 属性,所以一开始就是一个 Render Layer 和 Composited Layer,蓝色的 DIV B 因为有 position:relative,所以它是一个 Render Layer,但是它没有和 A 交叠,所以没有生成 Composited Layer;
  2. 5 秒后触发一个 JS 调用,直接修改了红色 DIV 的 transform 属性(实际上没有任何位置变化),使它满足 Inline Transform 的条件,这时即使蓝色 DIV 没有跟红色 DIV 交叠,它也会生成 Composited Layer;

Chrome 这样做的原因主要是考虑 CSS/JS Transform Animation 的性能,假设我们现在使用一个 Tranform Animation 移动 A,或者用手指拖动 A(假设 JS Touch Event Handler 会改变 A 的 Transform),假设我们只选择 1 的情况:

  1. 我们需要在改变 A 的 Transform 的同时,遍历 Render Layer 树,计算 A 跟其它 Render Layer 的 Overlap 状况;
  2. 如果 Overlap 状况发生变化,比如 A 从 B 的外面移动到 B 下方,我们需要重新更新整棵 Composited Layer 树,并且标记发生变化的区域然后重新光栅化(当一个 Render Layer 从非 Composited Layer 变成 Composited Layer,会使得它原来所在的 Composited Layer 和它新生成的 Composited Layer 都需要重新光栅化);

但是如果按照 2 的方式来处理,因为其它 Render Layer 事先已经生成 Composited Layer,所以无论怎么改变 A 的 Transform,我们都不需要重新计算 A 和其它 Render Layer 的 Overlap 状况,因为无论是否 Overlap,Composited Layer 树都是一样的。这样上面的 1 和 2 的开销都可以避免。1 的开销比较小,大约是毫秒级别的开销,但是只要 A 的 Transform 发生变化就会触发,2 虽然只在 Overlap 改变的瞬间触发,但是开销比较大,可能达到上百毫秒甚至几百毫秒

ifeng.png-175.9kB

Chrome 早期版本的处理方式在部分场景下可能存在性能问题,我们来考察手机凤凰网的例子。手机凤凰网上方有一个图片轮播的栏位,它实际上是一个超宽包含多个图片的 Composited Layer,用户可以左右拖动(JS 改变图层的 Transform),当用户手指离开屏幕时会触发一个 Transform 动画,把图层对齐到最靠近的图片边缘。所以这个 Layer 本身就具备 Inline Transform 的属性,当它触发一个 Transform 动画时,同时具备了 Transform Animation 的条件。而中部的“要闻”和“左滑更多”也生成了 Composited Layer:

  1. “要闻” 所在的元素具有 position:relative 属性,所以会生成 Render Layer,然后因为条件 2,同时生成 Composited Layer;
  2. “左滑更多”也是类似,它所在的元素具有 position:absolute 属性;

但是在早期版本的 Chrome,在上面图片显示的 Layer 没有触发 Transform 动画前,“要闻”和“左滑更多”实际上是不生成 Composited Layer 的,也就是说当用户手指拖动图片一段距离并离开屏幕后,Transform 动画触发的瞬间,“要闻”和“左滑更多”才生成 Composited Layer,而浏览器这时必须先更新 Composited Layer 树,然后重新光栅化变化的区域,动画才能够真正运行 (实际上除了“要闻”和“左滑更多”外,下方还有更多的使用 position:relative 元素会生成 Composited Layer),也就是说动画从触发到运行有一个明显用户可感知的延迟(一百甚至几百毫秒),另外通过 Timer 自行触发的 Transform 动画也有同样的问题,虽然用户可能感知不明显,不过如果用户正在惯性滚动页面的话,也有可能 CPU 被内核/光栅化线程所占据而导致卡顿。用户拖动图片的过程中性能也可能会受到影响,因为每一次移动都需要遍历 Render Layer 树重新计算 Overlap,即使并没有发生 Overlap 的变化。Chrome 新版本的处理方式,“要闻”和“左滑更多”一开始就会生成 Composited Layer,这样动画的触发就不会引起 Composited Layer 树的变化,所以从触发到运行的延迟就比早期版本低了很多。

但是 Chrome 新版本的处理方式也会造成比较明显的副作用,它有可能导致生成大量的 Composited Layer,在改变了对 Inline Transform 的 Overlap 处理方式后,只要有一个 Composited Layer 有 Inline Transform ,它就有可能会导致事先生成大量的 Composited Layer(跟它层级相同或者层级在它之后的 Render Layer 都会生成),即使我们永远也不会改变它的 Transform 的值,也就是说这些额外生成的 Composited Layer 是完全无用的。而大量的 Composited Layer 不但会占用更多内存,也会明显影响合成器的合成性能,导致动画,惯性滚动的性能下降

Chrome 使用了一种叫做图层合并(Layer Squashing)的技术来减轻上述副作用的影响,但是图层合并只在一定的条件下才会生效,我们实际测试还是会发现,在一些主流站点,比如天猫,百度图片,在 Chrome 修改处理逻辑后还是会导致 Composited Layer 数量的明显增加,也确实对性能造成一定的影响(Composited Layer 数量的增加对合成性能的影响在中低端手机上会比较明显)。

所以 Chrome 新旧版本对 Inline Transform Overlap 的处理方式,可能会在不同的网页或者同一个网页的不同使用场景造成不同的影响,有些可能是正面的,有些可能是负面的。有时浏览器本身很难兼顾不同场景的性能,它只能选择一个,放弃另外一个。比如这次它就只能选择:

a) 生成更多的 Composited Layer 来避免未来可能的 Composited Layer 树的变化带来的性能惩罚,但是 Composited Layer 数量的增加又有可能会导致整体合成性能的下降
b) 只生成当前必要的 Composited Layer,但是未来更容易遭遇到 Composited Layer 树的变化带来的性能惩罚

但是对于页端来说,优化页面的写法,就比较容易在不同的处理方式下都获得最好的性能表现。

再以手机凤凰网为例子,“要闻”和“左滑更多”实际上不需要使用 position:relative 和 position:absolute 也能实现同样的布局效果,而不使用 position:relative 和 position:absolute,浏览器就不会生成 Render Layer,也就不需要参与 Overlap 的计算,它们永远也不会生成 Composited Layer,这样即不影响上面图片 Layer 的拖动/动画性能,也减少了整个网页的 Composited Layer 数量,减少 Render Layer 树遍历,Overlap 计算的开销,提升了整个合成器甚至是内核重排版的性能。

所以对页端来说,可以做的页面优化包括:

  1. 避免不必要的 Inline Transform,特别是实际上不会改变元素的 Transform 属性的时候
  2. 避免不必要生成 Render Layer,因为只要生成 Render Layer,即使元素自身不满足 Composited Layer 的条件,也有可能因为 Overlap 的可能性而被动生成 Composited Layer(即使没有真正 Overlap)。在满足布局需要的条件下,尽可能让静态不会频繁发生变化或者移动的元素直接绘制在 Root Layer 上面,比方说尽可能优先使用默认的 position:static,避免滥用 position:relative/absolute;
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页