Wie füge ich einem Canvas-Element einen einfachen onClick-Ereignishandler hinzu?

128

Ich bin ein erfahrener Java-Programmierer, schaue mir aber zum ersten Mal seit etwa einem Jahrzehnt einige JavaScript / HTML5-Inhalte an. Ich bin völlig ratlos darüber, was das Einfachste überhaupt sein sollte.

Als Beispiel wollte ich nur etwas zeichnen und einen Ereignishandler hinzufügen. Ich bin mir sicher, dass ich etwas Dummes mache, aber ich habe überall gesucht und nichts, was vorgeschlagen wird (z. B. die Antwort auf diese Frage: Onclick-Eigenschaft zur Eingabe mit JavaScript hinzufügen ), funktioniert. Ich benutze Firefox 10.0.1. Mein Code folgt. Sie sehen mehrere kommentierte Zeilen und am Ende jeder Zeile finden Sie eine Beschreibung dessen, was passiert (oder was nicht).

Was ist hier die richtige Syntax? Ich werde verrückt!

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

Jer
quelle
8
Verwenden Sie onclickanstelle vononClick
noob
1
Um herauszufinden, warum diese Versuche nicht funktioniert haben ... In den ersten Kommentaren wird sofort eine Warnung angezeigt, da Sie alert()direkt anrufen <script>, anstatt eine Funktion zu definieren, die aufgerufen wird alert(). Der Rest tut nichts wegen der Großschreibung von onclick.
LarsH
Sie können diese lib jsfiddle.net/user/zlatnaspirala/fiddles verwenden , siehe bitbucket.org/nikola_l/visual-js . Sie erhalten viele Funktionen +
Nikola Lukic

Antworten:

223

Wenn Sie zu einem canvasElement zeichnen, zeichnen Sie einfach eine Bitmap im Sofortmodus .

Die gezeichneten Elemente (Formen, Linien, Bilder) haben außer den verwendeten Pixeln und ihrer Farbe keine Darstellung.

Um ein Klickereignis für ein canvas Element (eine Form) zu erhalten, müssen Sie daher Klickereignisse für das canvasHTML-Element erfassen und mithilfe von Mathematik bestimmen, auf welches Element geklickt wurde, vorausgesetzt, Sie speichern die Breite / Höhe und den x / y-Versatz der Elemente .

Verwenden Sie ..., clickum Ihrem canvasElement ein Ereignis hinzuzufügen ...

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

Um festzustellen, auf welches Element geklickt wurde ...

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 .

Dieser Code hängt ein clickEreignis an das canvasElement an und überträgt dann eine Form ( elementin meinem Code als "Form" bezeichnet ) an ein elementsArray. Sie können hier so viele hinzufügen, wie Sie möchten.

Der Zweck des Erstellens eines Arrays von Objekten besteht darin, dass wir ihre Eigenschaften später abfragen können. Nachdem alle Elemente auf das Array verschoben wurden, durchlaufen wir jedes Element und rendern es basierend auf seinen Eigenschaften.

Wenn das clickEreignis ausgelöst wird, durchläuft der Code die Elemente und bestimmt, ob der Klick über einem der Elemente im elementsArray erfolgte. Wenn ja, feuert sie eine alert(), die leicht modifiziert werden könnte etwas wie entfernen Sie das Array - Element zu tun, in dem Fall , dass Sie eine separate bräuchten machen Funktion , um das Update canvas.


Der Vollständigkeit halber, warum Ihre Versuche nicht funktioniert haben ...

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

Dies weist alert()der onClickEigenschaft von den Rückgabewert von zu elem. Es ruft sofort das auf alert().

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

In JavaScript sind die 'und "semantisch identisch, die der Lexer wahrscheinlich ['"]für Anführungszeichen verwendet.

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

Sie weisen der onClickEigenschaft von eine Zeichenfolge zu elem.

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

JavaScript unterscheidet zwischen Groß- und Kleinschreibung. Die onclickEigenschaft ist die archaische Methode zum Anhängen von Ereignishandlern. Es kann nur ein Ereignis an die Eigenschaft angehängt werden, und das Ereignis kann beim Serialisieren des HTML-Codes verloren gehen.

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

Wieder ' === ".

Alex
quelle
Hmm ... vergib mir, wenn ich ein Missverständnis habe - aber erfasse ich keine Klickereignisse auf dem Canvas-Element? Ich bin mir nicht sicher, was Sie damit meinen, auf welches Element geklickt wurde. Es gibt nur ein Element auf dieser Seite, richtig?
Jer
Er meinte "welches Element innerhalb der Leinwand" - das ist sozusagen "Form".
Jovan Perovic
1
Vielen Dank - obwohl ich beim letzten nicht ganz klar bin. Ich folge hier dem Beispiel: developer.mozilla.org/en/DOM/element.onclick (unter Verwendung der von ihnen beschriebenen anonymen Funktion) und es funktioniert, selbst wenn ich die Spanne in eine Zeichenfläche ändere. Es wird mir also nicht schwer fallen, ihr Beispiel langsam in mein Beispiel umzuwandeln, um zu sehen, was ich falsch mache. Vielen Dank für die ausführliche Antwort! Besonders hilfreich war die Erklärung, warum meine verschiedenen Versuche falsch waren.
Jer
1
@Jer Keine Sorge, wenn Sie weitere Fragen haben, können Sie mich gerne auf Twitter posten und anpingen , @alexdickson .
Alex
1
@HashRocketSyntax Es sollte funktionieren, aktualisieren Sie einfach die Positionen der Objekte im Speicher und diese Definitionen verwenden , um zu rendern , von
alex
4

Wahrscheinlich sehr spät zur Antwort, aber ich habe dies gerade gelesen, als ich mich auf meine 70-480Prüfung vorbereitete , und fand, dass dies funktioniert -

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

Beachten Sie das Ereignis als onclickstatt onClick.

JS Bin Beispiel.

Soham Dasgupta
quelle
3

Ich empfehle den folgenden Artikel: Trefferbereichserkennung für HTML5-Canvas und Anhören von Klickereignissen auf Canvas-Formen, die verschiedene Situationen durchlaufen.

Es wird jedoch nicht die addHitRegionAPI behandelt, was der beste Weg sein muss (die Verwendung von mathematischen Funktionen und / oder Vergleichen ist ziemlich fehleranfällig). Dieser Ansatz wird auf developer.mozilla detailliert beschrieben

RUser4512
quelle
"[ addHitRegion] ist veraltet. Obwohl es in einigen Browsern möglicherweise noch funktioniert, wird von seiner Verwendung abgeraten, da es jederzeit entfernt werden kann. Versuchen Sie, die Verwendung zu vermeiden." - Über den Link dev.moz, den Sie einfügen, um anderen einen Klick zu ersparen.
Chris
2

Als Alternative zu Alex 'Antwort:

Sie können eine SVG-Zeichnung anstelle einer Leinwandzeichnung verwenden. Dort können Sie Ereignisse direkt zu den gezeichneten DOM-Objekten hinzufügen.

siehe zum Beispiel:

Klicken Sie mit einem Klick auf ein SVG-Bildobjekt, um eine absolute Positionierung zu vermeiden

Goswin
quelle
1
Entschuldigung für die Begeisterung, ich hatte nur ein Problem, für das die Verwendung von SVG die offensichtliche Lösung ist, und ich hatte bis zu Ihrem Kommentar nicht daran gedacht ^^
Elias Dorneles
1

Sie können auch DOM-Elemente wie divoben auf der Leinwand platzieren, die Ihre Leinwandelemente darstellen und auf die gleiche Weise positioniert werden.

Jetzt können Sie Ereignis-Listener an diese Divs anhängen und die erforderlichen Aktionen ausführen.

Sergei Basharov
quelle
1

Als weitere billige Alternative auf etwas statischer Leinwand ist die Verwendung eines überlagerten img-Elements mit einer Usemap-Definition schnell und schmutzig. Funktioniert besonders gut auf polygonbasierten Canvas-Elementen wie einem Kreisdiagramm.

TadLewis
quelle
0

Alex Answer ist ziemlich ordentlich, aber bei Verwendung von Kontextdrehung kann es schwierig sein, x- und y-Koordinaten zu verfolgen. Deshalb habe ich eine Demo erstellt, die zeigt, wie man das verfolgt.

Grundsätzlich benutze ich diese Funktion und gebe ihr den Winkel und die zurückgelegte Entfernung in diesem Engel, bevor ich ein Objekt zeichne.

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
    };
}
Imran Bughio
quelle