如何优化动画??

动画基础知识

1. 动画帧率(FPS)计算

帧的定义:1幅画就叫作“1帧”,每秒帧数指的就是“每秒播放的画面数”。每一帧都是静止的图象,快速连续地显示帧便造成了运动的假象。高的帧率能够获得更流畅、更逼真的动画。每秒钟帧数 (fps) 愈多,所显示的动做就会愈流畅。css

理论上说,FPS 越高,动画会越流畅,目前大多数设备的屏幕刷新率为 60 次/秒,因此一般来说 FPS 为 60 frame/s 时动画效果最好,也就是每帧的消耗时间为 16.67ms。css3

直观感觉,不一样帧率的体验:git

  • 帧率可以达到 50 ~ 60 FPS 的动画将会至关流畅,让人倍感温馨;
  • 帧率在 30 ~ 50 FPS 之间的动画,因各人敏感程度不一样,温馨度因人而异;
  • 帧率在 30 FPS 如下的动画,让人感受到明显的卡顿和不适感;
  • 帧率波动很大的动画,亦会令人感受到卡顿。

计算帧率的方法:github

  • 借助 Chrome 开发者工具 - FPS meter
  • 使用 requestAnimationFrame 计算 FPS 原理,算法总有6种,详情请看 -》blog.csdn.net/allen807733…

2. setTimeout 与 requestAnimationFrame

setTimeout出现的问题:web

  1. 根据 event loop 的规则,setTimeout 会被推到"任务队列"挂起,必须等到当前代码(执行栈)执行完,主线程才会去执行它指定的回调函数。要是当前代码耗时很长,有可能要等好久,因此并无办法保证,回调函数必定会在setTimeout()指定的时间执行。
  2. HTML5标准规定了setTimeout()的第二个参数的最小值(最短间隔),不得低于4毫秒,若是低于这个值,就会自动增长。在此以前,老版本的浏览器都将最短间隔设为10毫秒。另外,对于那些DOM的变更(尤为是涉及页面从新渲染的部分),一般不会当即执行,而是每16毫秒执行一次。这时使用requestAnimationFrame()的效果要好于setTimeout()。

例子:算法

var FPS = 60;
setTimeout(draw, 1000/FPS);
复制代码

上述代码,若是draw带有大量逻辑计算,致使计算时间超过帧等待时间时,将会出现丢帧。除外,若是FPS过高,超过了当时浏览器的重绘频率,将会形成计算浪费,例如浏览器实际才重绘2帧,但却计算了3帧,那么有1帧的计算就浪费了。canvas

引入requestAnimationFrame,这个方法是用来在页面重绘以前,通知浏览器调用一个指定的函数,以知足开发者操做动画的需求。segmentfault

var fps = 30;
var now;
var then = Date.now();
var interval = 1000/fps;
var delta;

function tick() {
  requestAnimationFrame(tick);
  now = Date.now();
  delta = now - then;
  if (delta > interval) {
    // 这里不能简单then=now,不然还会出现上边简单作法的细微时间差问题。例如fps=10,每帧100ms,而如今每16ms(60fps)执行一次draw。16*7=112>100,须要7次才实际绘制一次。这个状况下,实际10帧须要112*10=1120ms>1000ms才绘制完成。
    then = now - (delta % interval);
    draw(); // ... Code for Drawing the Frame ...
  }
}
tick();
复制代码

3. transition 与 animate

transition:通常用来作过渡的, 没时间轴的概念, 经过事件触发(一次),没中间状态(只有开始和结束)
animate:作动效,有时间轴的概念(帧可控),能够重复触发和有中间状态;
过渡的开销比动效小,前者通常用于交互居多,后者用于活动页居多;浏览器

如何优化动画

大部分网站性能优化能够对动画有必定的优化做用,在这里不累赘地细讲了,详情请看 -》网站性能优化实战:juejin.im/post/5b0b7d…性能优化

本文主要讲动画的为何会出现动画卡顿问题,针对这种问题该如何解决。

1. 为何会形成动画卡顿呢?

Blink 内核早期架构
以 Chrome 浏览器内核 Blink 渲染页面为例。对早期的 Chrome 浏览器而言,每一个页面 Tab 对应一个独立的 renderer 进程,Renderer 进程中包含了主线程和合成线程。早期 Chrome 内核架构:


其中,主线程主要负责:

  • Javascript 的计算与执行
  • CSS 样式计算
  • Layout 计算
  • 将页面元素绘制成位图(paint),也就是光栅化(Raster)
  • 将位图给合成线程

合成线程则主要负责:

  • 经过GPU,将位图绘制到屏幕上
  • 对可见或即将可见的区域,询问主线程是否进行位图更新。
  • 计算页面的可见区域
  • 当滚动屏幕时,计算出即将可见的区域
  • 当滚动时移动页面区域

主线程和合成线程的调度不合理会形成渲染出现卡顿等问题。

2. CSS 动画与 JS 动画的细微区别

FPS(JS) = Time(主线程) + Time(合成线程)
FPS(CSS) = Time(合成线程)(有时候,例如opacity, transform)

在不频繁触发主线程时,css 动画比 js 动画节省性能。
若是任何动画触发了绘制,布局,或者二者,那么「主线程」会来完成该工做。这个对基于 CSS 仍是 JavaScript 实现的动画都同样,布局或者绘制的开销巨大,让与之关联的 CSS 或 JavaScript 执行工做、渲染都变得毫无心义。

3. CSS 动画卡顿性能优化

例子:

transition:margin 2s;
复制代码

在使用height,width,margin,padding做为transition的值时,会形成浏览器主线程的工做量较重,例如从margin-left:-20px渲染到margin-left:0,主线程须要计算样式margin-left:-19px,margin-left:-18px,一直到margin-left:0,并且每一次主线程计算样式后,合成进程都须要绘制到GPU而后再渲染到屏幕上,先后总共进行20次主线程渲染,20次合成线程渲染,20+20次,总计40次计算。

主线程的渲染流程,能够参考浏览器渲染网页的流程:

  • 使用 HTML 建立文档对象模型(DOM)
  • 使用 CSS 建立 CSS 对象模型(CSSOM)
  • 基于 DOM 和 CSSOM 执行脚本(Scripts)
  • 合并 DOM 和 CSSOM 造成渲染树(Render Tree)
  • 使用渲染树布局(Layout)全部元素
  • 渲染(Paint)全部元素

也就是说,主线程每次都须要执行Scripts,Render Tree ,Layout和Paint这四个阶段的计算。

transition:transform 2s;
复制代码

而若是使用transform的话,例如tranform:translate(-20px,0)到transform:translate(0,0),主线程只须要进行一次tranform:translate(-20px,0)到transform:translate(0,0),而后合成线程去一次将-20px转换到0px,这样的话,总计1+20计算。

css 动画卡顿的解决方案:

  1. 在使用css3 transtion作动画效果时,优先选择transform,尽可能不要使用height,width,margin和padding。
  2. 选择器越复杂,浏览器计算得越久。最糟状况下,浏览器须要遍历整个DOM-tree,计算量等于元素总个数乘以选择器个数。尽可能不要使选择器太复杂,事先给须要被操做的元素加上类名。
  3. reflow老是牵涉整个文档流。修改元素css后马上读取css计算值,将致使浏览器同步reflow,阻塞js线程。
  4. 使用 will-change 通知浏览器你打算更改元素的属性。浏览器会在你进行更改以前作最合适的优化。但不要过分使用 will-change,由于这样作会浪费浏览器资源,从而致使更多的性能问题。

相关动画插件

  • Echarts:很少说
  • Zrender:Echarts 是基于 Zrender 封装、加工实现的。Zrender 又是对 canvas 的封装。
  • motaion:蚂蚁对外的动画组件
  • three.js:3D 动画,基于 webGL 封装的 API
  • bodymovin:ui导出相应的文件,使用插件实现动画效果。
  • chart.js:与 Ecahrts 相似的插件

相关参考资料

相关文章
相关标签/搜索