Como adiciono um manipulador de events onClick simples a um elemento de canvas?

Sou um programador Java experiente, mas estou vendo algumas coisas de JavaScript / HTML5 pela primeira vez em cerca de uma década. Estou completamente perplexo com o que deveria ser a coisa mais simples de todas.

Por exemplo, eu só queria desenhar algo e adicionar um manipulador de events a ele. Tenho certeza de que estou fazendo algo estúpido, mas eu procurei por toda parte e nada que é sugerido (por exemplo, a resposta a esta pergunta: Adicionar propriedade onclick para input com JavaScript ) funciona. Estou usando o Firefox 10.0.1. Meu código segue. Você verá várias linhas comentadas e no final de cada uma delas está uma descrição do que (ou não) acontece.

Qual é a syntax correta aqui? Eu estou ficando louco!

    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);   

Quando você desenha em um elemento de canvas , simplesmente desenha um bitmap no modo imediato .

Os elementos (formas, linhas, imagens) que são desenhados não têm representação além dos pixels que eles usam e sua cor.

Portanto, para obter um evento de clique em um elemento de canvas (forma), é necessário capturar events de clique no elemento HTML da canvas e usar alguma matemática para determinar qual elemento foi clicado, desde que você esteja armazenando os elementos width / height e x / y offset.

Para adicionar um evento de click ao seu elemento de canvas , use …

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

Para determinar qual elemento foi clicado …

 var elem = document.getElementById('myCanvas'), elemLeft = elem.offsetLeft, elemTop = elem.offsetTop, 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 .

Esse código anexa um evento click ao elemento canvas e, em seguida, envia uma forma (chamada de element em meu código) para uma matriz de elements . Você pode adicionar quantos quiser aqui.

O objective de criar uma matriz de objects é para que possamos consultar suas propriedades posteriormente. Depois que todos os elementos foram colocados no array, nós passamos e renderizamos cada um com base em suas propriedades.

Quando o evento click é acionado, o código percorre os elementos e determina se o clique foi sobre qualquer um dos elementos na matriz de elements . Em caso afirmativo, ele triggers um alert() , que poderia ser facilmente modificado para fazer algo como remover o item da matriz, caso em que você precisaria de uma function de renderização separada para atualizar a canvas .

Para completar, por que suas tentativas não funcionaram ...

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

Isso está atribuindo o valor de retorno de alert() à propriedade onClick de elem . Ele está imediatamente invocando o alert() .

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

Em JavaScript, o ' e " são semanticamente idênticos, o lexer provavelmente usa ['"] para aspas.

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

Você está atribuindo uma string à propriedade onClick do elem .

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

JavaScript é sensível a maiúsculas e minúsculas. A propriedade onclick é o método arcaico de append manipuladores de events. Permite apenas que um evento seja anexado à propriedade e o evento pode ser perdido ao serializar o HTML.

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

Mais uma vez, ' === " .

Eu recomendo o seguinte artigo: Detecção de região de impacto para canvas HTML5 e como ouvir clique em events em formas de canvas que passa por várias situações.

No entanto, ele não cobre a API addHitRegion , que deve ser a melhor maneira (usar funções matemáticas e / ou comparações é bastante propenso a erros). Esta abordagem é detalhada em developer.mozilla

Como alternativa à resposta de alex:

Você poderia usar um desenho SVG em vez de um desenho em canvas. Lá você pode adicionar events diretamente aos objects DOM desenhados.

veja por exemplo:

Tornando um object de imagem svg clicável com onclick, evitando posicionamento absoluto

Você também pode colocar elementos DOM, como div na parte superior da canvas, que representariam seus elementos da canvas e serão posicionados da mesma maneira.

Agora você pode append ouvintes de events a esses divs e executar as ações necessárias.

Como outra alternativa barata em canvass um pouco estáticas, usar um elemento img de sobreposição com uma definição de mapa de uso é rápido e sujo. Funciona especialmente bem em elementos de canvas baseados em polígonos, como um gráfico de pizza.

Provavelmente muito tarde para a resposta, mas eu acabei de ler isso enquanto me preparava para o meu exame 70-480 , e descobri que isso funcionava –

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

Observe o evento como onclick vez de onClick .

Exemplo de JS Bin .

Alex Answer é bem legal, mas ao usar a rotação de contexto pode ser difícil traçar as coordenadas x, y, então eu fiz uma Demo mostrando como controlar isso.

Basicamente, estou usando esta function e dando-lhe o ângulo e a quantidade de distância percorrida naquele anjo antes de desenhar o object.

 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 }; }