window 定时器
JS 是单线程语言,但它允许通过设置超时值和间歇时间值来调度代码在特定的时刻执行。
开发环境下,很少真正使用间歇调用,原因是后一个间歇调用可能会在前一个间歇调用结束之前启动;
[ 应用 ?!] 所以,最好不要使用间歇调用,而是通过超时调用来代替。
定时器的运行机制 ?!
[1] 超时调用:指在指定的时间过后执行代码。
setTimeout() 方法,接受两个参数:要执行的代码和以毫秒表示的时间(1000ms = 1s)
1234 第一个参数可以是包含JS代码的字符串,也可以是一个函数;由于传递字符串可能导致性能损失,因此不建议以字符串作为第一个参数。setTimeout("alert('Hello World');",1000); // 不建议setTimeout(function(){alert("Hello World");},1000); // 推荐[ 设置与取消 ] 调用 setTimeout() 后,会返回一个数值 ID 表示超时调用。可以通过调用 clearTimeout() 方法,并将相应的超时调用 ID 作为参数传入,来取消尚未进行的超时调用计划。
1234 // 设置超时调用var timeoutid = setTimeout(function(){alert("Hello World");},1000);// 取消clearTimeout(timeoutid);
[2] 间歇调用:与超时调用类似,只不过它会按照指定的时间间隔重复执行代码
1234 // 不建议传递字符串setInterval("alert('Hello World'),1000");// 推荐的方式setInterval(function(){alert("Hello World");},1000);[取消] 未执行的间歇调用,可以使用 clearInterval() 方法并传入相应的间歇调用 ID。
[3] 代码示例
[3-1] 变量 num 每半分钟递增一次,当递增到最大值时就会取消先前设定的间歇调用
12345678910111213 // 使用间歇调用实现var num = 0;var max = 10;var intervalId = null;function incrementNumber(){num++;// 如果执行次数达到了max设定的值,则取消后续未执行的间歇调用if(num==max){clearInterval(intervalId);alert("Done");}}intervalId = setInterval(incrementNumber,500);
1234567891011 // 使用超时模式实现var num = 0;var max = 10;function incrementNumber(){if(num<max){setTimeout(incrementNumber,500);}else{alert("Done");}}setTimeout(incrementNumber,500);
[3-2] 使用定时器来调整事件发生顺序
用户自定义的回调函数,通常在浏览器的默认动作之前触发。比如,用户在输入框输入文本,keypress 事件会在浏览器接收文本之前触发。因此,下面的回调函数是达不到目的。
123456 <input type="text" id="myInput"><script>myInput.onkeypress = function(event) {this.value = this.value.toUpperCase();}</script>上面代码想在用户输入文本后,立即将字符转为大写。但是实际上,它只能将上一个字符转为大写,因为浏览器此时还没接收到文本,所以 this.value 取不到最新输入的那个字符。
只有用 setTimeout 改写,上面的代码才能发挥作用。
12345678 <input type="text" id="myInput"><script>myInput.onkeypress = function(event) {setTimeout(function(){myInput.value = myInput.value.toUpperCase();});}</script>
被誉为神器的 requestAnimationFrame(HTML5 新增)
与 setTimeout 和 setInterval 不同,requestAnimationFrame 不需要设置时间间隔。这有什么好处呢?
[1] 引入
计时器一直是 JS 动画的核心技术,而编写动画循环的关键是要知道延迟时间多长合适。一方面,循环间隔必须足够短,这样才能让不同的动画效果显得平滑流畅;另一方面,循环间隔还要足够长,这样才能确保浏览器有能力渲染产生的变化。大多数电脑显示器的刷新频率是 60Hz,大概相当于每秒钟重绘 60 次。
大多数浏览器都会对重绘操作加以限制,不超过显示器的重绘频率,因为即使超过那个频率用户体验也不会有提升。因此,最平滑动画的最佳循环间隔是1000ms/60,约等于 16.6ms。而 setTimeout 和 setInterval 的问题是,它们都不精确。它们的内在运行机制决定了时间间隔参数实际上只是指定了把动画代码添加到浏览器UI线程队列中以等待执行的时间。如果队列前面已经加入了其他任务,那动画代码就要等前面的任务完成后再执行。
requestAnimationFrame 采用系统时间间隔,保持最佳绘制效率,不会因为间隔时间过短,造成过度绘制,增加开销;也不会因为间隔时间太长,使用动画卡顿不流畅,让各种网页动画效果能够有一个统一的刷新机制,从而节省系统资源,提高系统性能,改善视觉效果。
[2] 特点
- requestAnimationFrame 会把每一帧中的所有 DOM 操作集中起来,在一次重绘或回流中就完成,并且重绘或回流的时间间隔紧紧跟随浏览器的刷新频率。
- 在隐藏或不可见的元素中,requestAnimationFrame 将不会进行重绘或回流,这当然就意味着更少的 CPU、GPU 和内存使用量。
- requestAnimationFrame 是由浏览器专门为动画提供的 API,在运行时浏览器会自动优化方法的调用,并且如果页面不是激活状态下的话,动画会自动暂停,有效节省了 CPU 开销。
[3] 使用
requestAnimationFrame 的用法与 settimeout 很相似,只是不需要设置时间间隔而已。requestAnimationFrame 使用一个回调函数作为参数,这个回调函数会在浏览器重绘之前调用。它返回一个整数,表示定时器的编号,这个值可以传递给 cancelAnimationFrame 用于取消这个函数的执行。
1 requestID = requestAnimationFrame(callback);
12345 //控制台输出1和0var timer = requestAnimationFrame(function(){console.log(0);});console.log(timer);//1
[ 取消 ] cancelAnimationFrame 方法用于取消定时器。
12345 //控制台什么都不输出var timer = requestAnimationFrame(function(){console.log(0);});cancelAnimationFrame(timer);也可以直接使用返回值进行取消。
1234 var timer = requestAnimationFrame(function(){console.log(0);});cancelAnimationFrame(1);
[4] 兼容写法:IE9- 浏览器不支持该方法,可以使用 setTimeout 来兼容
[ 简单兼容 ]
12345 if (!window.requestAnimationFrame) {requestAnimationFrame = function(fn) {setTimeout(fn, 17);};}[ 严格兼容 ]
123456789101112 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;}}
12345 if (!window.cancelAnimationFrame) {window.cancelAnimationFrame = function(id) {clearTimeout(id);};}