¿Cómo agrego un controlador de eventos onClick simple a un elemento de lienzo?
Soy un programador Java experimentado, pero estoy viendo algunas cosas de JavaScript/HTML5 por primera vez en aproximadamente una década. Estoy completamente perplejo sobre lo que debería ser lo más simple que existe.
Como ejemplo, solo quería dibujar algo y agregarle un controlador de eventos. Estoy seguro de que estoy haciendo algo estúpido, pero he buscado por todas partes y nada de lo sugerido (por ejemplo, la respuesta a esta pregunta: Agregar propiedad onclick para ingresar con JavaScript ) funciona. Estoy usando Firefox 10.0.1. Mi código sigue. Verás varias líneas comentadas y al final de cada una hay una descripción de lo que (o lo que no) sucede.
¿Cuál es la sintaxis correcta aquí? ¡Me estoy volviendo loco!
<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>
Cuando dibujas en un canvas
elemento, simplemente estás dibujando un mapa de bits en modo inmediato .
Los elementos (formas, líneas, imágenes) que se dibujan no tienen representación más allá de los píxeles que utilizan y su color.
Por lo tanto, para obtener un evento de clic en un canvas
elemento (forma), necesita capturar eventos de clic en el canvas
elemento HTML y usar algunas matemáticas para determinar en qué elemento se hizo clic, siempre que esté almacenando el ancho/alto y el desplazamiento x/y de los elementos. .
Para agregar un click
evento a su canvas
elemento, use...
canvas.addEventListener('click', function() { }, false);
Para determinar en qué elemento se hizo clic...
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 .
Este código adjunta un click
evento al canvas
elemento y luego inserta una forma (llamada en element
mi código) a una elements
matriz. Puedes agregar tantos como desees aquí.
El propósito de crear una matriz de objetos es que podamos consultar sus propiedades más adelante. Después de que todos los elementos se hayan insertado en la matriz, recorremos y representamos cada uno según sus propiedades.
Cuando click
se activa el evento, el código recorre los elementos y determina si el clic se realizó sobre alguno de los elementos de la elements
matriz. Si es así, activa un archivo alert()
, que podría modificarse fácilmente para hacer algo como eliminar el elemento de la matriz, en cuyo caso necesitaría una función de procesamiento separada para actualizar el archivo canvas
.
Para completar, por qué sus intentos no funcionaron...
elem.onClick = alert("hello world"); // displays alert without clicking
Esto es asignar el valor de retorno de alert()
a la onClick
propiedad de elem
. Inmediatamente está invocando el alert()
.
elem.onClick = alert('hello world'); // displays alert without clicking
En JavaScript, '
y "
son semánticamente idénticos, el lexer probablemente los use ['"]
para comillas.
elem.onClick = "alert('hello world!')"; // does nothing, even with clicking
Estás asignando una cadena a la onClick
propiedad de elem
.
elem.onClick = function() { alert('hello world!'); }; // does nothing
JavaScript distingue entre mayúsculas y minúsculas. La onclick
propiedad es el método arcaico de adjuntar controladores de eventos. Solo permite adjuntar un evento a la propiedad y el evento se puede perder al serializar el HTML.
elem.onClick = function() { alert("hello world!"); }; // does nothing
De nuevo, ' === "
.
2021:
Para crear un elemento rastreable en el lienzo HTML5, debe utilizar el nuevo método Path2D() .
Después de crear sus formas con new Path2D()
un nombre para cada una de ellas, escuche los eventos táctiles o del mouse (como onclick
, ondblclick
, oncontextmenu
o onmousemove
etc.) en su lienzo para obtener las coordenadas del punto event.offsetX
y event.offsetY
luego use CanvasRenderingContext2D.isPointInPath()
o CanvasRenderingContext2D.isPointInStroke()
para verificar con precisión si el mouse es desplazar tu elemento en ese evento.
EsPuntoEnPath:
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>
EsPuntoEnTrazo:
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>
Ejemplo con múltiples elementos:
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>
Si tiene una lista de elementos dinámicos para verificar, puede verificarlos en un bucle, como este:
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>
Fuentes :
- 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
Como alternativa a la respuesta de Alex:
Podrías usar un dibujo SVG en lugar de un dibujo de Canvas. Allí puede agregar eventos directamente a los objetos DOM dibujados.
ver por ejemplo:
Hacer que se pueda hacer clic en un objeto de imagen svg con onclick, evitando el posicionamiento absoluto
Recomiendo el siguiente artículo: Detección de región de acceso para lienzo HTML5 y cómo escuchar eventos de clic en formas de lienzo, que analiza varias situaciones.
Sin embargo, no cubre la addHitRegion
API, que debe ser la mejor manera (usar funciones matemáticas y/o comparaciones es bastante propenso a errores). Este enfoque se detalla en desarrollador.mozilla