document.onkeydown 两个不同 canvas 上的冲突

document.onkeydown conflicts on two different canvas

我正在尝试设置两个不同的 canvas 包含它们自己的实体,可通过不同的键盘输入移动。每个 canvas 都有自己的脚本文件。 然而,只有第二个 canvas 似乎处于活动状态,通过测试我得出的结论是第二个 canvas 的 document.onkeydown() 函数使我的第一个 canvas “非活动"(绘图功能很好,但 document.onkeydown 和 onkeyup 不工作),但我找不到原因。

这是第一个 canvas 的代码:

var canvas;
var player_y = 480/2;
const paddle_height = 100;
const paddle_width = 20;

document.addEventListener('DOMContentLoaded', function () {
canvas = document.getElementById('canvas');
draw();
});

function draw() {

var context = canvas.getContext('2d');

//canvas
context.fillStyle = color[3];
context.fillRect(0, 0, canvas.width, canvas.height);

// Draw middle line
context.strokeStyle = color[1];
context.beginPath();
context.moveTo(canvas.width / 2, 0);
context.lineTo(canvas.width / 2, canvas.height);
context.stroke();

//draw left paddle
context.fillStyle = color[1];
context.beginPath();
context.fillRect(10, player_y-paddle_height/2, paddle_width, paddle_height);
context.fill();
}

var UP = false; 
var DOWN = false;

function move() {
if(UP && player_y > paddle_height/2 ) { 
    player_y -= 5;
}
if(DOWN && player_y < 480 - paddle_height/2) {
    player_y += 5;  
}   
draw(); 
}

document.onkeydown = function(e) {
if(e.keyCode == 38) UP = true;
if(e.keyCode == 40) DOWN = true;
}

document.onkeyup = function(e) {
if(e.keyCode == 38) UP = false;
if (e.keyCode == 40) DOWN = false;
}

setInterval (update, 10);
function update() {
move();
}

这是第二个代码 canvas:

var canvas_exemple;
var cube = {
sz: 50,
x: 400/2,
y: 200/2,
sp: 5
}


document.addEventListener('DOMContentLoaded', function () {
canvas_exemple = document.getElementById('canvas_exemple');
draw_exemple();
});


function draw_exemple() {

var context = canvas_exemple.getContext('2d');

//canvas
context.fillStyle = "#242423";
context.fillRect(0, 0, canvas_exemple.width, canvas_exemple.height);


//draw cube
context.fillStyle = "#c70e0e";
context.beginPath();
context.fillRect(cube.x, cube.y, cube.sz, cube.sz);
context.fill();
}

function move_exemple() {
document.onkeydown = function (e) {
    if (e.keyCode == 90 && cube.y > 0) cube.y -= cube.sp;
    if (e.keyCode == 83 && cube.y < 200 - cube.sz) cube.y += cube.sp;
    if (e.keyCode == 81 && cube.x > 0) cube.x -= cube.sp;
    if (e.keyCode == 68 && cube.x < 400 - cube.sz) cube.x += cube.sp;
    draw_exemple();
    }
}

...but I cannot find why

因为你覆盖了它:

// First file:
document.onkeydown = function (e) { /*...*/ };
// Second file:
document.onkeydown = function (e) { /*...*/ }; // <=== Overwrites the first

这是对 属性 的分配。 属性 只能有一个值(尽管 DOM 中有一些奇怪的属性,例如 document.cookie)。

如果你想在 document 上有多个按键处理程序,请使用 addEventListener,而不是赋值,但你可能不希望那样。如果将 keydown 的两个事件处理程序放在同一个文档中,both 将被调用。你如何确定哪些应该做某事?

相反,将处理程序放在 canvas 元素上,而不是 document。这确实意味着用户必须关注 canvas 才能让键盘事件转到它,但是你有两个 canvases,所以你需要一些方法让用户指示是哪一个他们正在指导输入。 (请注意,为了更容易聚焦 canvas 元素,您可能需要将 tabindex="0" 放在 [details here] 上。)

(如果需要,您仍然可以在 document 上使用处理程序,但要查看事件是否经过相关的 canvas,但在这种情况下,仅添加处理程序会更简单canvas.)

我在下面包含了一个示例。在这个例子中,有很多重复的代码,通常我会做一个函数,我可以把东西传递进去,但我把它分开了,因为代码在你的问题中是分开的,有时重复的代码比参数化代码更容易阅读.您可以使用鼠标或 Tab 在 canvas 之间切换。活动 canvas 周围有绿色边框。

// Canvas 1
const canvas1 = document.getElementById("canvas1");
const ctx1 = canvas1.getContext("2d");
let x1 = Math.floor(canvas1.width / 2);
let y1 = Math.floor(canvas1.height / 2);
draw1();
canvas1.addEventListener("keydown", (event) => {
    switch (event.key) {
        case "ArrowLeft":
            x1 = x1 > 0 ? x1 - 1 : x1;
            break;
        case "ArrowRight":
            x1 = x1 < 300 ? x1 + 1 : x1;
            break;
        case "ArrowUp":
            y1 = y1 > 0 ? y1 - 1 : y1;
            break;
        case "ArrowDown":
            y1 = y1 < 300 ? y1 + 1 : y1;
            break;
        default:
            return;
    }
    event.preventDefault();
    draw1();
});
function draw1() {
    ctx1.fillRect(x1, y1, 1, 1);
}

// Canvas 2
const canvas2 = document.getElementById("canvas2");
const ctx2 = canvas2.getContext("2d");
let x2 = Math.floor(canvas2.width / 2);
let y2 = Math.floor(canvas2.height / 2);
ctx2.fillRect(x2, y2, 1, 1);
canvas2.addEventListener("keydown", (event) => {
    let originalX = x2;
    let originalY = y2;
    switch (event.key) {
        case "ArrowLeft":
            x2 = x2 > 0 ? x2 - 5 : x2;
            break;
        case "ArrowRight":
            x2 = x2 < 300 ? x2 + 5 : x2;
            break;
        case "ArrowUp":
            y2 = y2 > 0 ? y2 - 5 : y2;
            break;
        case "ArrowDown":
            y2 = y2 < 300 ? y2 + 5 : y2;
            break;
        default:
            return;
    }
    event.preventDefault();
    if (x2 !== originalX || y2 !== originalY) {
        ctx2.strokeStyle = "blue";
        ctx2.beginPath();
        ctx2.moveTo(originalX, originalY);
        ctx2.lineTo(x2, y2);
        ctx2.closePath();
        ctx2.stroke();
    }
});

// Focus the first canvas by default
canvas1.focus();
html {
    box-sizing: border-box;
    font-family: sans-serif;
}
*, *:before, *:after {
    box-sizing: inherit;
}
canvas {
    border: 1px solid grey;
    margin: 8px;
}
canvas:focus {
    outline: green solid 3px;
}
<div>Canvas 1:</div>
<canvas id="canvas1" tabindex="0" width="200" height="200"></canvas>
<div>Canvas 2:</div>
<canvas id="canvas2" tabindex="0" width="200" height="200"></canvas>