现象:使用setInterval定时器时经常会出现一个现象,从定时器页面切换到其它页面或者其它应用时在回到页面,会发现定时的动画会加速执行
原因:浏览器本着节省内存的性质,当切换到其他页面时,页面的定时器会延时执行,当切换回来的时候被延时执行的动画会一次依次执行,这样我们就会看到动画加速运动,出现错误,在轮播图之类的页面经常会发生这样的情况
解决方法一:
在这里我们需要用到以下三个知识点:
- document.onvisibilitychange :
只要页面发生变化,不管是切换到其他的页面还是把浏览器缩小,都会触发这个事件。
- document.hidden
这个是指当页面不是当前页面时为true,否则为false
- document.visibilityState:
这个属性有四个值,分为是:visible,hidden,prerender,unloaded
visible 表示当前网页是可见或者是部分可见的。
hidden:当前网页是不可见的
prerender 网页内容被预渲染并且用户不可见
unloaded 如果文档被卸载,那么这个值将被返回
解决setInterval在浏览器切换中的问题:
思路:如果页面是不可见的,那么我们就会清除定时器,如果页面是可见的,那么我们就重新开启定时器。
所以我们需要用document.onvisibilitychange进行监听,然后用document.visibilityState或者是document.hidden进行判断。
实际操作:
document.onvisibilitychange=function(){
if(document.visibilityState=="visible"){
timer=setInterval(f, 1000);
}else{
clearInterval(timer);
}
}
jq中animate的解决
如果用的是jq的animate这个方法,就只需要到这个方法的前面加上stop(true,true)
$(".slidePanel").stop(true,true).animate({
"left": -iNow*varWidth+"px",
"speed":300
});
方法二
可以用web work来跑我们的代码,通过在worker里面开启定时器,发送message给外部,外部收到message,然后再执行操作,这样我们切换页面,但是worker不受影响,定时器就可以解决延迟了
// time worker
var intervalIds = {};
// 监听message 开始执行定时器或者销毁
self.onmessage = function(e){
switch(e.data.command){
case 'interval:start': // 开启定时器
var intervalId = setInterval(function(){
postMessage({
message: 'interval:tick',
id: e.data.id
})
},e.data.interval);
postMessage({
message: 'interval:started',
id: e.data.id
});
intervalIds[e.data.id] = intervalId;
break;
case 'interval:clear': // 销毁
clearInterval(intervalIds[e.data.id]);
postMessage({
message: 'interval:cleared',
id: e.data.id
})
delete intervalIds[e.data.id];
break;
}
}
开启worker
var worker = new Worker('./time-worker.js');
var workerTimer = {
id: 0,
callbacks: {},
setInterval: function(cb, interval, context) {
this.id++;
var id = this.id;
this.callbacks[id] = { fn: cb, context: context };
worker.postMessage({ command: 'interval:start', interval: interval, id: id });
return id;
},
// 监听worker 里面的定时器发送的message 然后执行回调函数
onMessage: function(e) {
switch (e.data.message) {
case 'interval:tick':
var callback = this.callbacks[e.data.id];
if (callback && callback.fn) callback.fn.apply(callback.context);
break;
case 'interval:cleared':
delete this.callbacks[e.data.id];
break;
}
},
// 往worker里面发送销毁指令
clearInterval: function(id) {
worker.postMessage({ command: 'interval:clear', id: id });
}
};
worker.onmessage = workerTimer.onMessage.bind(workerTimer);
使用实例:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>稳定版定时器</title>
</head>
<body>
<div>
<p>Times:500ms</p>
<p>
开始时间(s):<span id="startTime"></span> ---- 结束时间(s):<span id="endTime"></span>
</p>
<p id="nums"></p>
<button id="btn">开始</button>
</div>
<script src="worker-timer.js"></script>
<script>
var nums = document.getElementById('nums');
var btn = document.getElementById('btn');
var startTime = document.getElementById('startTime');
var endTime = document.getElementById('endTime');
var number = 1;
var intervalId = null;
btn.addEventListener('click',function(e){
if(e.target.innerHTML == '开始'){
startTime.innerHTML = new Date().getSeconds();
intervalId = workerTimer.setInterval(function(){
nums.innerHTML = ++number;
},500);
e.target.innerHTML = '暂停';
}else{
endTime.innerHTML = new Date().getSeconds();
workerTimer.clearInterval(intervalId);
e.target.innerHTML = '开始'
}
},false);
</script>
</body>
</html>
参考 :