You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 18:45:55