我是一位经验丰富的 Java 程序员,但在大约十年来第一次看到一些 JavaScript/HTML5 的东西。我完全不知道什么应该是最简单的事情。
作为一个例子,我只是想画一些东西并向它添加一个事件处理程序。我确定我在做一些愚蠢的事情,但我已经搜索了所有内容,但没有任何建议(例如这个问题的答案:Add onclick property to input with JavaScript)有效。我正在使用 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>
onclick
而不是 onClick
<script>
中调用 alert()
,而不是定义将调用 alert()
的函数。由于 onclick
的大写,其余的不做任何事情。
当您绘制到 canvas
元素时,您只是在 immediate mode 中绘制位图。
绘制的元素(形状、线条、图像)除了它们使用的像素和颜色之外没有任何表示。
因此,要在 canvas
元素(形状)上获取 click 事件,您需要捕获 canvas
HTML 元素上的点击事件并使用一些数学来确定单击哪个元素,前提是您要存储元素的宽度/高度和 x/y 偏移量。
要将 click
事件添加到您的 canvas
元素,请使用...
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);
});
此代码将 click
事件附加到 canvas
元素,然后将一个形状(在我的代码中称为 element
)推送到 elements
数组。您可以在此处添加任意数量的内容。
创建对象数组的目的是让我们稍后可以查询它们的属性。在所有元素都被推入数组之后,我们循环并根据它们的属性渲染每个元素。
触发 click
事件时,代码会循环遍历元素并确定点击是否在 elements
数组中的任何元素上。如果是这样,它会触发一个 alert()
,可以轻松地对其进行修改以执行诸如删除数组项之类的操作,在这种情况下,您需要一个单独的 render 函数来更新 canvas
。
为了完整起见,为什么你的尝试没有奏效......
elem.onClick = alert("hello world"); // displays alert without clicking
这是将 alert()
的返回值分配给 elem
的 onClick
属性。它立即调用 alert()
。
elem.onClick = alert('hello world'); // displays alert without clicking
在 JavaScript 中,'
和 "
在语义上是相同的,词法分析器可能使用 ['"]
作为引号。
elem.onClick = "alert('hello world!')"; // does nothing, even with clicking
您正在为 elem
的 onClick
属性分配一个字符串。
elem.onClick = function() { alert('hello world!'); }; // does nothing
JavaScript 区分大小写。 onclick
属性是附加事件处理程序的古老方法。它只允许将一个事件附加到属性中,并且在序列化 HTML 时该事件可能会丢失。
elem.onClick = function() { alert("hello world!"); }; // does nothing
再次,' === "
。
2021年:
要在 HTML5 画布中创建可跟踪元素,您应该使用 new Path2D() 方法。
首先在画布上监听鼠标事件(onclick
或 ondblclick
或 oncontextmenu
或 onmousemove
等)以获取点(鼠标)坐标 event.offsetX
和 event.offsetY
,然后使用 CanvasRenderingContext2D.isPointInPath()
或 { 8}精确检查鼠标是否在该事件中悬停您的元素。
IsPointInPath:
const canvas = document.getElementById('canvas'); const ctx = canvas.getContext('2d'); // 创建圆 const circle = new Path2D(); circle.arc(150, 75, 50, 0, 2 * Math.PI); ctx.fillStyle = '红色'; ctx.fill(圆圈); // 监听鼠标移动 canvas.addEventListener('mousemove', function(event) { // 检查点是否在圆内 if (ctx.isPointInPath(circle, event.offsetX, event.offsetY)) { ctx.fillStyle = ' green'; } else { ctx.fillStyle = 'red'; } // 画圆 ctx.clearRect(0, 0, canvas.width, canvas.height); ctx.fill(circle); });
IsPointInStroke:
const canvas = document.getElementById('canvas'); const ctx = canvas.getContext('2d'); // 创建椭圆 const ellipse = new Path2D(); ellipse.ellipse(150, 75, 40, 60, Math.PI * .25, 0, 2 * Math.PI); ctx.lineWidth = 25; ctx.strokeStyle = '红色'; ctx.fill(椭圆); ctx.stroke(椭圆); // 监听鼠标移动 canvas.addEventListener('mousemove', function(event) { // 检查点是否在椭圆的笔划内 if (ctx.isPointInStroke(ellipse, event.offsetX, event.offsetY)) { ctx.strokeStyle = 'green'; } else { ctx.strokeStyle = 'red'; } // 绘制椭圆 ctx.clearRect(0, 0, canvas.width, canvas.height); ctx.fill(ellipse); ctx.stroke(ellipse) ; });
具有多个元素的示例:
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 = '红色'; ctx.fill(圆圈); const circletwo = new Path2D(); circletwo.arc(200, 75, 50, 0, 2 * Math.PI); ctx.fillStyle = '红色'; ctx.fill(circletwo); // 监听鼠标移动 canvas.addEventListener('mousemove', function(event) { // 检查点是否在圆内 if (ctx.isPointInPath(circle, event.offsetX, event.offsetY)) { ctx.fillStyle = '绿色'; ctx.fill(circle); } else { ctx.fillStyle = 'red'; ctx.fill(circle); } if (ctx.isPointInPath(circletwo, event.offsetX, event.offsetY)) { ctx.fillStyle = '蓝色'; ctx.fill(circletwo); } else { ctx.fillStyle = 'red'; ctx.fill(circletwo); } }); html {光标:十字准线;}
如果您有要检查的动态元素列表,则可以循环检查它们,如下所示:
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 = '红色'; ctx.fill(圆圈); const circletwo = new Path2D(); circletwo.arc(150, 75, 30, 0, 2 * Math.PI); ctx.fillStyle = '红色'; ctx.fill(circletwo); const circlethree = new Path2D(); circlethree.arc(250, 75, 30, 0, 2 * Math.PI); ctx.fillStyle = '红色'; 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]); } } } });
资料来源:
https://developer.mozilla.org/en-US/docs/Web/API/Path2D/Path2D
https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/isPointInPath
https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/isPointInStroke
答案可能很晚,但我只是在准备 70-480
考试时读到了这篇文章,发现这行得通 -
var elem = document.getElementById('myCanvas');
elem.onclick = function() { alert("hello world"); }
注意事件是 onclick
而不是 onClick
。
JS Bin 示例。
onclick
添加到画布内的元素,而不是画布本身
作为亚历克斯答案的替代方案:
您可以使用 SVG 绘图而不是 Canvas 绘图。在那里,您可以直接将事件添加到绘制的 DOM 对象。
参见例如:
Making an svg image object clickable with onclick, avoiding absolute positioning
我推荐以下文章:Hit Region Detection For HTML5 Canvas And How To Listen To Click Events On Canvas Shapes,它经历了各种情况。
但是,它不包括 addHitRegion
API,这一定是最好的方法(使用数学函数和/或比较很容易出错)。这种方法很详细on developer.mozilla
addHitRegion
] 已过时。虽然它可能在某些浏览器中仍然有效,但不鼓励使用它,因为它可能随时被删除。尽量避免使用它。” - 从您包含的 dev.moz 链接中,点击即可保存其他人。
您还可以将 DOM 元素(例如 div
)放在画布顶部,以表示您的画布元素并以相同的方式定位。
现在您可以将事件侦听器附加到这些 div 并运行必要的操作。
作为在有些静态画布上的另一种廉价替代方案,使用带有 usemap 定义的覆盖 img 元素既快速又脏。特别适用于基于多边形的画布元素,如饼图。
Alex Answer 非常简洁,但是当使用上下文旋转时,很难跟踪 x,y 坐标,因此我制作了一个 Demo 来展示如何跟踪它。
基本上我正在使用这个函数并在绘制对象之前给它一个角度和在那个天使中经过的距离。
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
};
}
不定期副业成功案例分享