帧的定义:1幅画就叫作“1帧”,每秒帧数指的就是“每秒播放的画面数”。每一帧都是静止的图象,快速连续地显示帧便造成了运动的假象。高的帧率能够获得更流畅、更逼真的动画。每秒钟帧数 (fps) 愈多,所显示的动做就会愈流畅。css
理论上说,FPS 越高,动画会越流畅,目前大多数设备的屏幕刷新率为 60 次/秒,因此一般来说 FPS 为 60 frame/s 时动画效果最好,也就是每帧的消耗时间为 16.67ms。css3
直观感觉,不一样帧率的体验:git
计算帧率的方法:github
setTimeout出现的问题:web
例子:算法
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();
复制代码
transition:通常用来作过渡的, 没时间轴的概念, 经过事件触发(一次),没中间状态(只有开始和结束)
animate:作动效,有时间轴的概念(帧可控),能够重复触发和有中间状态;
过渡的开销比动效小,前者通常用于交互居多,后者用于活动页居多;浏览器
大部分网站性能优化能够对动画有必定的优化做用,在这里不累赘地细讲了,详情请看 -》网站性能优化实战:juejin.im/post/5b0b7d…性能优化
本文主要讲动画的为何会出现动画卡顿问题,针对这种问题该如何解决。
Blink 内核早期架构
以 Chrome 浏览器内核 Blink 渲染页面为例。对早期的 Chrome 浏览器而言,每一个页面 Tab 对应一个独立的 renderer 进程,Renderer 进程中包含了主线程和合成线程。早期 Chrome 内核架构:
其中,主线程主要负责:
合成线程则主要负责:
主线程和合成线程的调度不合理会形成渲染出现卡顿等问题。
FPS(JS) = Time(主线程) + Time(合成线程)
FPS(CSS) = Time(合成线程)(有时候,例如opacity, transform)
在不频繁触发主线程时,css 动画比 js 动画节省性能。
若是任何动画触发了绘制,布局,或者二者,那么「主线程」会来完成该工做。这个对基于 CSS 仍是 JavaScript 实现的动画都同样,布局或者绘制的开销巨大,让与之关联的 CSS 或 JavaScript 执行工做、渲染都变得毫无心义。
例子:
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次计算。
主线程的渲染流程,能够参考浏览器渲染网页的流程:
也就是说,主线程每次都须要执行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 动画卡顿的解决方案: