前端性能-动画性能

前言:

我们都知道浏览器是单线程运行的,虽然说浏览器执行js是单线程执行(注意:不是说浏览器只有一个线程),实际上浏览器的两个个重要的执行线程,这 两 个线程协同工作来渲染网页:主线程和合成线程。

相应地,合成线程负责:通过 GPU 将位图绘制到屏幕上;通知主线程更新页面中可见或即将变成可见的部分的位图;计算出页面中哪部分是可见的;计算出当你在滚动页面时哪部分是即将变成可见的;当你滚动页面时将相应位置的元素移动到可视区域。

而我们经常遇到的动画卡顿就是主线程和合成线程的调度不合理。

渲染过程:

网页动画的每一帧都是一次重新渲染,每秒低于24帧的动画,人眼就能感受到停顿,每秒30-60帧才比较流畅 浏览器会按照大多数显示器的刷新频率60Hz来刷新动画, 如果想达到60FPS,就意味着每一帧的任务耗时不能高于16.67ms。

浏览器渲染每一帧的过程:
在这里插入图片描述
大致的渲染阶段:

  • 解析html建立dom树
  • 解析css构建render树(将CSS代码解析成树形的数据结构,然后结合DOM合并成render树)
  • 布局render树(Layout/reflow),负责各元素尺寸、位置的计算
  • 绘制render树(paint),绘制页面像素信息
  • 浏览器会将各层的信息发送给GPU(GPU进程:最多一个,用于3D绘制等),GPU会将各层合成(composite),显示在屏幕上。
  1. JavaScript:使用 JavaScript 来实现一些视觉变化的效果。比如用 jQuery 的 animate 函数做一个动画、对一个数据集进行排序或者往页面里添加一些 DOM 元素等;
  2. Style:样式计算,此过程是根据匹配选择器计算出哪些元素应用哪些 CSS3 规则的过程,将应用规则并计算每个元素的最终样式。
  3. Layout:知道对一个元素应用哪些规则之后,浏览器即可开始计算它要占据的空间大小及其在屏幕的位置,网页的布局模式意味着一个元素可能影响其他元素。
  4. Paint:绘制是填充像素的过程。它涉及绘出文本、颜色、图像、边框和阴影,基本上包括元素的每个可视部分。绘制一般是在多个表面(通常称为层)上完成的。
  5. Composite:由于页面的各部分可能被绘制到多层,由此它们需要按正确顺序绘制到屏幕上,以便正确渲染页面。

“生成布局”(flow)和 “绘制”(paint)这两步,合称为"渲染"(render)。重新渲染就是需要重新生成布局和重新绘制。

重排和重绘会不断触发,这是不可避免的,还会消耗大量的CPU和GPU资源,前端性能优化最主要的优化点就是尽量减少重绘和重排。这个可以了阅读之前写的一篇文章重绘和重排


影响渲染性能的因素:

影响网页渲染的因素,样式表越简单,重绘和重排越快,重绘和重排的DOM元素层级越高,成本就越高,因此我们在开发前端页面时就需要精简DOM元素,合理布局。

提高渲染性能的操作(重绘和重排):

  • DOM元素读写分离;
  • 让进行大量动画的元素脱离文档流,减少重排开销;
  • 通过改变元素的class或csstext一次性的更改样式;
  • 缓存DOM元素的位置信息,避免不必要的属性读取;
  • 尽量使用离线DOM;
  • 避免使用table布局;
  • 使用css3 transform优化动画性能;

回归动画:

上面说到使用css3的transform,下面就解释下为啥推荐使用transform?

transform为我们提供了丰富的api,例如scale,translate,rotate, skew等等。这是通过修改CSS 可视格式模型的坐标空间来实现的。

css3 transform 的执行效率:

参考别人的一个例子:

<!-- height-->

div { height: 100px; transition: height 1s linear; }

div:hover { height: 200px; } 

<!-- transform  -->

div { transform: scale(0.5); transition: transform 1s linear; }

div:hover { transform: scale(1.0); }
  • 通过改变height属性的动画渲染过程:
    在这里插入图片描述
  • 通过transform的scale进行动画的渲染过程:
    在这里插入图片描述
    将每一帧的变化浏览器都在进行布局、绘制、把新的位图交给 GPU 内存,但是在将位图加载到GPU内存中的操作是个相对耗时的操作。

通过两者之间的渲染过程比较可知:transform的动画并没有每次经过主线程的绘制然后再同步到GPU当中,而是直接交给GPU去做这件事,专业的事情交给专业的人去做。我们也可以借助chrome devtools的performance工具对比二者的性能差异。


GPU擅长的事情:

  • 绘制位图到屏幕上
  • 可不断的绘制相同的位图
  • 将同一位图进行位移、旋转、缩放

所以为啥推荐使用transform?

在使用css3 transtion做动画效果时,transform实现的动画是与合成器线程相关的,不需要等待主线程样式计算或者 JS 执行,计算速度是很快的;而使用height,width,margin和padding时,导致布局和绘制的调整,主线程需要重新计算样式并且执行JS,然后再同步到GPU绘制,这当然就慢了。

层的引入:

在页面的渲染过程中,浏览器还具有一系列并不直接暴露给开发者的页面中间表示方式。这些表示方式中最重要的结构就是层。

这些层分别是掌管DOM子树的渲染层(RenderLayer)以及掌管渲染层子树的图形层(GraphicsLayer),某些特殊的渲染层会被认为是合成层(Compositing Layers,合成层拥有单独的 GraphicsLayer。

拥有单独GraphicsLayer的层,都会将位图存储在共享内存中,作为纹理上传到 GPU 中,最后由 GPU 将多个位图进行合成,然后 绘制到屏幕上。

将渲染层提升为合成层的方式:

  • 进行3D或者透视变换的CSS属性;
  • 具有3D(WebGL)上下文或者硬件加速的2D上下文的元素;
  • 组合型插件(即Flash);
  • 具有有CSS透明度动画或CSS变形动画;
  • 使用了硬件加速的CSS Filters技术(有的文献中表示filters属性并没有提升为合成层的效果,推测只有一部分filters滤镜效果需要使用硬件加速,并非所有);
  • 使用了剪裁Clip或者反射Reflection,并且它的后代中包含一个合成层;
  • 子元素中存在具有组合层的元素的元素(换句话说,就是存在具有自己的层的子元素的元素);
  • 同级元素中有Z索引比其小的元素,而且该Z索引比较小的元素具有组合层(换句话说就是在组合层之上进行渲染的元素);

提升为合成层的优势:

  • 合成层的位图,会交由 GPU 合成,比 CPU 处理要快;
  • 当需要 repaint 时,只需要 repaint 本身,不会影响到其他的层;
  • 对于 transform 和 opacity 效果,不会触发 layout 和 paint;

requestAnimationFrame:

window.requestAnimationFrame() 告诉浏览器——你希望执行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。该方法需要传入一个回调函数作为参数,该回调函数会在浏览器下一次重绘之前执行。

这个api比setTimeout和setInterval更加适用于动画的制作,跟随浏览器每帧的变化进行每一帧动画的改变,给人感受到动画更加流畅自然,不会受一些同步的代码之类的影响到动画流畅性。

注意:若你想在浏览器下次重绘之前继续更新下一帧动画,那么回调函数自身必须再次调用window.requestAnimationFrame();

最终:

附上Chrome Devtool Performance的食用方法,让大家去分析页面运行时的性能表现。

全新Chrome Devtool Performance使用指南

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值