现在假设有个需求,要我们做三个方块,其中第三个方块要从初始位置往左滑动
先实现基础功能
我们先回顾一下简单的CSS动画是如何实现的:
此时的代码为:
<html>
<head></head>
<body>
<div id="a" class="area"></div>
<div id="b" class="area"></div>
<div id="c" class="area"></div>
</body>
</html>
<style>
.area {
width: 50px;
height: 50px;
margin-bottom: 10px;
}
#a {
background-color: red;
}
#b {
background-color: blue;
}
#c {
background-color: green;
animation: slidein 5s infinite;
}
@keyframes slidein {
from {
margin-left: 0;
}
to {
margin-left: 100px;
}
}
</style>
优化思路
我们通过改变margin的方式让第三个方块动了起来,好了工作完成了,该下班回家了。。。才怪,PM嫌咱们做的动画在老设备上太卡,要提高流畅度,我们该怎么优化咧?
在着手优化之前,我们得先想明白,为什么这种方式是低效的。我们都知道,浏览器渲染会进行重排和重绘,一旦文档结构发生变更,便会牵一发而动全身,导致元素布局重新计算,如果元素之间关联紧密且变动频繁,便容易出现卡顿。故而我们在进行CSS修改的时候,往往会刻意避免大量的结构变更,并且尽可能将多次CSS操作合并起来。
回到我们的动画上,动画元素天然就是不停在动的,浏览器也就不得不反复的进行着计算工作。那我们该怎么尽可能减少计算压力呢?很简单,让动的元素超脱出来,不干扰其他元素的布局即可。
用绝对定位可以不
我们脑子里一瞬间就闪出了万恶的绝对定位:
<style>
.area {
position: absolute;
left: 10px;
width: 50px;
height: 50px;
}
#a {
top: 10px;
background-color: red;
}
#b {
top: 65px;
background-color: blue;
}
#c {
top: 120px;
background-color: green;
animation: slidein 5s infinite;
}
@keyframes slidein {
from {
left: 10px;
}
to {
left: 100px;
}
}
</style>
绝对定位的元素确实脱离文档流了,但它真能让动画元素超脱出来,减轻浏览器负担么,我们可以用Chrome的Layers视图来看一下:
(控制台 => more => More tools => Layers)
右侧的立体图只看截图不太直观,但左侧的图层我们可以看到只有文档一层,元素并没有成功完成"飞升",这种情况下浏览器的计算工作依然巨大,所以单纯的绝对定位等脱离文档流的方式,并不能满足我们的需求呢。
如果能让动的元素成为单独的一层,其他不动的当做背景层,才算真正脱钩呢,那该怎么实现呢?
让GUP上吧
我们把CSS动画实现的代码再来改版一下:
@keyframes slidein {
from {
transform: translateX(0);
}
to {
transform: translateX(100px);
}
}
此时我们再来看一下Layers:
可以看到已经变成两层了呢,我们再换个角度截图:
可以看到绿色区域C已经完全脱离了原图层了呢,这是利用了CSS translate3d属性会使用GUP渲染的原理。
所以到目前为止我们终于可以得出一个有用的结论:动画元素尽可能用GUP渲染,最简单的是使用translate3d属性。
就这?
这种基础知识点确实没必要长篇大论说这么多,咱们接下来说说隐患。在我们的需求里,A和B元素都是不动的,只有最底层的C需要动起来,那么我们把需求改成让B动,C不用动了,图层的视角会怎么样咧?
<style>
.area {
position: absolute;
left: 10px;
width: 50px;
height: 50px;
}
#a {
top: 10px;
background-color: red;
}
#b {
top: 65px;
background-color: blue; /* 只有B在动哦 */ animation: slidein 5s infinite;
}
#c {
top: 120px;
background-color: green;
}
@keyframes slidein {
from {
transform: translateX(0);
}
to {
transform: translateX(0);
}
}
</style>
这貌似不对呀?只有B用GUP提升了,为啥会有三个图层,而且为啥看立体图C还在B上面?
这里涉及到一个知识点,在都不用GUP提升的时候,所有元素都在一个图层上,文档的上下排列顺序无所谓,不会出现谁在谁上面的问题。但一旦其中一个元素被GUP单独渲染,那么在它文档结构下面的、且脱离了文档流的元素,则也会被单独渲染为一个图层,文档结构越靠下图层层级便越高。
这样会带来什么问题呢?每个图层都将占用相应的内存,图层越多内存占用越多,随着而来的就是卡顿甚至崩溃。
那如何避免生成不想要的图层呢?也很简单,给动画元素加上z-index就可以了。
最佳实践
所以我们针对动画元素应该:
- 让该元素脱离文档流,比如position:relative;
- 让该元素用GUP渲染到独立图层,比如:transform: translateZ(0);
- 给该元素设置z-index: 1;
我们来看看让B动起来的最终代码:
<style>
.area {
width: 50px;
height: 50px;
margin-bottom: 10px;
}
#a {
background-color: red;
}
#b {
position: relative;
z-index: 1;
background-color: blue;
animation: slidein 5s infinite;
}
#c {
background-color: green;
}
@keyframes slidein {
from {
transform: translateX(0);
}
to {
transform: translateX(100px);
}
}
</style>
以及对应图层视图:
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。