如何在Canvas中移动矩形?求助解决键盘控制失效问题
问题修复方案
直接说你代码里的核心问题和修复方式:
1. 事件绑定完全错误
你写的player.document.addEventListener无效,player是自定义普通对象,根本没有document属性,应该直接给document绑定键盘事件。
2. 移动逻辑只画不更新坐标
按下方向键只是在新位置画矩形,但player的x/y坐标没修改,下次移动还是基于初始位置,而且旧矩形会留在画布上,看起来像没动。
3. 需要重绘整个场景
Canvas是像素级绘制,要实现移动效果,每次按键后必须先清空画布,再重新绘制迷宫和新位置的玩家,否则会留下移动轨迹。
4. 未定义的update函数
代码最后调用update()但你没写这个函数,浏览器会直接报错。
修正后的完整代码
var can = document.querySelector('canvas'); can.style.position = 'absolute'; can.style.top = "25%"; can.style.left = "30%"; const maze = [ [1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], [1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0], [1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0], [1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0], [1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0], [1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0], [1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0], [1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0], [1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0], [1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0], [1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0], [1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0], [1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0], [1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0], [1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0], [1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0], [1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1], [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 2], ]; const c = document.getElementById("gameCanvas"); const ctx = c.getContext("2d"); const BLOCK_SIZE = 22; // 封装绘制迷宫的函数,方便重复调用 function drawMaze() { for (let i = 0; i < maze.length; i++) { // 遍历每行的所有列,用maze[i].length避免列数不一致的问题 for (let j = 0; j < maze[i].length; j++) { if (maze[i][j] == 1) { ctx.fillStyle = 'red'; ctx.fillRect(BLOCK_SIZE * j, BLOCK_SIZE * i, BLOCK_SIZE, BLOCK_SIZE); } if (maze[i][j] == 2) { ctx.fillStyle = 'green'; ctx.fillRect(BLOCK_SIZE * j, BLOCK_SIZE * i, BLOCK_SIZE, BLOCK_SIZE); } } } } // 封装绘制玩家的函数 function drawPlayer() { ctx.fillStyle = "#2a6592"; ctx.fillRect(player.x, player.y, player.width, player.height); } let player = { x: BLOCK_SIZE, y: BLOCK_SIZE, width: 22, height: 22, }; // 初始绘制场景 drawMaze(); drawPlayer(); // 正确绑定键盘事件到document document.addEventListener("keydown", function (event) { if (event.defaultPrevented) { return; } // 先根据按键更新玩家坐标 switch (event.key) { case "ArrowDown": // Canvas的y轴向下为正,所以向下移动是y增加 player.y += BLOCK_SIZE; break; case "ArrowUp": player.y -= BLOCK_SIZE; break; case "ArrowLeft": player.x -= BLOCK_SIZE; break; case "ArrowRight": player.x += BLOCK_SIZE; break; default: return; } // 清空整个画布,然后重绘迷宫和玩家 ctx.clearRect(0, 0, c.width, c.height); drawMaze(); drawPlayer(); event.preventDefault(); }, true);
关键修复点说明
- 把绘制迷宫和玩家的逻辑封装成函数,方便每次移动后重复调用
- 修正事件绑定对象,直接绑定到
document - 按键时先更新
player的坐标,再清空画布重绘整个场景 - 修正移动方向的坐标计算(Canvas y轴向下,所以向下移动是y增加)
- 给循环变量
i和j加上let声明,避免全局变量污染 - 内层循环用
maze[i].length遍历每行的所有列,适配不同长度的行
内容的提问来源于stack exchange,提问作者JDio
相关产品推荐
相关产品推荐

