如何向画布元素添加一个简单的 onClick 事件处理程序?

我是一个经验丰富的 Java 程序员,但是我正在研究一些 JavaScript/HTML5的东西,这是十年来的第一次。我完全不知道什么才是最简单的事情。

作为一个例子,我只是想画一些东西,并添加一个事件处理程序到它。我确信我在做一些愚蠢的事情,但是我搜索了所有的地方,没有一个建议(例如这个问题的答案: 使用 JavaScript 将 onclick 属性添加到输入)有效。我使用的是 Firefox 10.0.1。我的代码如下。您将看到几行注释,每行的末尾都描述了发生了什么(或者没有发生什么)。

这里的正确语法是什么? 我要疯了!

<html>
<body>
<canvas id="myCanvas" width="300" height="150"/>
<script language="JavaScript">
var elem = document.getElementById('myCanvas');
// elem.onClick = alert("hello world");  - displays alert without clicking
// elem.onClick = alert('hello world');  - displays alert without clicking
// elem.onClick = "alert('hello world!')";  - does nothing, even with clicking
// elem.onClick = function() { alert('hello world!'); };  - does nothing
// elem.onClick = function() { alert("hello world!"); };  - does nothing
var context = elem.getContext('2d');
context.fillStyle = '#05EFFF';
context.fillRect(0, 0, 150, 100);
</script>


</body>

308974 次浏览

当您绘制到 canvas元素时,您只是在 立即模式中绘制一个位图。

所绘制的 元素(形状、线条、图像)除了它们使用的像素和它们的颜色之外没有其他表示形式。

因此,要获得 canvas 元素(形状)上的 点击事件,您需要捕获 canvas HTML 元素上的 click 事件,并使用一些数学方法来确定单击了哪个元素,前提是您存储元素的宽度/高度和 x/y 偏移量。

要向 canvas元素添加 click事件,请使用..。

canvas.addEventListener('click', function() { }, false);

为了确定点击了哪个 元素..。

var elem = document.getElementById('myCanvas'),
elemLeft = elem.offsetLeft + elem.clientLeft,
elemTop = elem.offsetTop + elem.clientTop,
context = elem.getContext('2d'),
elements = [];


// Add event listener for `click` events.
elem.addEventListener('click', function(event) {
var x = event.pageX - elemLeft,
y = event.pageY - elemTop;


// Collision detection between clicked offset and element.
elements.forEach(function(element) {
if (y > element.top && y < element.top + element.height
&& x > element.left && x < element.left + element.width) {
alert('clicked an element');
}
});


}, false);


// Add element.
elements.push({
colour: '#05EFFF',
width: 150,
height: 100,
top: 20,
left: 15
});


// Render elements.
elements.forEach(function(element) {
context.fillStyle = element.colour;
context.fillRect(element.left, element.top, element.width, element.height);
});​

JsFiddle .

这段代码将一个 click事件附加到 canvas元素,然后将一个形状(在我的代码中称为 element)推送到一个 elements数组。你可以在这里想加多少就加多少。

创建对象数组的目的是为了以后可以查询它们的属性。在所有元素都被推送到数组之后,我们循环遍历并根据它们的属性呈现每个元素。

当触发 click事件时,代码循环遍历这些元素并确定单击是否位于 elements数组中的任何元素上。如果是这样的话,它会触发一个 alert(),可以很容易地修改它来执行一些操作,比如删除数组项,在这种情况下,您需要一个单独的 呈现函数来更新 canvas


为了完整起见,为什么你的尝试没有成功..。

elem.onClick = alert("hello world"); // displays alert without clicking

这是将 alert()的返回值赋给 elemonClick属性。它立即调用 alert()

elem.onClick = alert('hello world');  // displays alert without clicking

在 JavaScript 中,'"在语义上是相同的,lexer 可能使用 ['"]作为引号。

elem.onClick = "alert('hello world!')"; // does nothing, even with clicking

您正在为 elemonClick属性分配一个字符串。

elem.onClick = function() { alert('hello world!'); }; // does nothing

JavaScript 是区分大小写的。onclick属性是附加事件处理程序的过时方法。它只允许在属性中附加一个事件,并且在序列化 HTML 时事件可能会丢失。

elem.onClick = function() { alert("hello world!"); }; // does nothing

再来,' === "

作为对 Alex 回答的替代:

您可以使用 SVG 绘图代替 Canvas 绘图。在那里,您可以直接向所绘制的 DOM 对象添加事件。

例如:

通过 onclick 使 svg 图像对象可点击,避免绝对定位

您还可以将 DOM 元素(如 div)放在画布的顶部,这将表示您的画布元素并以相同的方式定位。

现在可以将事件侦听器附加到这些 div 并运行必要的操作。

亚历克斯答案 是相当整洁,但是当使用上下文旋转它可能很难跟踪 x,y 坐标,所以我已经做了一个 演示显示如何保持跟踪。

基本上,我正在使用这个函数,并给它的角度和距离前,在该天使绘制对象。

function rotCor(angle, length){
var cos = Math.cos(angle);
var sin = Math.sin(angle);


var newx = length*cos;
var newy = length*sin;


return {
x : newx,
y : newy
};
}

我推荐以下文章: HTML5画布的命中区域检测以及如何监听画布形状上的单击事件,它经历了各种情况。

但是,它没有涵盖 addHitRegion API,而 addHitRegion API 必须是最好的方法(使用数学函数和/或比较非常容易出错)。这种方法是详细的 开发者 Mozilla

作为静态画布上的另一种廉价替代方案,使用带有 usemap 定义的重叠 img 元素既快又脏。特别适用于基于多边形的画布元素,如饼图。

可能很晚的答案,但我只是在准备我的 70-480考试的时候读这个,并发现这个工作-

var elem = document.getElementById('myCanvas');
elem.onclick = function() { alert("hello world"); }

注意事件是 onclick而不是 onClick

JS Bin 示例。

2021年:

要在 HTML5画布中创建可跟踪的元素,应该使用 新路径2D ()方法。

首先在画布上听取鼠标事件(onclickondblclickoncontextmenuonmousemove等)以获得点(鼠标)坐标 event.offsetXevent.offsetY,然后使用 CanvasRenderingContext2D.isPointInPath()CanvasRenderingContext2D.isPointInStroke()精确检查鼠标是否在该事件中悬停你的元素。

IsPointInPath:

const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');


// Create circle
const circle = new Path2D();
circle.arc(150, 75, 50, 0, 2 * Math.PI);
ctx.fillStyle = 'red';
ctx.fill(circle);


// Listen for mouse moves
canvas.addEventListener('mousemove', function(event) {
// Check whether point is inside circle
if (ctx.isPointInPath(circle, event.offsetX, event.offsetY)) {
ctx.fillStyle = 'green';
}
else {
ctx.fillStyle = 'red';
}


// Draw circle
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fill(circle);
});
<canvas id="canvas"></canvas>

IsPointInStroke:

const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');


// Create ellipse
const ellipse = new Path2D();
ellipse.ellipse(150, 75, 40, 60, Math.PI * .25, 0, 2 * Math.PI);
ctx.lineWidth = 25;
ctx.strokeStyle = 'red';
ctx.fill(ellipse);
ctx.stroke(ellipse);


// Listen for mouse moves
canvas.addEventListener('mousemove', function(event) {
// Check whether point is inside ellipse's stroke
if (ctx.isPointInStroke(ellipse, event.offsetX, event.offsetY)) {
ctx.strokeStyle = 'green';
}
else {
ctx.strokeStyle = 'red';
}


// Draw ellipse
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fill(ellipse);
ctx.stroke(ellipse);
});
<canvas id="canvas"></canvas>

Example with multiple elements:

const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');


const circle = new Path2D();
circle.arc(50, 75, 50, 0, 2 * Math.PI);
ctx.fillStyle = 'red';
ctx.fill(circle);


const circletwo = new Path2D();
circletwo.arc(200, 75, 50, 0, 2 * Math.PI);
ctx.fillStyle = 'red';
ctx.fill(circletwo);


// Listen for mouse moves
canvas.addEventListener('mousemove', function(event) {
// Check whether point is inside circle
if (ctx.isPointInPath(circle, event.offsetX, event.offsetY)) {
ctx.fillStyle = 'green';
ctx.fill(circle);
}
else {
ctx.fillStyle = 'red';
ctx.fill(circle);
}
  

if (ctx.isPointInPath(circletwo, event.offsetX, event.offsetY)) {
ctx.fillStyle = 'blue';
ctx.fill(circletwo);
}
else {
ctx.fillStyle = 'red';
ctx.fill(circletwo);
}
  

});
html {cursor: crosshair;}
<canvas id="canvas"></canvas>

如果您有一个要检查的动态元素列表,您可以在一个循环中检查它们,如下所示:

const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
var elementslist = []


const circle = new Path2D();
circle.arc(50, 75, 30, 0, 2 * Math.PI);
ctx.fillStyle = 'red';
ctx.fill(circle);


const circletwo = new Path2D();
circletwo.arc(150, 75, 30, 0, 2 * Math.PI);
ctx.fillStyle = 'red';
ctx.fill(circletwo);


const circlethree = new Path2D();
circlethree.arc(250, 75, 30, 0, 2 * Math.PI);
ctx.fillStyle = 'red';
ctx.fill(circlethree);


elementslist.push(circle,circletwo,circlethree)


document.getElementById("canvas").addEventListener('mousemove', function(event) {
event = event || window.event;
var ctx = document.getElementById("canvas").getContext("2d")


for (var i = elementslist.length - 1; i >= 0; i--){


if (elementslist[i] && ctx.isPointInPath(elementslist[i], event.offsetX, event.offsetY)) {
document.getElementById("canvas").style.cursor = 'pointer';
ctx.fillStyle = 'orange';
ctx.fill(elementslist[i]);
return
} else {
document.getElementById("canvas").style.cursor = 'default';
ctx.fillStyle = 'red';
for (var d = elementslist.length - 1; d >= 0; d--){
ctx.fill(elementslist[d]);
}
}
}


});
<canvas id="canvas"></canvas>


Sources: