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

如何用原生JavaScript阻止Sprite移出Canvas画布?

如何用原生JavaScript阻止Sprite移出Canvas画布

我正在用原生JS、HTML和少量CSS开发游戏,创建了两个Sprite方块,但它们向Canvas左右边缘移动时停不下来。现有解决方案都是用其他语言或库,求原生JS的解决办法。


问题分析

看你代码里的两处核心问题:

  • 在Sprite类的update方法中,this.velocity.x == 0是比较运算符,不是赋值操作,根本没把速度设为0;
  • 就算改了赋值,animate函数里每次都会重置velocity.x = 0,再根据按键重新设置速度,导致边界停止的逻辑被覆盖。

解决方案

要彻底解决这个问题,需要做两处关键修改:

  1. 修复赋值错误,同时在触碰边界时把Sprite的位置拉回画布范围内,避免偏移;
  2. 在移动逻辑中,判断Sprite是否已经处于画布边缘,禁止继续向边界方向移动。

修改后的完整代码

JavaScript 代码

const canvas = document.querySelector('#canvas');
const ctx = canvas.getContext("2d");

canvas.width = 1024;
canvas.height = 576;

function logMessage(msg) {
    document.querySelector('#myMessage').textContent += msg + '. ';
}

const gravity = 0.7;

class Sprite {
    constructor({position, velocity}) {
        this.position = position;
        this.velocity = velocity;
        this.height = 150;
        this.width = 50; // 将宽度设为类属性,避免硬编码
        this.lastKey;
    }

    draw() {
        ctx.fillStyle = 'red';
        ctx.fillRect(this.position.x, this.position.y, this.width, this.height);
    }

    update() {
        this.draw();
        this.position.x += this.velocity.x;
        this.position.y += this.velocity.y;

        // 左右边界检测与位置修正
        // 左边界处理
        if (this.position.x <= 0) {
            this.position.x = 0; // 拉回画布内
            this.velocity.x = 0; // 停止x方向移动
            logMessage("左边界触发");
        }
        // 右边界处理(Sprite宽度+位置不能超过画布宽度)
        else if (this.position.x + this.width >= canvas.width) {
            this.position.x = canvas.width - this.width; // 拉回画布内
            this.velocity.x = 0; // 停止x方向移动
            logMessage("右边界触发");
        }

        // 底部边界逻辑保留
        if (this.position.y + this.height + this.velocity.y >= canvas.height) {
            this.velocity.y = 0;
        } else {
            this.velocity.y += gravity;
        }
    }
}

const Player = new Sprite({
    position: {
        x: 0,
        y: 0
    },
    velocity: {
        x: 0,
        y: 0
    }
})

const Enemy = new Sprite({
    position: {
        x: 400,
        y: 100
    },
    velocity: {
        x: 0,
        y: 0
    }
})

const keys = {
    w: { pressed: false },
    a: { pressed: false },
    d: { pressed: false },
    ArrowUp: { pressed: false },
    ArrowLeft: { pressed: false },
    ArrowRight: { pressed: false }
}

function animate() {
    window.requestAnimationFrame(animate);
    ctx.fillStyle = 'black';
    ctx.fillRect(0, 0, canvas.width, canvas.height);
    Player.update();
    Enemy.update();

    // 玩家移动逻辑:增加边界判断,禁止边缘外移动
    Player.velocity.x = 0;
    if (keys.a.pressed && Player.lastKey === "a" && Player.position.x > 0) {
        Player.velocity.x = -5;
    } else if (keys.d.pressed && Player.lastKey === "d" && Player.position.x + Player.width < canvas.width) {
        Player.velocity.x = 5;
    }

    // 敌人移动逻辑同理
    Enemy.velocity.x = 0;
    if (keys.ArrowLeft.pressed && Enemy.lastKey === "ArrowLeft" && Enemy.position.x > 0) {
        Enemy.velocity.x = -5;
    } else if (keys.ArrowRight.pressed && Enemy.lastKey === "ArrowRight" && Enemy.position.x + Enemy.width < canvas.width) {
        Enemy.velocity.x = 5;
    }
}

animate();

window.addEventListener('keydown', (event) => {
    switch (event.key) {
        case 'w':
            Player.velocity.y = -20;
            break;
        case 'a':
            keys.a.pressed = true;
            Player.lastKey = "a";
            break;
        case 'd':
            keys.d.pressed = true;
            Player.lastKey = "d";
            break;
    }

    switch (event.key) {
        case 'ArrowUp':
            Enemy.velocity.y = -20;
            break;
        case 'ArrowRight':
            keys.ArrowRight.pressed = true;
            Enemy.lastKey = 'ArrowRight';
            break;
        case 'ArrowLeft':
            keys.ArrowLeft.pressed = true;
            Enemy.lastKey = 'ArrowLeft';
            break;
    }    
})

window.addEventListener('keyup', (event) => {
    switch (event.key) {
        case 'w':
            keys.w.pressed = false;
            break;
        case 'a':
            keys.a.pressed = false;
            break;
        case 'd':
            keys.d.pressed = false;
            break;
    }

    switch (event.key) {
        case 'ArrowUp':
            keys.ArrowUp.pressed = false;
            break;
        case 'ArrowLeft':
            keys.ArrowLeft.pressed = false;
            break;
        case 'ArrowRight':
            keys.ArrowRight.pressed = false;
            break;
    }
})

CSS 代码

#myConsole {
    background-color: black;
    color: white;
    min-height: 100px;
}

HTML 代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE-edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="author" content="Christian Davis">
    <link rel="stylesheet" href="styles.css">
    <title>Fighting Game</title>
</head>
<body>
    <canvas id="canvas"></canvas>
    <p id="myConsole">&gt;&nbsp;<span id="myMessage"></span></p>
    <script src="app.js"></script>
</body>
</html>

关键修改说明

  1. 给Sprite类添加了width属性,避免硬编码宽度值,提升代码可维护性;
  2. 在update方法中,不仅停止x方向速度,还将Sprite位置修正到画布边缘内,防止因速度导致的偏移;
  3. 在移动逻辑中增加边界判断:只有当Sprite不在对应边缘时,才允许向该方向移动,彻底阻止移出画布。

内容的提问来源于stack exchange,提问作者That New Coder

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 02:15:54