动画性能优化

11,013次阅读
没有评论

共计 3444 个字符,预计需要花费 9 分钟才能阅读完成。

动画性能不仅在前端, 在任何客户端技术中心都是性能的重灾区, 归根到底是需要大量的计算和渲染工作, 远超普通的静态 UI.

在前端实现动画有三种主流的方式:

当然,DOM+js 的这种方式由于极易引起浏览器重绘或者回流, 有非常大的性能风险, 对于这种动画的优化方法就是不用 DOM 进行动画操作.

CSS3 动画优化原理

要想进行 CSS 的动画优化必须了解一定的浏览器原理, 我们会介绍浏览器原理的几个概念, 图层、重绘、回流。

图层

浏览器在渲染一个页面时,会将页面分为很多个图层,图层有大有小,每个图层上有一个或多个节点。在渲染 DOM 的时候,浏览器所做的工作实际上是:

  • 获取 DOM 后分割为多个图层
  • 对每个图层的节点计算样式结果(Recalculate style–样式重计算)
  • 为每个节点生成图形和位置(Layout–回流和重布局)
  • 将每个节点绘制填充到图层位图中(Paint Setup 和 Paint–重绘)
  • 图层作为纹理上传至 GPU
  • 符合多个图层到页面上生成最终屏幕图像(Composite Layers–图层重组)

回流

有些节点,当你改变他时,会需要重新布局(这也意味着需要重新计算其他被影响的节点的位置和大小)。

这种情况下,被影响的 DOM 树越大(可见节点),重绘所需要的时间就会越长,而渲染一帧动画的时间也相应变长。所以需要尽力避免这些属性

一些常用的改变时会触发重布局的属性:

盒子模型相关属性会触发重布局:

  • width
  • height
  • padding
  • margin
  • display
  • border-width
  • border
  • min-height

定位属性及浮动也会触发重布局:

  • top
  • bottom
  • left
  • right
  • position
  • float
  • clear

改变节点内部文字结构也会触发重布局:

  • text-align
  • overflow-y
  • font-weight
  • overflow
  • font-family
  • line-height
  • vertival-align
  • white-space
  • font-size

重绘

修改时只触发重绘的属性有:

  • color
  • border-style
  • border-radius
  • visibility
  • text-decoration
  • background
  • background-image
  • background-position
  • background-repeat
  • background-size
  • outline-color
  • outline
  • outline-style
  • outline-width
  • box-shadow

这些属性都不会修改节点的大小和位置,自然不会触发重布局,但是节点内部的渲染效果进行了改变,所以只需要重绘就可以了.

CSS3 动画优化

经过上面的介绍, 我们大致了解了浏览器的绘制原理, 那么想进行 css 动画优化就要遵循以下原则:

  1. 尽量将动画放在一个独立图层, 这样可以避免动画效果影响其他渲染层的元素
  2. 尽量避免回流和重绘
  3. 尽量使用 GPU, 速度更快

因此, 我们需要创建独立的合成层.

那么如何才能创建合成层呢?

直接原因(direct reason):

  • 硬件加速的 iframe 元素(比如 iframe 嵌入的页面中有合成层)demo
  • video 元素
  • 覆盖在 video 元素上的视频控制栏
  • 3D 或者 硬件加速的 2D Canvas 元素
    • demo:普通 2D Canvas 不会提升为合成层
    • demo:3D Canvas 提升为合成层
  • 硬件加速的插件,比如 flash 等等
  • 在 DPI 较高的屏幕上,fix 定位的元素会自动地被提升到合成层中。但在 DPI 较低的设备上却并非如此,因为这个渲染层的提升会使得字体渲染方式由子像素变为灰阶
  • 有 3D transform
  • backface-visibility 为 hidden
  • 对 opacity、transform、fliter、backdropfilter 应用了 animation 或者 transition(需要是 active 的 animation 或者 transition,当 animation 或者 transition 效果未开始或结束后,提升合成层也会失效)
  • will-change 设置为 opacity、transform、top、left、bottom、right(其中 top、left 等需要设置明确的定位属性,如 relative 等)demo
  • 后代元素原因:
    • 有合成层后代同时本身有 transform、opactiy(小于 1)、mask、fliter、reflection 属性 demo
    • 有合成层后代同时本身 overflow 不为 visible(如果本身是因为明确的定位因素产生的 SelfPaintingLayer,则需要 z-index 不为 auto)demo
    • 有合成层后代同时本身 fixed 定位 demo
    • 有 3D transfrom 的合成层后代同时本身有 preserves-3d 属性 demo
    • 有 3D transfrom 的合成层后代同时本身有 perspective 属性 demo

提升合成层的最好方式是使用 CSS 的 will-change 属性。从上一节合成层产生原因中,可以知道 will-change 设置为 opacity、transform、top、left、bottom、right 可以将元素提升为合成层。

关于合成层的更多知识可以移步淘宝 FED 的 无线性能优化:Composite

那么如何避免重绘和回流?

具体而言, 就是多使用 transform 或者 opacity 来实现动画效果, 上述方法在合成层使用不会引起重绘和回流.

那么如何利用 GPU 加速呢?

以下几个属性会获得 GPU 加速

  • opacity
  • translate
  • rotate
  • scale

Canvas 动画优化

CSS 虽然更加简单也更加保证性能的下限, 但是要想实现更加复杂可控的动画, 那就必须用到 Canvas+JavaScript 这个组合了.

Canvas 作为浏览器提供的 2D 图形绘制 API 本身有一定的复杂度, 优化的方法非常多, 我们仅仅介绍几种比较主流的优化方式.

运用requestAnimationFrame

很多时候我们会使用 setInterval 这种定时器来完成 js 动画循环, 但是定时器在单线程的 js 环境下并不可靠, 并不是能保证严格按照开发者的设置来进行动画循环, 因此很多时候 setInterval 会引起掉帧的情况.

因此 requestAnimationFrame 的优势就体现出来了:

  • 性能更好: 优点是它能够将所有的动画都放到一个浏览器重绘周期里去做,这样能保存你的 CPU 的循环次数, 提高性能
  • 开销更小: requestAnimationFrame 是由浏览器专门为动画提供的 API,在运行时浏览器会自动优化方法的调用,并且如果页面不是激活状态下的话,动画会自动暂停,有效节省了 CPU 开销

离屏 canvas

离屏渲染的原理是把离屏 canvas 当成一个缓存区。把需要重复绘制的画面数据进行缓存起来,减少调用 canvas 的 API 的消耗:

  1. 创建离屏 canvas;
  2. 设置离屏 canvas 的宽高;
  3. 在离屏 canvas 中进行绘制;
  4. 在离屏 canvas 的全部或部分绘制到正在显示的 canvas 上

避免浮点运算

利用 canvas 进行动画绘制时,如果计算出来的坐标是浮点数,那么可能会出现 CSS Sub-pixel 的问题,也就是会自动将浮点数值四舍五入转为整数,那么在动画的过程中,由于元素实际运动的轨迹并不是严格按照计算公式得到,那么就可能出现抖动的情况,同时也可能让元素的边缘出现抗锯齿失真 这也是可能影响性能的一方面,因为一直在做不必要的取证运算.

减少调用 Canvas API

canvas 也是通过操纵 js 来绘制的,但是相比于正常的 js 操作,调用 canvas API 将更加消耗资源,所以在绘制之前请做好规划,通过 适量 js 原生计算减少 canvas API 的调用是一件比较划算的事情.

比如, 作粒子效果时,尽量少使用圆,最好使用方形,因为粒子太小,所以方形看上去也跟圆差不多。至于原因,很容易理解,我们画一个圆需要三个步骤:先 beginPath,然后用 arc 画弧,再用 fill 进行填充才能产生一个圆。但是画方形,只需要一个 fillRect 就可以了。虽然只是差了两个调用,当粒子对象数量达到一定时,这性能差距就会显示出来了。

web worker

在进行某些耗时操作,例如计算大量数据,一帧中包含了太多的绘制状态,大规模的 DOM 操作等,可能会导致页面卡顿,影响用户体验.

web worker 最常用的场景就是大量的频繁计算,减轻主线程压力,如果遇到大规模的计算,可以通过此 API 分担主线程压力,此 API 兼容性已经很不错了,既然 canvas 可以用,那 web worker 也就完全可以考虑使用.

    正文完
     0
    Yojack
    版权声明:本篇文章由 Yojack 于1970-01-01发表,共计3444字。
    转载说明:
    1 本网站名称:优杰开发笔记
    2 本站永久网址:https://yojack.cn
    3 本网站的文章部分内容可能来源于网络,仅供大家学习与参考,如有侵权,请联系站长进行删除处理。
    4 本站一切资源不代表本站立场,并不代表本站赞同其观点和对其真实性负责。
    5 本站所有内容均可转载及分享, 但请注明出处
    6 我们始终尊重原创作者的版权,所有文章在发布时,均尽可能注明出处与作者。
    7 站长邮箱:laylwenl@gmail.com
    评论(没有评论)