Как добавить простой обработчик события onClick к элементу холста?

Я опытный Java-программист, но впервые за несколько десятилетий я просматриваю материал JavaScript/HTML5. Я полностью потрясен тем, что должно быть самым простым.

В качестве примера я просто хотел нарисовать что-то и добавить к нему обработчик событий. Я уверен, что делаю что-то глупое, но я искал все и ничего, что было предложено (например, ответ на этот вопрос: Добавить свойство onclick для ввода с помощью 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>

Ответ 1

Когда вы рисуете элемент canvas, вы просто рисуете растровое изображение в немедленном режиме.

Элементы (фигуры, линии, изображения), которые рисуются, не имеют представления, кроме пикселей, которые они используют, и их цвета.

Поэтому, чтобы получить событие click на элементе canvas (shape), вам нужно зафиксировать события click на элементе canvas HTML и использовать некоторую математику для определения того, какой элемент был нажат, при условии, что вы храните элементы 'width/height и x/y offset.

Чтобы добавить событие click к вашему элементу canvas, используйте...

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

Чтобы определить, какой элемент был нажат...

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.

Этот код присоединяет событие click к элементу canvas, а затем толкает одну форму (называемую element в моем коде) в массив elements. Вы можете добавить столько, сколько хотите.

Цель создания массива объектов заключается в том, что мы можем позже запросить их свойства. После того, как все элементы были перенесены на массив, мы прокручиваем и визуализируем каждый на основе их свойств.

Когда событие click запускается, код проходит через элементы и определяет, был ли клик над любым из элементов массива elements. Если это так, он запускает alert(), который можно легко изменить, чтобы сделать что-то вроде удаления элемента массива, и в этом случае вам понадобится отдельная функция рендеринга для обновления canvas.

Для полноты, почему ваши попытки не сработали...

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

Это присваивает возвращаемое значение alert() свойству onClick elem. Он немедленно вызывает alert().

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

В JavaScript символы ' и " семантически идентичны, лексер, вероятно, использует ['"] для кавычек.

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

Вы назначаете строку свойству onClick elem.

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

JavaScript чувствителен к регистру. Свойством onClick является архаичный метод привязки обработчиков событий. Он позволяет только привязать одно событие к свойству, и событие может быть потеряно при сериализации HTML.

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

Опять же, ' === ".

Ответ 2

Вы также можете поместить элементы DOM, например div поверх холста, которые будут представлять ваши элементы холста и будут расположены одинаково.

Теперь вы можете присоединить слушателей событий к этим div и выполнить необходимые действия.

Ответ 4

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