DOM 事件是否适用于指针锁定?



我在画布元素上使用指针锁,并且画布全屏显示。我想检测右键单击和左键单击以响应它们。是否可以全屏和指针锁定响应点击?我已经知道如何使用指针锁定 api 和全屏 api,我不想要任何解释如何使用它们的答案。任何帮助将不胜感激。

根据我所做的实验,简短的回答是"视情况而定"。请看下面的演示。有一个画布,缩放为每个维度的屏幕大小的四分之一。将光标移到画布上时,画布上会出现一个白色圆圈。左键单击时,您将在画布上绘制一个红色圆圈,右键单击时,您将在画布上绘制一个青色圆圈。当您单击"全屏"按钮时,您将激活指针锁定并进入全屏模式。如果按"Esc"键,您将退出指针锁定和全屏模式。

请注意,您需要将代码复制并粘贴到文件中并加载它。如果您只单击"运行代码片段",演示将无法运行。

就你的问题而言,我知道有两个问题:

  1. 在 Chrome 中,即使在全屏/指针锁定的情况下,也会触发右键和左键点击事件。但是,在 Firefox 中,只会触发左键单击事件;我无法使用我尝试的任何处理程序(clickmousedownmouseupcontextmenu)获取右键单击事件。当不在全屏/指针锁定中时,左键和右键单击事件都会在两个浏览器中按预期触发。如果有人有任何解决方案可以在全屏/指针锁定时收听右键单击事件,我很想听听。
  2. 似乎在Chrome/Firefox的指针锁定中,事件不再滴落到带有指针锁定的元素中包含的元素中,而是继续冒泡到父元素。所以在演示中,canvasdiv内。div具有指针锁定。onclick处理程序附加到canvasdivdocument,以在控制台中报告单击事件。如果没有指针锁定,单击画布会触发所有三个元素(canvasdivdocument)的onclick处理程序。但是,在div上锁定指针时,canvasonclick处理程序永远不会被触发,尽管divdocument的处理程序会触发。

我还发现了 Firefox 的其他几个怪癖,虽然与你最初的问题没有直接关系,但可能对有兴趣实现这种事情的人有所帮助:

  1. 当进入全屏模式时,Firefox 会将样式应用于全屏元素以使其填满屏幕。当canvas全屏放置时,我无法正确设置样式(即占据全屏)。相反,我不得不将canvas包裹在div中,然后在div上全屏进入。有关更多信息,请参阅 MDN 上的全屏 API 文档:

如果你试图在Gecko上模拟WebKit的行为,你需要将你想要呈现的元素放在另一个元素中,你将全屏显示,并使用CSS规则来调整内部元素以匹配你想要的外观。

  1. 在 Firefox 中,激活全屏模式停用了指针锁定。为了激活两者,我必须首先激活全屏模式,然后激活指针锁定。但是简单的两行代码:

    canvasContainer.requestFullscreen();
    canvasContainer.requestPointerLock();
    

    没有用。我对正在发生的事情的理解是,对requestPointerLock的调用是在全屏模式完全建立之前启动的。这导致指针锁定被激活,然后再次快速停用。我发现有必要等到全屏模式完全建立后再调用requestPointerLock()。检查document.mozFullScreenElement !== null似乎足以检查全屏模式是否完全可操作。以下单击处理程序定义为我解决了这个问题:

    document.getElementById('fullscreen_button').onclick = function(e) {
    // When button is clicked, enter both full screen and pointer lock
    canvasContainer.requestFullscreen();
    var timeout = 2000;
    var interval = window.setInterval(function() {
    if (document.mozFullScreenElement !== null) {
    window.clearInterval(interval);
    canvasContainer.requestPointerLock();
    } else if (timeout <= 0) {
    addErrorMessage('Unable to establish pointer lock.');
    clearTimeout(interval);
    } else {
    timeout -= 50;
    }
    }, 50);
    }
    

    此功能反复检查是否建立了全屏模式。如果是,它将启动指针锁定。如果 2 秒后无法确定全屏模式,则会超时。

我没有在IE中进行任何测试。

<!DOCTYPE HTML>
<html lang="en-US">
<head>
<style>
</style>
</head>
<body>
<p id="msgs">Click 'Full screen' button below to go full screen. <br>
Click the left mouse button to draw a red circle. <br>
Click any other mouse button to draw a cyan circle. <br>
Press the 'Esc' key to exit full screen.</p>
<div id="canvas_container">
<canvas id="canvas"> </canvas>
</div>
<br>
<button id='fullscreen_button'>Full screen</button>
</body>
<script>
// Display constants
var CANVAS_BG_COLOR = 'rgb(75, 75, 75)';
var LEFT_CLICK_COLOR = 'rgb(255, 150, 150)';
var OTHER_CLICK_COLOR = 'rgb(150, 255, 255)';
var CURSOR_COLOR = 'rgb(200, 200, 200)';
var CANVAS_SCALING_FACTOR = 4;              // Ratio between screen dimension and canvas dimension before going full-screen
// Store mouse position
var mouseX, mouseY;
// Setup onscreen canvas, smaller than the screen by a factor of CANVAS_SCALING_FACTOR
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
canvas.width = screen.width/CANVAS_SCALING_FACTOR;
canvas.height = screen.height/CANVAS_SCALING_FACTOR;
// Create an offscreen canvas that's the same as the size of the screen
var offscreenCanvas = document.createElement('canvas');
var offscreenCtx = offscreenCanvas.getContext('2d');
offscreenCanvas.width = screen.width;
offscreenCanvas.height = screen.height;

var canvasContainer = document.getElementById('canvas_container');
// Radius of the circle drawn and of the circle cursor
var circleRadius = 12;
var cursorRadius = circleRadius/CANVAS_SCALING_FACTOR
offscreenCtx.drawCircle = ctx.drawCircle = function (x, y, color, radius) {
this.fillStyle = color;
this.beginPath();
this.arc(x, y, radius, 0, 2*Math.PI, true);
this.fill();
}
offscreenCtx.clearCanvas = function() {
this.fillStyle = CANVAS_BG_COLOR;
this.fillRect(0, 0, this.canvas.width, this.canvas.height);
}
ctx.update = function() {
// Copy the offscreen canvas, scaling down if not in full-screen mode
this.drawImage(offscreenCanvas, 0, 0, offscreenCanvas.width, offscreenCanvas.height,
0, 0, canvas.width, canvas.height);
// Draw the cursor
this.drawCircle(mouseX, mouseY, CURSOR_COLOR, cursorRadius);
}
function pointerLockActive() {
return document.pointerLockElement===canvasContainer || document.mozPointerLockElement === canvasContainer;
}
// Perform initial canvas setup
offscreenCtx.clearCanvas();
ctx.update();
// Setup pointerlock and fullscreen API functions for cross-browser support
function addErrorMessage(msg) {
document.getElementById('msgs').innerHTML += ('<br><font color="red">' + msg + '</font>');
}
canvasContainer.requestPointerLock = canvasContainer.requestPointerLock || canvasContainer.mozRequestPointerLock;
canvasContainer.requestFullscreen = canvasContainer.webkitRequestFullscreen || canvasContainer.mozRequestFullScreen || canvasContainer.msRequestFullscreen
if (!canvasContainer.requestPointerLock) addErrorMessage('Error: Pointer lock not available');
if (!canvasContainer.requestFullscreen) addErrorMessage('Error: Full screen mode not available');
canvasContainer.addEventListener('mousemove', function(e) {
if (pointerLockActive()) {
// If in pointer lock, then cursor positions need to be updated manually;
// Normal cursor positions (e.g. e.clientX and e.clientY) don't get updated in pointer lock
mouseX += e.movementX, mouseY += e.movementY;
// Prevent the mouse from moving off-screen
mouseX = Math.min(Math.max(0, mouseX), canvas.width);
mouseY = Math.min(Math.max(0, mouseY), canvas.height);
} else {
// If pointer lock is inactive, then mouse position is just position relative to canvas offset
mouseX = (e.pageX - canvas.offsetLeft)
mouseY = (e.pageY - canvas.offsetTop)
}
ctx.update();   // Update the onscreen canvas
}, false);
// Handle entering and exiting pointer lock; pointer lock status is yoked to full screen status; both are entered and exited at the same time
document.addEventListener('pointerlockchange', function(e) {
if (!pointerLockActive()) {
console.log('Pointer lock deactivated');
canvas.width /= CANVAS_SCALING_FACTOR;
canvas.height /= CANVAS_SCALING_FACTOR
cursorRadius /= CANVAS_SCALING_FACTOR;

} else {
console.log('Pointer lock activated')
canvas.width *= CANVAS_SCALING_FACTOR;
canvas.height *= CANVAS_SCALING_FACTOR;
cursorRadius *= CANVAS_SCALING_FACTOR;
// Set the initial mouse position to be the middle of the canvas
mouseX = screen.width/2, mouseY = screen.height/2;
}
// Update the onscreen canvas
ctx.update();
});
document.getElementById('fullscreen_button').onclick = function(e) {
// When button is clicked, enter both full screen and pointer lock
canvasContainer.requestFullscreen();
var timeout = 2000;
var interval = window.setInterval(function() {
if (document.mozFullScreenElement !== null) {
window.clearInterval(interval);
canvasContainer.requestPointerLock();
} else if (timeout <= 0) {
addErrorMessage('Unable to establish pointer lock.');
clearTimeout(interval);
} else {
timeout -= 50;
}
}, 50);

}
canvasContainer.onclick = function(e) {
console.log('canvasContainer clicked');
if (pointerLockActive())
// If pointer lock is active, then use the mouseX and mouseY positions that are manually updated by the mousemove event handler
var cursorX = mouseX, cursorY = mouseY;
else
// Otherwise use the mouse positions passed in the event object
// If not in full screen mode, the cursor position has to be scaled up, because the mouse position is relative to the onscreen canvas, but we're drawing on the offscreen canvas, which is larger by a factor of fullscreenScale
var cursorX = (e.pageX - canvas.offsetLeft)*CANVAS_SCALING_FACTOR, cursorY = (e.pageY - canvas.offsetTop)*CANVAS_SCALING_FACTOR;
// If the left mouse button is clicked (e.which===1), draw a circle of one color
// If any other mouse button is clicked, draw a circle of another color
var color = e.which === 1 ? LEFT_CLICK_COLOR : OTHER_CLICK_COLOR;
offscreenCtx.drawCircle(cursorX, cursorY, color, circleRadius);
ctx.update();
};
// Detect canvas right-click events. Prevent default behavior (e.g. context menu display) and pass on to the onclick handler to do the rest of the work
canvasContainer.oncontextmenu = function(e) {
e.preventDefault();
this.onclick(e);
}
canvas.onclick = function() {
console.log('canvas clicked');
}
document.onclick = function() {
console.log('document clicked');
}
</script>    
</html>

这适用于我在指针锁定后处理右键单击。

const onMouseDown = (evt) => {
switch (evt.which) {
case 1: return handleLeftClick();
case 3: return handleRightClick();
}
};
document.body.addEventListener('mousedown', onMouseDown, true);

最新更新