使用JS更新Canvas内倒计时器时文字重叠该如何解决?
问题成因
- Canvas是逐像素渲染的画布,本身没有图层和元素节点的概念,你每次调用
fillText()绘制新的时间文本时,只会在现有画布像素上叠加新的内容,不会自动擦除上一秒绘制的旧文字,所以就出现了文字重叠的效果。 - 代码存在逻辑缺陷:你在
setInterval的回调中重复调用了requestAnimationFrame(drawTimer),相当于每秒都会新启动一个独立的倒计时定时器,运行时间越长同时运行的定时器越多,不仅会加重性能负担,还会导致计时逻辑混乱、文本绘制频率异常。
可行解决方案
方案1:纯Canvas实现(无需修改页面结构)
你只需要在每次绘制新的时间文本前,先清除上一次文字所在区域的画布内容,同时修复重复启动计时器的逻辑错误即可,修改后的代码如下:
// 倒计时相关变量提到外部,避免重复初始化 let timeLeft = 10; let countDown = null; function drawTimer() { // 先清除之前文字所在的区域,范围可根据你实际文字大小调整 ctx.clearRect(195, 5, 120, 20); ctx.font = "16px Arial"; ctx.fillStyle = "#FFFFFF"; ctx.strokeStyle = "#000000"; ctx.fillText("Time left: " + timeLeft, 200, 20); } function startCountDown() { countDown = setInterval(function() { timeLeft -= 1; if (timeLeft <= 0) { alert("Time's up!"); isWinner = false; clearInterval(countDown); document.location.reload(); return; } drawTimer(); }, 1000); // 初始化时先绘制一次10秒的初始状态 drawTimer(); } // 游戏启动时调用startCountDown()即可
如果你的画布上还有其他固定内容不希望被清除,也可以每次绘制文字前,用和画布背景相同的颜色绘制一个矩形覆盖之前的文字区域,效果和clearRect一致。
方案2:HTML实现(更推荐,性能更高、修改更灵活)
直接在HTML里加一个专门显示倒计时的DOM元素,用JS更新元素内容即可,完全不需要处理画布重绘逻辑,后期调整样式、位置也更方便:
首先在HTML中添加计时器容器:
<div id="timer" style="position: absolute; left: 200px; top: 20px; font: 16px Arial; color: #fff; text-shadow: 1px 1px 0 #000;"></div>
JS逻辑修改为:
let timeLeft = 10; let countDown = null; const timerEl = document.getElementById('timer'); function startCountDown() { // 渲染初始时间 timerEl.textContent = `Time left: ${timeLeft}`; countDown = setInterval(() => { timeLeft -= 1; if (timeLeft <= 0) { alert("Time's up!"); isWinner = false; clearInterval(countDown); document.location.reload(); return; } timerEl.textContent = `Time left: ${timeLeft}`; }, 1000); }
内容的提问来源于stack exchange,提问作者RecurseThis
相关产品推荐
相关产品推荐

