导语: 几个星期前,因为公司活动,需要做一个比较炫酷的
H5
动画, 一顿猛如虎的coding
之后,发现运行起来卡成狗,当时绝望的想要出家,还好组内有经验比较丰富的动画大神帮忙调整,顺利度过难关。
作为一个有追求的程序员!!!痛定思痛!!!同样的悲剧绝对不能在同一个技术面发生两次!!!流着泪写下该篇总结,希望可以给大家一些启发。
本文目录:
为什么动画会有卡顿感?
回答这个问题前,我们应该要明白一个问题,为什么我们会感到动画在动?
- 动画的实现原理,是利用了人眼的“视觉暂留”现象,在短时间内连续播放数幅静止的画面,使肉眼因视觉残象产生错觉,而误以为画面在“动”。
- 大多数设备的刷新频率是 60 次/秒,(1000/60 = 16.6ms)也就说是浏览器对每一帧画面的渲染工作要在 16ms 内完成,超出这个时间,页面的渲染就会出现卡顿现象,影响用户体验。
综上,我们或觉得动画卡,一个很大的原因就是因为 FPS
(帧率) 过低导致,也就是说每一帧的画面不能的保证在16ms之内完成渲染工作,所以我们会觉得卡顿。
解释清楚了会感到卡顿的原因,大家是不是很好奇每一帧的渲染过程到底发生了什么会导致它的渲染时间超过 16ms ?
现在进入到我们的第二部分,浏览器渲染绘制过程
浏览器渲染绘制过程
以Webkit
的渲染流程为例分析下浏览器:
简单概括为如下几步:
-
处理HTML标记数据并生成DOM树。
-
处理CSS标记数据并生成CSSOM树。
-
将DOM树与CSSOM树合并在一起生成渲染树。
-
Layout(布局):计算每个 DOM 元素在最终屏幕上显示的大小和位置。由于 web 页面的元素布局是相对的,所以其中任意一个元素的位置发生变化,都会联动的引起其他元素发生变化,这个过程叫
reflow
(回流 or 重排)。 -
Paint(绘制):在多个层上绘制 DOM 元素的的文字、颜色、图像、边框和阴影等。
-
composite(渲染层合并):按照合理的顺序合并图层然后显示到屏幕上。
每一帧的渲染经过如上步骤,呈现在用户的眼前,当这些步骤时间的总和 > 16ms, 用户就会有卡顿感产生。
在 动画性能分析 部分,会重点结合Layout
, Paint
, composite
部分来具体分析动画卡顿的原因及优化方式,在此之前,我们先了解下强大的 chrome
提供的性能分析工具,以便我们更好分析问题。
强大的chrome性能分析工具介绍
-
performance
使用 Performance 工具时,为了规避其它 Chrome 插件对页面的性能影响,我们最好在无痕模式下打开页面
点击左上角实心圆开始录制,看下性能分析面板。
重点介绍下图中标红处:
FPS:这是一个和动画性能密切相关的指标,它表示每一秒的帧数。图中绿色柱状越高表示帧率越高,体验就越流畅。若出现红色块,则代表长时间帧,很可能会出现卡顿。图中以绿色为主,偶尔出现红块,说明网页性能并不糟糕,但仍有可优化的空间。
CPU:表示CPU的使用情况,不同的颜色片段代表着消耗CPU资源的不同事件类型。这部分的图像和下文详情面板中的Summary内容有对应关系,我们可以结合这两者挖掘性能瓶颈。
summary: 渲染过程中,每个部分的耗时占比。
-
Layers
是不是已经被这个高逼格的界面深深震撼到了!!!
重点看下 Paint flashing
, Layer borders
, FPS meter
:
-
Paint flashing: 标记当前正在重绘的元素,(元素会被一个绿色的半透明遮罩蒙上),如上图;
-
Layer borders: 复合层
- 黄色边框: 有动画 3d 变换的元素,表示放到了一个新的复合层(composited layer)中渲染。
- 蓝色的栅格:这些分块可以看作是比层更低一级的单位,这些区域就是 RenderLayer。
-
FPS meter: GPU性能监控
- Frame Rate: 帧率。
- GPU Raster:GPU 光栅(默认开启)。
- GPU Memory: GPU 使用率。
综上就是对
chrome
一些性能分析工具的简单介绍,说了这么多!!!让我们写一些动画实操一下吧!!!
下面就进入我们最最重要的部分!!!动画性能分析实战
动画性能分析实战
在浏览器中打开如下代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
.react {position: absolute;width: 100px;height: 100px;background: #f00;animation: react-run 3s linear 0s infinite;}
@keyframes react-run {
0% { top: 0px;left: 0px;}
25% {top: 0px;left: 200px;}
50% {top: 200px;left: 200px;}
75% {top: 200px;left: 0px;}
100% {top: 0px;left: 0px;}
}
</style>
</head>
<body>
<div class="react"></div>
</body>
</html>
运行结果:
-
Layers
-
performance
性能分析:
-
图中移动的小方块在不停的重排重绘过程中。
-
GPU 的内存使用率为2.4 ~ 4.9 之间。
-
一秒内重排和重绘的耗时分别为
7.3ms
和4.9ms
。
这里还有个值得注意的点:
- 每次元素移动到光栅处,内存都会变大一倍从
2.4
变为4.9
。
如下图:
图层被光栅化后,分块存入GPU
的内存中,当元素跨光栅移动时,两块内存都在变化,所以消耗自然加倍啦!!!
好,现在让我们开启传说中的 3D
引擎加速!!!
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
.react {position: absolute;
width: 100px;
height: 100px;
background: #f00;
animation: react-run 3s linear 0s infinite;
transform: translate3d(0, 0, 0);
}
@keyframes react-run {
0% {transform: translate3d(0, 0, 0);}
25% {transform: translate3d(200, 0, 0);}
50% {transform: translate3d(200, 200, 0);}
75% {transform: translate3d(0, 200, 0);}
100% {transform: translate3d(0, 0, 0);}
}
</style>
</head>
<body>
<div class="react"></div>
</body>
</html>
运行结果:
-
Layers
-
performance
性能分析:
-
图中移动的小方块没有发生重排和重绘,只有一个图层合并。
-
GPU 的内存使用率几乎为0。
-
一秒内重排和重绘的耗时分别为
0ms
和0ms
,图层合并时间为89us
。
比较上述两种写法可以得处: 开启
GPU
加速时的动画性能要比不开启高效。
- 未开启
GPU
加速:
- 开启
GPU
加速:
写到这里,大家可能会有疑问,为什么开启GPU
加速就没有发生重排和重绘 ?是不是所有动画全部都开启GPU
加速,就会变的很快?
下面就来分析这两个问题:
1. 为什么开启GPU
加速就没有发生重排和重绘 ?
观察上图:
在开启GPU
加速时,运动的红色方块开启了新的合成层,所以不用再重排
和重绘
,只需要一个合成图层的时间。
2. 是不是所有动画全部都开启GPU
加速,就会变的很快?
因为开启GPU
加速后会建立新的图层,新的图层就需要一定的内存空间,而且图层在合成时,图层越多耗费的时间肯定也是越多的,所以疯狂的开启GPU
加速,不但不能解决性能问题,反而可能会带来性能问题。
如下 (GPU
内存使用较高):
综上所述: 合理的开启
GPU
加速,建立新的合成层,可以给性能带来很大的提升。
那么,都有那些放法建立新的合成层呢?
亲测有效的方法:
-
使用
css
属性:1. transform: translate3d(0, 0, 0); 2. will-change: '将会发生变化的属性';
-
标签:
<video></video> <canvas></canvas>
总结
通过此篇文章,我们应该对动画性能分析有一个比较系统的认识:
- 为什么会有卡顿感产生?
- 浏览器每一帧的渲染工作是怎么进行的?
- 如何使用
chrome
开发工具对动画性能进行分析? GPU
加速和普通渲染的区别在哪里?- 我们应该如何开启
GPU
加速?
动画卡顿问题的分析,不像是查找
js
上的bug
,有逻辑可寻,所以在遇到问题时,我们只有明白底层的原理,结合分析工具,才能更好的发现问题。