用setInterval是每隔n毫秒执行一次;用setTimeOut是延迟n毫秒之后,执行一次
小demo:
设置小方块移动,从最左边开始,每隔100毫秒向右移动10px,最终移到距离一开始500px位置
用setInterval实现:
var oDemo = document.getElementsByClassName('demo')[0];
var timer = setInterval(move, 100);
function move(){
var l = oDemo.offsetLeft;
if (l < 500){
oDemo.style.left = l + 10 + 'px';
}else {
oDemo.style.left = '500px';
}
}
用setTimeOut实现:
var oDemo = document.getElementsByClassName('demo')[0];
var time2;
function move(){
var l = oDemo.offsetLeft;
if (l < 500){
oDemo.style.left = l + 10 + 'px';
setTimeout(move, 100);
}else {
oDemo.style.left = '500px';
}
}
move();
效果一致
计时器做动画存在的问题:
1.当前窗口不再动画页面时,计时器仍将继续工作
比如做了一个动画,动画还没播放完,用户已经再看其他页面了,这个动画会依然继续下去,浪费了cpu,如果是在移动端,就体现在手机耗电的影响了
2.回调函数执行耗时,老是排队
比如上面的move函数执行需要200毫秒,设置每隔100毫秒就执行一次,一次move还没完成,另一个请求已经发来了,就出现了排队情况;只有前一个move完成,才能执行后一个,时间的设置就不太准确;排队太多也会导致浏览器崩溃
3.设置动画频率高,过度绘制,出现掉帧
浏览器屏幕自动刷新频率大概是16.7ms/次,1s刷新60次,与设备性能和cpu负载情况有关
浏览器不断刷新,页面不断重绘,我们才看到了动画的效果
如果计时器频率高于浏览器刷新的频率,即使代码执行了,浏览器没有刷新,也是没有显示的,出现掉帧情况,不流畅
举个例子:
比如,小方块原本在最左边,left为0,设置计时器每10ms刷新一次,小方块向右前进10px,浏览器每16.7ms刷新一次
1.当计时器开始工作,第0ms时,小方块在0位置并显示,
2.第10ms时,小方块本该到10px位置,但浏览器未刷新,小方块还在0位置;
3.第16.7ms时,浏览器刷新了,此时显示小方块在10px位置,从0移动到10px处;
4.第20ms时,小方块本该到20px位置,但浏览器未刷新,显示小方块在10px位置;
5.第30ms时,小方块本该到30位置,但浏览器未刷新,显示小方块在10px位置;
6.第16.7*2ms时,浏览器刷新了,小方块从10px处一下子移动到30px位置,跳过了20px位置,即掉帧
本来按计时器设置的,在黑色字体的123位置都会显示一次小方块
但是由于浏览器刷新频率没有那么高,只有标红的12位置被展示出来
计时器多执行一次,没有显示
html5对此提供了requestAnimationFrame,来解决这个问题
requestAnimationFrame优势:
1.当前窗口不在动画页面时,停止工作
2.浏览器刷新屏幕时自动执行,无需设置时间间隔
3.浏览器内部优化了该方法,没有优化计时器
和setTimeOut一样是n毫秒之后再执行,但这个n毫秒,自动设置成浏览器刷新频率,浏览器刷新一次,执行一次,不需要手动设置;
浏览器不刷新,就不执行,没有排队掉帧的情况
requestAnimationFrame使用:
1.req = requestAnimationFrame(cb);屏幕每次绘制时执行回调函数cb
2.cancelAnimationFrame(req);
<div class="demo">div>
<button id="stop">stopbutton>
<script>
var oDemo = document.getElementsByClassName('demo')[0],
oStop = document.getElementById('stop');
var req;
function move(){
var l = oDemo.offsetLeft;
if (l < 1000){
oDemo.style.left = l + 5 + 'px';
req = requestAnimationFrame(move);//当页面刷新时执行下一次
}else {
oDemo.style.left = '1000px';
}
}
move();
oStop.onclick = function(){
cancelAnimationFrame(req);
}//点击时清除req
script>
有的浏览器不兼容使用requestAnimationFrame,就需要这样写:
window.requestAnimationFrame = (function (callbask) {
return window.requestAnimationFrame ||
window.WebKitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function () {
window.setTimeout(callback, 1000 / 60)
}
})();//立即执行
如果各个浏览器都不支持,就使用setTimeOut
对应的清除计时器的兼容性写法:
window.cancelAnimationFrame = (function (timer) {
return window.cancelAnimationFrame ||
window.WebKitCancelAnimationFrame ||
window.mozCancelAnimationFrame ||
window.oCancelAnimationFrame ||
window.msCancelAnimationFrame ||
function () {
window.clearTimeout(timer)
}
})();