javascript中的定时器入门
JavaScript提供定时器(timer)的功能,可以延期执行或重复执行函数或代码段。
window对象提供了三个方法来实现定时器的效果,分别是setTimeout()、setInternal()和requestAnimationFrame()。
定时器是JavaScript动画的核心技术。
setTimeout()
setTimeout()方法用来指定某个函数或代码段/字符串在指定的毫秒数之后执行。它返回一个整数,表示定时器的编号,这个值可以传递给clearTimeout()用来取消这个函数的执行。
var timer = setTimeout(function() { console.log(timer); // 2.1000ms后,再输出1(1是定时器的编号) }, 1000); console.log(0); // 1.先输出0
也可以写成字符串参数的形式,但是这种形式会造成JavaScript引擎的二次解析,降低性能,因此并不推荐使用。
var timer = setTimeout("console.log(timer);", 1000); console.log(0);
如果省略setTimeout的第二个参数,则该参数默认为0。
var Timer = setTimeout(function() { console.log(Timer); // 2.过了0ms后,再输出1 }); console.log(0); // 1.先输出0
实际上,除了上面提到的两个参数之外,setTimeout还允许添加更多的参数,它们将被传入到定时器的函数之中。
setTimeout(function(a,b) { console.log(a + b); // 等待1000ms后,输出2 }, 1000, 1, 1);
要注意的是,IE9及以下的浏览器只支持setTimeout有两个参数,最后的输出结果会是NaN(名副其实的Fuck-IE),这时候可以使用IIFE传参来进行兼容。
setTimeout((function(a, b) { return function() { console.log(a + b); } })(1, 1), 1000);
或者也可以将函数写在定时器的外面,然后函数在定时器的匿名函数中带参数调用。
function calc(a, b) { console.log(a + b); } setTimeout(function() { calc(1, 1); }, 1000);
要注意的是,IE8浏览器不允许向定时器中传递事件对象event,如果要使用事件对象中的某些属性,可以将其保存在变量中传递进去。
div.onclick = function(e) { e = e || event; var type = e.type; setTimeout(function() { console.log(type); // click console.log(e.type); // 报错 }) }
cleartimeout()
setTimeout函数返回一个表示计数器编号的整数值,将该整数传入clearTimeout函数就能取消对应的定时器。
var Timer = setTimeout(function() { console.log(Timer); }, 100); clearTimeout(Timer);
或者直接使用返回值作为参数。
var Timer = setTimeout(function() { console.log(Timer); }, 100); clearTimeout(1);
一般来说,setTimeout返回的整数值是连续的,也就是说,第二个setTimeout方法返回的整数值比第一个的整数值大1。
// 控制台输出1、2、3 var Timer1 = setTimeout(function() { console.log(Timer1); }, 100); var Timer2 = setTimeout(function() { console.log(Timer2); }, 100); var Timer3 = setTimeout(function() { console.log(Timer3); }, 100);
setInterval()
setInterval()的用法与setTimeout完全一致,区别仅仅在于setInterval指定某个任务每隔一段时间就执行以此,也就是无限次的定时执行。
var count = 0; var timer = setInterval(function() { console.log(count++); }, 1000);
要注意的是,HTML5的标准规定,setTimeout的最短时间间隔是4毫秒;setInterval的最短间隔时间是10毫秒,也就是说,小于10毫秒的时间间隔会被调整到10毫秒。
requestAnimationFrame
计时器一直是javascript动画的核心技术。而编写动画循环的关键是要知道延迟时间多长合适。一方面,循环间隔必须足够短,这样才能让不同的动画效果显得平滑流畅;另一方面,循环间隔还要足够长,这样才能确保浏览器有能力渲染产生的变化。
大多数电脑显示器的刷新频率是60Hz,大概相当于每秒钟重绘60次。大多数浏览器都会对重绘操作加以限制,不超过显示器的重绘频率,因为即使超过那个频率用户体验也不会有提升。因此,最平滑动画的最佳循环间隔是1000ms/60,约等于16.6ms。
而setTimeout和setInterval的问题是,它们都不精确。它们的内在运行机制决定了时间间隔参数实际上只是指定了把动画代码添加到浏览器UI线程队列中以等待执行的时间。如果队列前面已经加入了其他任务,那动画代码就要等前面的任务完成后再执行。
requestAnimationFrame采用系统时间间隔,保持最佳绘制效率,不会因为间隔时间过短,造成过度绘制,增加开销;也不会因为间隔时间太长,使用动画卡顿不流畅,让各种网页动画效果能够有一个统一的刷新机制,从而节省系统资源,提高系统性能,改善视觉效果。
也就是说,requestAnimationFrame与setTimeout、setInterval不同,不需要设置时间间隔。
requestAnimationFrame的特点
1.requestAnimationFrame会把每一帧中的所有DOM操作集中起来,在一次重绘或回流中就完成,并且重绘或回流的时间间隔紧紧跟随浏览器的刷新频率。
2.在隐藏或不可见的元素中,requestAnimationFrame将不会进行重绘或回流,这当然就意味着更少的CPU、GPU和内存使用量。
3.requestAnimationFrame是由浏览器专门为动画提供的API,在运行时浏览器会自动优化方法的调用,并且如果页面不是激活状态下的话,动画会自动暂停,有效节省了CPU开销。
requestAnimationFrame的使用
requestAnimationFrame的用法与settimeout很相似,只是不需要设置时间间隔而已。requestAnimationFrame使用一个回调函数作为参数,这个回调函数会在浏览器重绘之前调用。它返回一个整数,表示定时器的编号,这个值可以传递给cancelAnimationFrame用于取消这个函数的执行。
requestID = requestAnimationFrame(callback);
// 控制台输出1和0 var timer = requestAnimationFrame(function() { console.log(0); }); console.log(timer); // 1
// 控制台什么都不输出 var timer = requestAnimationFrame(function() { console.log(0); }); cancelAnimationFrame(timer);
也可以直接使用返回值进行取消。
var timer = requestAnimationFrame(function() { console.log(0); }); cancelAnimationFrame(1);
requestAnimationFrame的兼容
IE9浏览器及以下不支持这个方法,可以使用setTimeout来兼容。
// 简单兼容 if (!window.requestAnimationFrame) { requestAnimationFrame = function(fn) { setTimeout(fn, 17); }; }
// 严格兼容 if(!window.requestAnimationFrame) { var lastTime = 0; window.requestAnimationFrame = function(callback) { var currTime = new Date().getTime(); var timeToCall = Math.max(0,16.7 - (currTime - lastTime)); var id = window.setTimeout(function() { callback(currTime + timeToCall); }, timeToCall); lastTime = currTime + timeToCall; return id; } } if (!window.cancelAnimationFrame) { window.cancelAnimationFrame = function(id) { clearTimeout(id); }; }
“最怕你一生碌碌无为,还要安慰自己平凡可贵。”