首页 > 解决方案 > 两个不同画布上的 document.onkeydown 冲突

问题描述

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

这是第一个画布的代码:

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();
}

这是第二个画布的代码:

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();
    }
}

标签: javascriptcanvaskeyboard-events

解决方案


...但我找不到原因

因为你覆盖它:

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

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

如果你想在document, 上使用多个 keydown 处理程序addEventListener,而不是赋值,但你可能不希望这样。如果您将两个事件处理程序keydown放在同一个文档上,则两者都会被调用。你如何确定哪个应该做某事?

相反,将处理程序放在canvas元素上,而不是document. 这确实意味着用户必须聚焦画布才能让键盘事件转到它,但是您有两个画布,因此您需要一些方法让用户指示他们将输入定向到哪个画布。(请注意,为了更容易聚焦canvas元素,您可能需要将其放在tabindex="0"上面 [此处详细信息]。)

(如果需要,您仍然可以使用处理程序document,但要查看事件是否通过相关的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>


推荐阅读