js
setTimeout()&setInterval()
setTimeout(fn,17)会在fn内部自己调用自己直至动画,而setInterval(fn,17)需要在动画完成后手动清除。
基本用法如下
大多数电脑显示器的刷新频率是60Hz,大概相当于每秒钟重绘60次。大多数浏览器都会对重绘操作加以限制,不超过显示器的重绘频率,因为即使超过那个频率用户体验也不会有提升。因此,最平滑动画的最佳循环间隔是1000ms/60,约等于16.6ms,帧率为60fps。
而setTimeout和setInterval的问题是,它们都不精确。它们的内在运行机制决定了时间间隔参数实际上只是指定了把动画代码添加到浏览器UI线程队列中以等待执行的时间。如果队列前面已经加入了其他任务,那动画代码就要等前面的任务完成后再执行。
也就是说如果仅依赖计时器延迟的时间,在不同浏览器平台将出现不同的效果,比如在一个手机上执行的时候可能有60fps的帧率,在另外一个手机上可能就只有30fps,更甚可能只有10fps。所以我们最好以时间为单位更新,而不是以帧(即制定的计时器延迟时间)为单位更新。
但是当动画是循环往复地播放的时候,if(progress > 1) progress = 1;
这句代码将会使动画切换间隙中巡视一部分时间,因为当下一帧进行时process>1
而就会有造成timePassed-opts.duration
时间浪费。帧率越低时,浪费的时间会越多。
|
|
下面是settimeout使用的一些注意事项
- 其中,如果setTimeout第一个参数采用字符串模式,JavaScript引擎将会解析两次,将会调用eval()函数动态执行字符串脚本,性能降低。即
var Timer = setTimeout('console.log(Timer);',1000);
- 但是因为字符串参数是由eval()函数解析的,所以当要使用字符串参数来传入一个参数的时候,需要在函数名后面加上括号,即
setTimeout('fn()',100)
- 可是如果参数不是使用字符串,
setTimeout(fn(),100)
中的fn会立刻执行,因为这里fn()
是一个函数执行而不是函数定义。如果想要函数延迟执行应该传入一个地址,即setTimeout(fn,100)
。抑或在第一种写法的基础上,在fn中直接return一个函数。 settimeout中第一个参数内部中的this在非严格模式是指向window,如
setTimeout('console.log(this);',1000);
结果会打印window。而若是setTimeout(this.fn,100);
中的this则是和上下文有关。
最后不建议使用字符串参数,因为如果不了解eval()函数的用法可能用错。再者使用eval()在浏览器中属于可能不可预测的代码,所以在现代浏览器中会使用slowpath编译方式执行,影响性能。更多原因可以参考这里window.requestAnimationFrame()
requestAnimationFrame采用系统时间间隔,保持最佳绘制效率,不会因为间隔时间过短,造成过度绘制,增加开销。浏览器页面每次要重绘,就会通知requestAnimationFrame。页面不出于当前页面(比如:页面最小化了),页面是不会进行重绘的。
就算很多个requestAnimationFrame()要执行,浏览器只要通知一次就可以了,而setTimeout是多个独立绘制。12345678910111213141516171819function animate({timing, draw, duration}) {let start = performance.now();requestAnimationFrame(function animate(time) {// timeFraction goes from 0 to 1let timeFraction = (time - start) / duration;if (timeFraction > 1) timeFraction = 1;// calculate the current animation statelet progress = timing(timeFraction) // 类似CSS属性transition-timing-function,根据进度返回相应的动画中某些属性值draw(progress); // 动画实际是多个静态的画面经过微小的时间差串连在一次,这里则是实现某个静态画面(即帧)if (timeFraction < 1) {requestAnimationFrame(animate);}});}requestAnimationFrame会把每一帧中的所有DOM操作集中起来,在一次重绘或回流中就完成,并且重绘或回流的时间间隔紧紧跟随浏览器的刷新频率
- 在隐藏或不可见的元素中,requestAnimationFrame将不会进行重绘或回流,这当然就意味着更少的CPU、GPU和内存使用量
- requestAnimationFrame是由浏览器专门为动画提供的API,在运行时浏览器会自动优化方法的调用,并且如果页面不是激活状态下的话,动画会自动暂停,有效节省了CPU开销
css
css3动画不会阻塞js引擎?
原文In non-supporting browsers, which is most of them, the kill switch kills all the animations. Business as usual.
In the supporting browsers (All Safaris and Andriod Chrome) the kill only affects the blue button, the one that animates a CSS property, as opposed to using a CSS transform. But the animations that use a transform keep on going!
用CSS3动画替代JS模拟动画的好处:
- 不占用JS主线程;
- 可以利用硬件加速;
- 浏览器可对动画做优化(元素不可见时不动画减少对FPS影响)
坏处是:
浏览器对渲染的批量异步化处理让动画难以控制,此时可以用如下代码来强制同步。transform&animation
transform不会引起repaint,transform 动画由GPU控制,支持硬件加速,并不需要软件方面的渲染。
浏览器接收到页面文档后,会将文档中的标记语言解析为DOM树。DOM树和CSS结合后形成浏览器构建页面的渲染树。渲染树中包含了大量的渲染元素,每一个渲染元素会被分到一个图层中,每个图层又会被加载到GPU形成渲染纹理,而 图层在GPU中 transform 是不会触发 repaint 的,这一点非常类似3D绘图功能,最终这些使用 transform 的图层都会由独立的合成器进程进行处理。
在我们的示例中,CSS transform 创建了一个 新的复合图层,可以被GPU直接用来执行 transform 操作。
此时,你也许会问:浏览器什么时候会创建一个独立的复合图层呢?事实上一般是在以下几种情况下:
- 3D 或者 CSS transform
<video>
和<canvas>
标签- CSS filters
元素覆盖时,比如使用了 z-index 属性GPU加速
并不是所有的CSS属性都能触发GPU的硬件加速,实际上只有少数属性可以,比如下面的这些: - transform
- opacity
- filter
使用硬件加速并不是十全十美的事情,比如:
- 内存。如果GPU加载了大量的纹理,那么很容易就会发生内容问题,这一点在移动端浏览器上尤为明显,所以,一定要牢记不要让页面的每个元素都使用硬件加速。
- 使用GPU渲染会影响字体的抗锯齿效果。这是因为GPU和CPU具有不同的渲染机制。即使最终硬件加速停止了,文本还是会在动画期间显示得很模糊。