问题1:我们使用left/top来看看动画的性能
body {
padding: 30px;
text-align: center;
}
.container {
position: relative;
min-height: 400px;
-webkit-tap-highlight-color: rgba(0,0,0,0);
}
.ball {
position: absolute;
top: 0;
left: 0;
width: 100px;
height: 100px;
border-radius: 50%;
box-shadow: 0 0 5px 0 rgba(0, 0, 0, 0.75);
}
.ball:nth-last-child(1) {
background-image: url('https://s3-us-west-2.amazonaws.com/s.cdpn.io/123941/splogo.png');
background-position: center center;
background-repeat: no-repeat;
}
.ball-running {
-webkit-animation: run-around 4s infinite;
animation: run-around 4s infinite;
}
@keyframes run-around {
0%: {
top: 0;
left: 0;
}
25% {
top: 0;
left: 200px;
}
50% {
top: 200px;
left: 200px;
}
75% {
top: 200px;
left: 0;
}
}
我们的页面结构如下:
<div class="container">
<div class="ball" id="ball"></div>
</div>
然后我们看看JS代码:
var balls = document.getElementById('ball');
balls.classList.add("ball");
balls.classList.add('ball-running');
我们从控制台来看看动画的执行性能:
首先我们看到动画运行的时候一直有一个绿色框,这表示重绘的区域,而且是一直重绘的:
第二步:我们看看这个动画的时间分布
我们可以看到页面重绘的时间是107.3ms,二页面回流的时间是186.8ms
第三步:我们看看具体的事件步骤
我们可以看到每次都会重新计算样式,位置大小等,然后更新每一层的元素的相关信息,然后合成每一层的内容显示。这将导致很严重的重绘和回流,对于性能的消耗要大得多。注意:这里只有一个图层,就像PS图层一样,我们只是会重新绘制图层中的元素,而不是整个图层本身,因此图层本身并没有变成绿色。
问题2:我们看看使用transform实现的动画性能
修改CSS样式如下:
body {
padding: 30px;
text-align: center;
}
.container {
position: relative;
min-height: 400px;
-webkit-tap-highlight-color: rgba(0,0,0,0);
}
.ball {
position: absolute;
top: 0;
left: 0;
width: 100px;
height: 100px;
border-radius: 50%;
box-shadow: 0 0 5px 0 rgba(0, 0, 0, 0.75);
}
.ball:nth-last-child(1) {
background-image: url('https://s3-us-west-2.amazonaws.com/s.cdpn.io/123941/splogo.png');
background-position: center center;
background-repeat: no-repeat;
}
.ball-running {
animation: run-around 4s infinite;
}
@keyframes run-around {
0% {
transform: translate(0, 0);
}
25% {
transform: translate(200px, 0);
}
50% {
transform: translate(200px, 200px);
}
75% {
transform: translate(0, 200px);
}
}
第一步:我们看到浏览器为该元素产生了一个独立的层
第二步:我们看看总时间分布
可以看到重绘的时间明显减少
第三步:看看事件的顺序
我们看到这里主要牵涉到样式计算,然后更新特定层的元素,最后再合成就可以了,明显没有了前面的重绘和回流操作。注意:因为这里是transform属性的改变,如果是改变left/top值,那个这个图层还是会发生回流和重绘操作的。
浏览器接收到页面文档后,会将文档中的标记语言解析为DOM树。DOM树和CSS结合后形成浏览器构建页面的渲染树。渲染树中包含了大量的渲染元素,每一个渲染元素会被分到一个图层中,每个图层又会被加载到GPU形成渲染纹理,而图层在GPU中 transform 是不会触发 repaint 的(只是重新计算元素的样式,然后根据计算的样式更新特定层的元素,然后把更新后的层和其他层合成起来,这个过程中是没有repaint的),这一点非常类似3D绘图功能,最终这些使用 transform 的图层都会由独立的合成器进程进行处理。
注意:因为上面我们使用的是2D变化,而不是3D变换。3D 和 2D transform 的区别就在于,浏览器在页面渲染前为3D动画创建独立的复合图层,而在运行期间为2D动画创建。动画开始时,生成新的复合图层并加载为GPU的纹理用于初始化 repaint。然后由GPU的复合器操纵整个动画的执行。最后当动画结束时,再次执行 repaint 操作删除复合图层。所以,上面的例子在开始和结束的时候会有两次repaint操作。
问题2:不要弄混淆,即使单独创建了一个图层,如果变动了重绘和重排的指标,也是会回流和重排的
例子的代码如下:
.box{
width:100px;
height:100px;
background-color: #ccc;
position:absolute;
top:0;
left:0;
transition:left 5s linear;
transform: translateZ(0);
/*这时候虽然给我们的box元素放在一个独立的层当中,但是我们依然修改了他的left属性,所以他肯定会重绘回流,除非我们修改为transform属性和opacity*/
}
.hv{
left:400px;
}
JS代码如下:
window.οnlοad=function(){
var box=document.querySelector('.box');
box.classList.add('hv');
}
DOM结构如下:
<div class="box"></div>
这时候我们通过了transform:translateZ为这个元素创建了一个独立的图层,但是我们的transition依然是通过left来完成的,
这时候也是会发生回流,重绘,合成操作的。但是这时候的回流,重绘,合成操作都是在该图层中进行的,而不会影响其他的图层。
我们把上面的动画使用transform来完成
.box1{
width:100px;
height:100px;
background-color: #ccc;
position:absolute;
top:0;
left:0;
transition:transform 5s;
-webkit-transition: -webkit-transform 2s;
-webkit-backface-visibility:hidden;
transform: translate3d(0,0,0);
-webkit-perspective:1000;
}
.hv1{
transform: translate3d(200px,0,0);
}
修改CSS属性如上,我们看看结果如何
这时候我们看到已经没有前面说的重绘和回流了,这就是transform动画的优秀之处。使用tranforms来实现移动效果的元素将会被正常绘制,同时不会触发对其他元素的绘制。这种处理方式和思想跟图像处理软件(比如Sketch/GIMP/Photoshop)是一致的,它们都是可以在图像中的某个单个图层上做操作,最后合并所有图层得到最终的图像。摘抄自简化绘制的复杂度、减小绘制区域