如何将简单的 onClick 事件处理程序添加到画布元素



我是一个经验丰富的Java程序员,但大约十年来第一次看到一些JavaScript/HTML5的东西。 我完全被有史以来最简单的事情难倒了。

例如,我只想绘制一些东西并向其添加一个事件处理程序。 我确定我在做一些愚蠢的事情,但是我已经搜索了所有内容,没有任何建议(例如,这个问题的答案:将onclick属性添加到使用JavaScript的输入中(都不起作用。 我使用的是火狐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>

绘制到 canvas 元素时,只需在即时模式下绘制位图。

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

因此,若要获取canvas元素(形状(上的单击事件,需要捕获 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);
});​

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 中,'" 在语义上是相同的,词法分析器可能使用 ['"] 作为引号。

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

再次,' === ".

> 2021

要在HTML5画布中创建可跟踪的元素,您应该使用新的Path2D((方法。

首先在画布上侦听触摸或鼠标事件(如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>

具有多个元素的示例:

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>

<小时 />

资料来源

  • 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

作为亚历克斯答案的替代方案:

您可以使用 SVG 绘图而不是画布绘图。在那里,您可以将事件直接添加到绘制的 DOM 对象中。

例如,请参阅:

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

我重新命令以下文章:HTML5画布的命中区域检测以及如何收听画布形状上的点击事件,这经历了各种情况。

但是,它没有涵盖addHitRegion API,这必须是最好的方法(使用数学函数和/或比较非常容易出错(。这种方法在developer.mozilla

请注意,该事件为 onclick 而不是 onClick

JS垃圾桶示例。

您还可以将 DOM 元素(如 div(放在画布上,这些元素将代表您的画布元素并以相同的方式放置。

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

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

Alex Answer非常简洁,但是当使用上下文旋转时,很难跟踪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
    };
}

相关内容

  • 没有找到相关文章

最新更新