我在画布元素上使用了指针锁定,并且画布处于全屏状态。我想检测右键单击和左键单击以响应它们。是否可以响应全屏单击和指针锁定?我已经知道如何使用指针锁定api和全屏api,我不需要任何解释如何使用它们的答案。任何帮助,将不胜感激。
最佳答案
根据我所做的实验,简短的答案是“取决于情况”。看一下下面的演示。在每个尺寸中都有一个画布,缩放为屏幕尺寸的四分之一。将光标移到其上方时,画布上会出现一个白色圆圈。左键单击时,将在画布上绘制一个红色圆圈,右键单击时,将在画布上绘制一个青色圆圈。单击“全屏”按钮时,将激活指针锁定并进入全屏模式。如果按“ Esc”键,将退出指针锁定和全屏模式。
请注意,您需要将代码复制并粘贴到文件中并加载。如果仅单击“运行代码段”,则演示将无法运行。
关于您的问题,我知道有两个问题:
在Chrome中,即使处于全屏/指针锁定状态,也会触发右键单击事件和左键单击事件。但是,在Firefox中,仅触发左键单击事件。我无法使用尝试的任何处理程序(click
,mousedown
,mouseup
,contextmenu
)获得右键单击事件。如果未处于全屏/指针锁定状态,则在两个浏览器中都会同时触发左键单击事件和右键单击事件。如果有人在全屏/指针锁定状态下有任何解决方案来收听右键单击事件,我很想听听它们。
似乎在两个Chrome / Firefox中的指针锁定中,事件都不再滴入具有指针锁定的元素中包含的元素,而是继续上升到父元素。因此,在演示中,canvas
位于div
内部。 div
具有指针锁定。 onclick
处理程序附加到canvas
,div
和document
,以报告控制台中的单击事件。如果没有指针锁定,则单击画布会触发所有三个元素(onclick
,canvas
和div
)的document
处理程序。但是,将指针锁定在div
上时,尽管onclick
和canvas
的处理程序会执行操作,但永远不会触发div
的document
处理程序。
我还发现了Firefox的其他一些怪癖,尽管它们与您最初提出的问题没有直接关系,但对有兴趣实现此类事情的人们可能会有所帮助:
进入全屏模式后,Firefox会将样式应用于全屏元素以使其充满整个屏幕。将canvas
设置为全屏显示时,我无法正确设置其样式(即占据全屏显示)。相反,我不得不将canvas
包装在div
中,并在div
上输入全屏。有关更多信息,请参见Fullscreen API documentation on MDN:
如果要在Gecko上模拟WebKit的行为,则需要将要呈现的元素放置在另一个元素中,将其全屏显示,并使用CSS规则调整内部元素以匹配所需的外观。
在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>