JavaScript Canvas使用clearRect()后仍残留绘制轨迹问题求助
问题
我认为自己忽略了某个明显的细节,但始终找不到问题所在:
我编写了两个绘制函数:draw_canvas()用于绘制背景(两个几乎覆盖整个Canvas的矩形),draw_player()通过context.stroke()绘制一个三角形。
这两个函数都在由setInterval(update,10)调用的update函数中执行,该函数内还调用了context.clearRect()。三角形的朝向通过document.onkeydown和onkeyup事件更新。
我理解clearRect()应该清空Canvas所有内容,draw_canvas()会重绘背景,两者都应该清除之前的三角形,但切换朝向时,每一次的三角形轨迹都能被看到,我无法理解原因,希望有人能解释问题所在。
代码
JavaScript 代码
var canvas; var ctx; var lx; var ly; right = false; left = false; var player = { x: 0, y: 0, size: 32, angle: 90 } window.addEventListener("load", function(event) { canvas = document.getElementById('canvas'); ctx = canvas.getContext('2d'); player.x = canvas.width/2; player.y = canvas.height/2; }); document.onkeydown = function (e) { /* left = 37 up = 38 right = 39 down = 40 space = 32 */ if(e.keyCode == 37){ left = true; right = false; } if(e.keyCode == 39){ left = false; right = true; } } document.onkeyup = function (e) { if(e.keyCode == 37 || e.keyCode == 39){ left = false; right = false; } } function draw_canvas(){ ctx.fillStyle = 'black'; ctx.fillRect(0, 0, canvas.width, canvas.height); ctx.fillStyle = 'white'; ctx.fillRect(1, 1, canvas.width-2, canvas.height-2); } function draw_player(){ // draw du player, triangle équilatéral avec angle ctx.strokeStyle = 'black'; ctx.linewidth = 2; lx = player.x + (player.size/2)*Math.cos(Math.PI*player.angle/180); ly = player.y + (player.size/2)*Math.sin(-Math.PI*player.angle/180); ctx.moveTo(lx,ly); ctx.lineTo( lx + player.size*Math.cos(Math.PI*(player.angle-150)/180), ly + player.size*Math.sin(-Math.PI*(player.angle-150)/180)); ctx.stroke(); lx = lx + player.size*Math.cos(Math.PI*(player.angle-150)/180), ly = ly + player.size*Math.sin(-Math.PI*(player.angle-150)/180); ctx.moveTo(lx,ly); ctx.lineTo( lx + player.size*Math.cos(Math.PI*(90 + player.angle)/180), ly + player.size*Math.sin(-Math.PI*(90 + player.angle)/180)); ctx.stroke(); lx = lx + player.size*Math.cos(Math.PI*(90 + player.angle)/180); ly = ly + player.size*Math.sin(-Math.PI*(90 + player.angle)/180); ctx.moveTo(lx,ly); ctx.lineTo( lx + player.size*Math.cos(Math.PI*(player.angle-30)/180), ly + player.size*Math.sin(-Math.PI*(player.angle-30)/180)); ctx.stroke(); } function update_player(){ if (right){ player.angle -= 2; } else if (left){ player.angle += 2; } } setInterval(update,10); function update(){ ctx.clearRect(0,0,canvas.width,canvas.height); draw_canvas(); draw_player(); update_player(); }
HTML 代码
<!DOCTYPE html> <html> <head> <meta charset="utf-8"/> <title>Asteroids</title> <script type="text/javascript" src="asteroids.js"></script> </head> <body> <canvas id="canvas" width="512" height="512"></canvas> </body> </html>
问题原因及解决方法
问题出在Canvas的路径状态未重置上。Canvas绘图上下文会持续保留之前的路径记录,每次调用stroke()时,不仅会绘制当前新添加的路径,还会重复绘制所有未关闭的历史路径。哪怕用clearRect()清空了画布,路径状态依然存在,导致每次stroke()都会重绘历史路径,从而留下轨迹。
核心解决方法:在绘制玩家三角形前,调用ctx.beginPath()重置路径状态,确保每次绘制都是全新的路径,不会重复绘制历史轨迹。
同时可以优化两个细节:
linewidth需改为驼峰式的lineWidth,否则宽度设置不生效- 将三次
stroke()合并为一次,减少API调用次数,提升性能
修改后的draw_player()函数如下:
function draw_player(){ // 重置路径,解决轨迹问题的核心操作 ctx.beginPath(); // draw du player, triangle équilatéral avec angle ctx.strokeStyle = 'black'; ctx.lineWidth = 2; // 修正驼峰式命名 lx = player.x + (player.size/2)*Math.cos(Math.PI*player.angle/180); ly = player.y + (player.size/2)*Math.sin(-Math.PI*player.angle/180); ctx.moveTo(lx,ly); ctx.lineTo( lx + player.size*Math.cos(Math.PI*(player.angle-150)/180), ly + player.size*Math.sin(-Math.PI*(player.angle-150)/180)); lx = lx + player.size*Math.cos(Math.PI*(player.angle-150)/180), ly = ly + player.size*Math.sin(-Math.PI*(player.angle-150)/180); ctx.lineTo( lx + player.size*Math.cos(Math.PI*(90 + player.angle)/180), ly + player.size*Math.sin(-Math.PI*(90 + player.angle)/180)); lx = lx + player.size*Math.cos(Math.PI*(90 + player.angle)/180); ly = ly + player.size*Math.sin(-Math.PI*(90 + player.angle)/180); ctx.lineTo( lx + player.size*Math.cos(Math.PI*(player.angle-30)/180), ly + player.size*Math.sin(-Math.PI*(player.angle-30)/180)); // 一次stroke绘制整个三角形路径 ctx.stroke(); }
内容的提问来源于Stack Exchange,提问作者Gwaka
相关产品推荐
相关产品推荐

