如何实现动画?

  • jQuery animation:setTimeout,top/left
  • animatin,transition,transform
  • javascript+canvas/webGL/SVG
  • requestAnimationFrame
  • GPU acceleration(硬件加速)

webkit 的渲染流程

浏览器渲染小结

  • 渲染三个阶段: Layout,Paint,Composite Layers
  • 修改不同 CSS 属性会触发不同阶段
  • 触发的阶段越前,渲染的代价越高

硬件加速(GPU 加速)

  • Texture,即 GPU 传输到 GPU 的一个 Bitmap
  • GPU 能快速对 texture 进行偏移、缩放、旋转、修改透明度等操作

  • 相同之处:两者都有总线和外界联系,有自己缓存体系,以及数字和逻辑运算单元。一句话,两者都为了完成计算任务而设计。

  • 不同之处:CPU 主要负责操作系统和应用程序,GPU 主要负责跟显示相关的数据处理,GPU 的活 CPU 一般都可以干,但是效率低下

Layer 模型

  • 浏览器根据 CSS 属性为元素生成 Layers
  • 将 Layers 作为 texture 上传到 GPU
  • 当改变 Layer 的 transform,opacity 属性时,渲染会跳过 Layout,paint,直接通知 GPU 对 Layer 做变换。

Layer 创建标准

  • 拥有 3d transform 属性
  • 使用 animation,transition 实现 opacity,transform 的动画
  • video
  • canvas
  • Flash
  • 使用 CSS filters 的元素
  • z-index 大于某个相邻节点的 Layer 的元素

为什么使用硬件加速快呢?

  • 如果使用 jquery 或者 js 来做一个 top 移动 100px 的动画,每次都会重主线程发传到合成器线程
  • 但是,如果用 GPU transform 来做的话,主线程只会传一次到合成器线程,其余任务全部是在合成器线程,所以效率比较高。

节省了哪些时间?

  • CPU 进行 Layout 和 paint 的时间
  • CPU 向 GPU 传输位图的时间

完美的 Animation

对眼睛来说:60FPS 更舒服更完美,约等于 16.7ms 内,我们准备好一帧的动画

  • 开始绘制的时间
  • 绘制一帧的时间
    1. setTimeout(callback,1/60) 依靠浏览器内置时钟更新频率,eg、IE8 及以前更新间隔为 15.6,setTimeout 16.7,它需要两个 15.6ms 触发。超过 14.5ms,所以会出现丢帧的现象
      main thread 队列
    2. requestAnimationFrame - 定义绘制每一帧的工作requestAnimationFrame(callback)
  • 自动调节频率 ,callback 工作太多无法再一帧内完成,会自动降低为 30fps,虽然频率降低但比丢帧好。

Layout

触发 Layout

  • 改变 widht,height 等和大小、位置相关的属性
  • 读取 size、positoin 相关的属性

尽量不触发 Layout,使用 transform 代替 top,left 的动画。


但是,如果是这样

我们可以这样改

介绍一个库

fastdom.js

在每一帧,先将读操作批量运行,在批量运行写操作

Layout 小结

  • 不但改变 css 可能导致 Layout,读取位置大小相关属性也会导致 Layout(滚动条也会导致 Layout)
  • 分离读写,减少 Layout
  • 面对解耦代码,可以使用 rAF 推迟的方法分离读写

如何开发不会导致重拍

  • 样式表越简单,重拍和重汇越快
  • 重拍和重绘的 DOM 元素层级越高,成本就越高
  • table 元素的重排和重绘成本,要高于 div 元素
  • 尽量不要把读操作和写操作,放在一个语句里
  • 统一改变样式
  • 缓存重排结果
  • 离线 DOM Fragment/clone
  • 虚拟 DOM React
  • 必要的时候 display:none 不可见元素不影响重排重绘。
  • visibility 对重排影响不影响重绘

Paint

触发 paint

  • 当修改 border-radius,box-shadow,color 等展示相关属性,会触发 paint

paint 的代价

  • continuous painting mode
  • paint prefiler
  • 在经常 paint 的区域,要避免代价太高的 style(比如不要的 gif 图设置 display:none)

减少不必的绘制

  • gif 图即使被其他 Layout 盖住不可见,也可能导致 paint,不需要时应将 gif 图的 display 属性设为 none。轮播图也一样 http://jsbin.com/dizak/3/edit?html,css,output (可以用调试工具去测试性能,z-index:0,也会重排重绘,应该直接设置 display:none)
  • 减少绘制区域,为引起大范围 Paint 的元素生成独立的 Layout 以减小 Paint 的范围

Paint 小结

  • 简化绘制的复杂度
  • 避免不必要的绘制
  • 减少绘制区域

composite Layout

  • GPU 也是有限度的,不要滥用 GPU 资源生成不必要的 Layout
  • 留意以外生成的 Layout

国外一个关于高性能动画研究: https://link.jianshu.com/?t=http%3A%2F%2Fjankfree.org%2F