解决重复创建定时器的BUG
1. 问题描述
在点击事件中创建定时器,制作一些动画,如果重复点击,会导致动画的速度加快
如,下面的星星随机移动的主代码:
function f1() {
var i = 0;//测试用,执行5次,就清除定时器
var timerId = setInterval(function () {
i++;
my$("dv").innerHTML = "<span>☆</span>"
var starObj = my$("dv").firstElementChild;
var x = parseInt(Math.random() * 200 + 1);
var y = parseInt(Math.random() * 200 + 1);
starObj.style.color = "#fff";
starObj.style.fontSize = "30px";
starObj.style.position = "absolute";
starObj.style.left = x + "px";
starObj.style.top = y + "px";
if (i >= 5) {
clearInterval(timerId);
}
console.log(timerId);
console.log("i"+"---(1)------"+i);//检测i的生存期
}, 500);
console.log("i"+"-----(2)----"+i);//检测i的生存期
}
my$("btn2").onclick = function(){
console.log("i"+"-----(3)----"+i);//检测i的生存期
};
my$("btn").onclick = f1;
2. 问题分析
定时器创建后的id是存放在定时器函数的外部变量(点击事件处理函数的内部变量timerId)中的
在之前的定时器没有完成清除的时候,又有点击事件发生,调用事件发生函数,创建定时器
这个时候,又会申请空间,存放变量timerId,存放新的定时器的id值,虽然同名,但是并不冲突
这些存放定时器id值的变量,在点击事件处理函数看来,是局部变量
在定时器函数看来,是外部变量
这些变量不会在点击事件函数执行完后就销毁,而是会保存在内存中,供定时器函数使用
至于定时器清除后,这些变量是否存在,暂时不清楚,推测是会被销毁
反正这些变量也仅限于定时器函数和点击事件处理函数使用
基于此,尽管多个定时器被创建,但是最终也是能够成功地被清除
不过是会导致动画效果有异样罢了,比如看起来会加快
3. 解决思路
在每一次要创建定时器之前,判断是否已经有定时器在工作了,如果有,就删掉—>有则删之
还有一种思路就是,如果没有,就创建—>无则加之
至于用来存放timerId的,是变量或者是对象的属性,都可以实现
不过用变量的话,要注意变量的生存期
所以个人喜欢用对象的属性—>自定义属性(有点大材小用)或者点语法(方便)
下面是封装后的元素水平移动的代码:
/**
* 实现元素水平移动
* @param element 要操作的对象
* @param target 目标位置,number,px为单位,但是请只传数字,left大小
* @param step 每次定时器操作所移动的步长,number,px为单位,但是请只传数字
* @param timeStep 定时器周期,number,ms为单位,但是请只传数字
*/
function animate(element, target, step, timeStep) {
if (element.timerId) {
clearInterval(element.timerId);
}
element.timerId = setInterval(function () {
var current = element.offsetLeft;
var distance = Math.abs(current - target);
//关键!!!定时器函数会保存所有的参数!所以要进行初始化处理!
// 否则在反向的时候,step的值保留了上一次的负值,会反转成正值,循环往复,原地抖动
step = Math.abs(step);
step = distance > step ? step : distance;
step = current > target ? -step : step;
current += step;
element.style.left = current + "px";
if (current == target) {
clearInterval(element.timerId);
element.timerId = null;
}
console.log(current);
}, timeStep);
}
4. 几点补充
4.1
在上面的封装函数中,元素在current大于target的时候,会发生原地抖动的现象
原因是定时器函数会保存其参数的状态
所以step的值第一次翻转为负值
第二次再次反转,就变为正值
所以会出现原地抖动的现象
故,对参数进行初始化处理解决
4.2
对于有则删之和无则加之两种思路
- 有则删之----->会更新定时器
每一次都会重新创建定时器,就是说如果先后创建两个定时器,那么后面的会更新掉前面的定时器 - 无则加之----->会保护当前的定时器工作
当已经有定时器在工作了,那么之后的定时器创建申请都将不会通过
直到当前定时器完成清除,才会重新开放接收创建请求
这种思想会在很多场景运用
具体孰优孰劣,尚无说法,根据需求选择即可
HOME