在以下代码段中,您会注意到叠加层会要求您点击“随处点击”,并会在mousedown
和mouseup
个事件的画布上添加圈子。虽然叠加文本在mousedown上消失,但如果单击叠加元素上的任何位置,画布不会获取mousedown事件来绘制起始圆。
var canvas = document.getElementById('target'),
context = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
canvas.addEventListener('mousedown', addStartNode);
canvas.addEventListener('mouseup', addEndNode);
document.body.addEventListener('mousedown', hideOverlay);
document.body.addEventListener('mouseup', showOverlay);
function hideOverlay() {
document.getElementById('overlay').style.display = 'none';
}
function showOverlay() {
document.getElementById('overlay').style.display = 'block';
}
function addStartNode(evt) {
drawCircle(evt.clientX, evt.clientY, 10, 'green');
}
function addEndNode(evt) {
drawCircle(evt.clientX, evt.clientY, 10, 'blue');
}
function drawCircle(x, y, r, c) {
context.beginPath();
context.arc(x, y, r, 0, 2 * Math.PI, false);
context.fillStyle = c;
context.fill();
context.lineWidth = 5;
context.strokeStyle = '#003300';
context.stroke();
}

html,
body {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
}
#overlay {
position: absolute;
top: 10%;
left: 1em;
right: 1em;
font-size: 4em;
text-align: center;
z-index: 10;
background: rgba(255, 255, 255, 0.7);
}
#target {
position: relative;
width: 100%;
height: 100%;
background: aqua;
}
.node {
position: absolute;
background: blue;
width: 20px;
height: 20px;
transform: translate(-50%, -50%);
}

<div id="overlay">
Click Anywhere
</div>
<canvas id="target"></canvas>
&#13;
我想过将事件处理程序移动到容器元素或body
元素上,这会在传播时捕获事件,但有时候这是不可能的,例如当你&# 39;重新使用HTML5 game engine处理交互。
有没有人有这方面的聪明解决方案?
我真正想到的是将鼠标位置转换为画布的情况要复杂得多,例如在使用游戏引擎或其他画布库时。在以下代码段中,我使用了PIXI.js。请注意如何拖动左上角和中间底部节点,但不能拖动文本覆盖的右侧节点。
document.body.addEventListener('mousedown', hideOverlay);
document.body.addEventListener('mouseup', showOverlay);
function hideOverlay() {
document.getElementById('overlay').style.display = 'none';
}
function showOverlay() {
document.getElementById('overlay').style.display = 'block';
}
var nodeRadius = 50;
function Node(x, y) {
this.g = new PIXI.Graphics();
this.position.x = x;
this.position.y = y;
this.g.node = this;
this.drawCircle();
this.g.pivot = new PIXI.Point(nodeRadius / 2, nodeRadius / 2);
this.g.interactive = true;
this.defaultPos = new PIXI.Point(this.position.x, this.position.y);
this.g.on('mousedown', selectNode);
this.g.on('mouseup', connectNode);
this.g.on('mousemove', moveNode);
}
Node.prototype = {
reset: function() {
this.position.x = this.defaultPos.x;
this.position.y = this.defaultPos.y;
},
drawCircle: function() {
this.g.clear();
this.g.beginFill(0x3333FF, 1);
this.g.drawCircle(0, 0, nodeRadius);
this.g.endFill();
},
connectTo: function(node) {
this.drawCircle();
this.g.moveTo(0, 0);
this.g.lineStyle(5, 0xDDEEFF);
this.g.lineTo(node.position.x - this.position.x, node.position.y - this.position.y);
},
get position() {
return this.g.position;
},
set position(p) {
this.g.position = p;
}
};
var renderer = PIXI.autoDetectRenderer(600, 400),
container = new PIXI.Container(),
selectedNode = null;
container.position.x = -150;
container.position.y = 200;
container.scale.x = 0.3;
container.scale.y = 0.3;
var nodes = [
new Node(700, -500),
new Node(2200, 50),
new Node(1500, 450)
];
for (var i = 0; i < nodes.length; i++) {
container.addChild(nodes[i].g);
}
document.getElementById('content').appendChild(renderer.view);
requestAnimationFrame(animate);
function animate() {
renderer.render(container);
requestAnimationFrame(animate);
}
function selectNode() {
this.node.drawCircle();
selectedNode = this.node;
}
function connectNode() {
if (selectedNode) {
selectedNode.reset();
selectedNode = null;
}
}
function moveNode() {
if (selectedNode) {
var mousePos = renderer.plugins.interaction.mouse.getLocalPosition(container);
selectedNode.position.x = mousePos.x;
selectedNode.position.y = mousePos.y;
checkCollision();
}
}
function checkCollision() {
if (selectedNode) {
for (var i = 0; i < nodes.length; i++) {
if (nodes[i] !== selectedNode && dist(selectedNode.position, nodes[i].position) < nodeRadius * 2) {
selectedNode.reset();
selectedNode.connectTo(nodes[i]);
selectedNode = null;
break;
}
}
}
}
function dist(p1, p2) {
return Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2));
}
&#13;
html,
body {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
}
#overlay {
position: absolute;
top: 100px;
left: 10px;
width: 580px;
font-size: 4em;
text-align: center;
z-index: 100;
background: rgba(255, 255, 255, 0.5);
}
#content {
display: inline-block;
position: relative;
margin: 1em;
}
&#13;
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="style.css">
<!--<script src="https://cdnjs.cloudflare.com/ajax/libs/pixi.js/3.0.8/pixi.min.js"></script>-->
<script src="https://cdnjs.cloudflare.com/ajax/libs/pixi.js/3.0.8/pixi.js"></script>
</head>
<body>
<div id="content">
<div id="overlay">
Drag one node to another
</div>
<script src="script.js"></script>
</div>
</body>
</html>
&#13;
我故意移动并缩放container
,因为这经常发生在游戏中。画布本身可能偏离页面,甚至可能与叠加共享相同的父级,这进一步使鼠标位置转换为游戏坐标变得复杂。
这就是为什么我认为以某种方式移除 mousedown
事件之前的覆盖元素会很好,因为那时我们不必担心它。
答案 0 :(得分:0)
在叠加层中添加eventlistener也可以解决您的问题:
document.getElementById("overlay").addEventListener('mousedown', addStartNode);
var canvas = document.getElementById('target'),
context = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
canvas.addEventListener('mousedown', addStartNode);
canvas.addEventListener('mouseup', addEndNode);
document.getElementById("overlay").addEventListener('mousedown', addStartNode);
document.body.addEventListener('mousedown', hideOverlay);
document.body.addEventListener('mouseup', showOverlay);
function hideOverlay() {
document.getElementById('overlay').style.display = 'none';
}
function showOverlay() {
document.getElementById('overlay').style.display = 'block';
}
function addStartNode(evt) {
drawCircle(evt.clientX, evt.clientY, 10, 'green');
}
function addEndNode(evt) {
drawCircle(evt.clientX, evt.clientY, 10, 'blue');
}
function drawCircle(x, y, r, c) {
context.beginPath();
context.arc(x, y, r, 0, 2 * Math.PI, false);
context.fillStyle = c;
context.fill();
context.lineWidth = 5;
context.strokeStyle = '#003300';
context.stroke();
}
&#13;
html,
body {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
}
#overlay {
position: absolute;
top: 10%;
left: 1em;
right: 1em;
font-size: 4em;
text-align: center;
z-index: 10;
background: rgba(255, 255, 255, 0.7);
}
#target {
position: relative;
width: 100%;
height: 100%;
background: aqua;
}
.node {
position: absolute;
background: blue;
width: 20px;
height: 20px;
transform: translate(-50%, -50%);
}
&#13;
<div id="overlay">
Click Anywhere
</div>
<canvas id="target"></canvas>
&#13;
答案 1 :(得分:0)
事件在dom的层次结构中冒出来。因为你的隐藏过度&#39;是一个身体上的事件,最重要的元素,它不会冒泡到画布。它不得不泡下来,它没有。由于叠加层位于绝对位置,因此它不在画布内,因此点击它不会冒泡到画布。
简而言之,简单的解决方案是在mousedown上只有一个事件处理程序,例如body.mousedown,这意味着你想要在屏幕上发生事件的同时做同样的事情,即隐藏覆盖和绘图画布上的圆圈,所以不要让两个事件处理程序复杂化。在body.mousedown中,隐藏叠加层,然后画一个圆圈。
如果您只是从hideOverlay调用addStartNode,那么您的代码也会按预期工作,但为什么会这样?
答案 2 :(得分:0)
您可以简单地为每个鼠标事件使用唯一的事件检测。
首先删除canvas.addEventListener()
,然后收集显示/隐藏叠加层并绘制蓝色/绿色圆圈,如下所示:
var canvas = document.getElementById('target'),
context = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
document.body.addEventListener('mousedown', mouseDown);
document.body.addEventListener('mouseup', mouseUp);
function mouseDown(evt) {
document.getElementById('overlay').style.display = 'none';
drawCircle(evt.clientX, evt.clientY, 10, 'green');
}
function mouseUp(evt) {
drawCircle(evt.clientX, evt.clientY, 10, 'blue');
document.getElementById('overlay').style.display = 'block';
}
function drawCircle(x, y, r, c) {
context.beginPath();
context.arc(x, y, r, 0, 2 * Math.PI, false);
context.fillStyle = c;
context.fill();
context.lineWidth = 5;
context.strokeStyle = '#003300';
context.stroke();
}
答案 3 :(得分:0)
nus'关于使用dispatchEvent
的评论可以解决问题。
document.getElementById('overlay').addEventListener('mousedown', function(evt) {
setTimeout(function() {
renderer.view.dispatchEvent(evt);
},0);
});
document.body.addEventListener('mousedown', hideOverlay);
document.body.addEventListener('mouseup', showOverlay);
function hideOverlay() {
document.getElementById('overlay').style.display = 'none';
}
function showOverlay() {
document.getElementById('overlay').style.display = 'block';
}
var nodeRadius = 50;
function Node(x, y) {
this.g = new PIXI.Graphics();
this.position.x = x;
this.position.y = y;
this.g.node = this;
this.drawCircle();
this.g.pivot = new PIXI.Point(nodeRadius / 2, nodeRadius / 2);
this.g.interactive = true;
this.defaultPos = new PIXI.Point(this.position.x, this.position.y);
this.g.on('mousedown', selectNode);
this.g.on('mouseup', connectNode);
this.g.on('mousemove', moveNode);
}
Node.prototype = {
reset: function() {
this.position.x = this.defaultPos.x;
this.position.y = this.defaultPos.y;
},
drawCircle: function() {
this.g.clear();
this.g.beginFill(0x3333FF, 1);
this.g.drawCircle(0, 0, nodeRadius);
this.g.endFill();
},
connectTo: function(node) {
this.drawCircle();
this.g.moveTo(0, 0);
this.g.lineStyle(5, 0xDDEEFF);
this.g.lineTo(node.position.x - this.position.x, node.position.y - this.position.y);
},
get position() {
return this.g.position;
},
set position(p) {
this.g.position = p;
}
};
var renderer = PIXI.autoDetectRenderer(600, 400),
container = new PIXI.Container(),
selectedNode = null;
document.getElementById('overlay').addEventListener('mousedown', function(evt) {
setTimeout(function() {
renderer.view.dispatchEvent(evt);
}, 0);
});
container.position.x = -150;
container.position.y = 200;
container.scale.x = 0.3;
container.scale.y = 0.3;
var nodes = [
new Node(700, -500),
new Node(2200, 50),
new Node(1500, 450)
];
for (var i = 0; i < nodes.length; i++) {
container.addChild(nodes[i].g);
}
document.getElementById('content').appendChild(renderer.view);
requestAnimationFrame(animate);
function animate() {
renderer.render(container);
requestAnimationFrame(animate);
}
function selectNode() {
this.node.drawCircle();
selectedNode = this.node;
}
function connectNode() {
if (selectedNode) {
selectedNode.reset();
selectedNode = null;
}
}
function moveNode() {
if (selectedNode) {
var mousePos = renderer.plugins.interaction.mouse.getLocalPosition(container);
selectedNode.position.x = mousePos.x;
selectedNode.position.y = mousePos.y;
checkCollision();
}
}
function checkCollision() {
if (selectedNode) {
for (var i = 0; i < nodes.length; i++) {
if (nodes[i] !== selectedNode && dist(selectedNode.position, nodes[i].position) < nodeRadius * 2) {
selectedNode.reset();
selectedNode.connectTo(nodes[i]);
selectedNode = null;
break;
}
}
}
}
function dist(p1, p2) {
return Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2));
}
&#13;
html,
body {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
}
#overlay {
position: absolute;
top: 100px;
left: 10px;
width: 580px;
font-size: 4em;
text-align: center;
z-index: 100;
background: rgba(255, 255, 255, 0.5);
}
#content {
display: inline-block;
position: relative;
margin: 1em;
}
&#13;
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="style.css">
<!--<script src="https://cdnjs.cloudflare.com/ajax/libs/pixi.js/3.0.8/pixi.min.js"></script>-->
<script src="https://cdnjs.cloudflare.com/ajax/libs/pixi.js/3.0.8/pixi.js"></script>
</head>
<body>
<div id="content">
<div id="overlay">
Drag one node to another
</div>
<script src="script.js"></script>
</div>
</body>
</html>
&#13;
请注意,它需要在超时时间内完成,或者您获得InvalidStateError
,因为&#34;事件已经被调度&#34;。