在拖拽操作中重复调用 `setinterval` 会导致多个定时器同时运行,造成计时混乱;解决方法是将定时器 id 提升至全局作用域,并在每次启动新定时器前调用 `clearinterval` 清除旧定时器。
在实现如“汉诺塔”这类带倒计时机制的交互游戏时,一个常见需求是:每当用户开始拖拽某个元素(如圆盘),倒计时就应重置为初始值(例如 10 秒),并重新开始计时。但若直接在 start 回调中反复调用 timer(),而未清理前一次的 setInterval,就会导致多个定时器并发执行——表现为倒计时跳变加快、多次弹出“Game over”警告,甚至页面异常刷新。
根本原因在于:setInterval 返回一个唯一的定时器 ID(数值),只有通过 clearInterval(id) 才能终止对应任务;而原代码中 downloadTimer 是函数内局部变量,每次调用 timer() 都会创建全新变量,旧定时器 ID 丢失,无法清除。
✅ 正确做法是:
以下是优化后的完整实现:
// ✅ 全局声明定时器 ID(推荐放在闭包或模块顶层)
let downloadTimer = null;
function timer() {
// ? 每次启动前先清除可能存在的旧定时器
if (downloadTimer !== null) {
clearInterval(downloadTimer);
}
let timeleft = 10;
downloadTimer = setInterval(() => {
if (timeleft <= 0) {
clearInterval(downloadTimer); // ✅ 清理自身,防内存泄漏
alert("Game over! You ran out of time\nPlay again ?");
location.reload();
} else {
timeleft -= 1;
document.getElementById("timer").textContent = timeleft;
}
}, 1000);
}
function Drag() {
$(".draggable").draggable({
stack: $(".draggable"),
helper: "clone",
start: function () {
timer(); // ✅ 每次拖拽开始即重置计时
// 其余逻辑(如记录平台、序列等)保持不变
const parentNode = "#" + this.parentNode.id;
platf
orms.push(parentNode);
const shape = "#" + this.id;
sequence.push(shape);
const shapeParent = "#" + this.closest(".holder").id;
}
});
}⚠️ 注意事项:
总结:定时器管理的核心原则是「一启一清」——每次新建前必清旧,每次结束时必清己。这不仅是解决重叠计时的关键,更是编写健壮前端定时逻辑的基本功。