Jak dodać prostą procedurę obsługi zdarzeń onClick do elementu Canvas?

128

Jestem doświadczonym programistą Java, ale po raz pierwszy od dekady przyglądam się JavaScript / HTML5. Jestem kompletnie zaskoczony tym, co powinno być najprostszą rzeczą na świecie.

Jako przykład chciałem tylko narysować coś i dodać do tego procedurę obsługi zdarzeń. Jestem pewien, że robię coś głupiego, ale przeszukałem wszystko i nic, co jest sugerowane (np. Odpowiedź na to pytanie: Dodaj właściwość onclick do wprowadzania w JavaScript ) nie działa. Używam przeglądarki Firefox 10.0.1. Mój kod następuje. Zobaczysz kilka skomentowanych linii, a na końcu każdego z nich jest opis tego, co (lub co się nie dzieje).

Jaka jest poprawna składnia tutaj? Wariuję!

<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
źródło
8
Użyj onclickzamiastonClick
noob
1
Aby wyjaśnić, dlaczego te próby nie zadziałały ... Pierwsze kilka komentarzy wyświetla alert natychmiast, ponieważ dzwonisz alert()bezpośrednio <script>, zamiast definiować funkcję, która wywoła alert(). Reszta nic nie robi z powodu kapitalizacji onclick.
LarsH
Możesz użyć tej biblioteki jsfiddle.net/user/zlatnaspirala/fiddles , zajrzyj na bitbucket.org/nikola_l/visual-js . Dostaniesz wiele funkcji +
Nikola Lukic

Odpowiedzi:

223

Kiedy rysujesz do canvaselementu, po prostu rysujesz bitmapę w trybie natychmiastowym .

Te elementy (kształty, linie, obrazy), które są wystawione nie mają reprezentacji oprócz pikseli, których używają, a ich kolor.

Dlatego, aby uzyskać zdarzenie kliknięcia canvas elementu (kształtu), musisz przechwycić zdarzenia kliknięcia canvaselementu HTML i użyć matematyki, aby określić, który element został kliknięty, pod warunkiem, że przechowujesz szerokość / wysokość i przesunięcie x / y elementów .

Aby dodać clickzdarzenie do swojego canvaselementu, użyj ...

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

Aby określić, który element został kliknięty ...

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 .

Ten kod dołącza clickzdarzenie do canvaselementu, a następnie wypycha jeden kształt (nazywany elementw moim kodzie) do elementstablicy. Możesz tutaj dodać tyle, ile chcesz.

Celem tworzenia tablicy obiektów jest umożliwienie nam późniejszego zbadania ich właściwości. Po wypchnięciu wszystkich elementów do tablicy wykonujemy pętlę i renderujemy każdy z nich na podstawie ich właściwości.

Po clickwyzwoleniu zdarzenia kod przechodzi przez elementy w pętli i określa, czy kliknięcie dotyczy któregokolwiek z elementów elementstablicy. Jeśli tak, uruchamia plik alert(), który można łatwo zmodyfikować, aby zrobić coś takiego, jak usunięcie elementu tablicy, w takim przypadku potrzebna byłaby oddzielna funkcja renderująca, aby zaktualizować canvas.


Dla kompletności, dlaczego twoje próby nie zadziałały ...

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

To jest przypisanie wartości zwracanej alert()do onClickwłaściwości elem. Natychmiast wywołuje alert().

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

W JavaScript, 'i "są semantycznie identyczne, lekser prawdopodobnie używa ['"]cudzysłowów.

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

Przypisujesz ciąg do onClickwłaściwości elem.

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

JavaScript rozróżnia wielkość liter. onclickNieruchomość jest archaiczna metoda mocowania obsługi zdarzeń. Umożliwia dołączenie tylko jednego zdarzenia do właściwości, a zdarzenie może zostać utracone podczas serializacji kodu HTML.

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

Ponownie ' === ".

Alex
źródło
Hmm ... wybacz mi, jeśli nie rozumiem - ale czy nie rejestruję kliknięć w elemencie Canvas? Nie jestem pewien co masz na myśli przez co element został kliknięty. Na tej stronie jest tylko jeden element, prawda?
Jer
Miał na myśli „który element płótna” - czyli, że tak powiem, „kształt”.
Jovan Perovic
1
Bardzo dziękuję - chociaż nie mam pewności co do ostatniego. Podążam za przykładem: developer.mozilla.org/en/DOM/element.onclick (używając funkcji anonimowej, którą opisują) i działa, nawet gdy zmieniam zakres na płótno. Więc nie będzie mi trudno powoli przekształcić ich przykład w mój, aby zobaczyć, co robię źle. Dziękuję za szczegółową odpowiedź! Szczególnie pomocne było wyjaśnienie, dlaczego moje różne próby były błędne.
Jer
1
@Jer Nie martw się, jeśli masz więcej pytań, napisz do mnie na Twitterze, @alexdickson .
Alex
1
@HashRocketSyntax Powinno działać dobrze, po prostu zaktualizuj pozycje obiektów w pamięci i użyj tych definicji, aby renderować od
alex
4

Prawdopodobnie bardzo późno na odpowiedź, ale właśnie przeczytałem to przygotowując się do 70-480egzaminu i okazało się, że działa -

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

Zwróć uwagę na wydarzenie jako onclickzamiast onClick.

Przykład JS Bin .

Soham Dasgupta
źródło
3

Polecam następujący artykuł: Wykrywanie obszarów trafień dla płótna HTML5 i jak słuchać zdarzeń kliknięcia na kształtach płótna, który przechodzi przez różne sytuacje.

Nie obejmuje to jednak addHitRegionAPI, które musi być najlepszym sposobem (używanie funkcji matematycznych i / lub porównań jest dość podatne na błędy). To podejście jest szczegółowo opisane w witrynie developer.mozilla

RUser4512
źródło
„[ addHitRegion] jest przestarzały. Chociaż może nadal działać w niektórych przeglądarkach, odradza się jego używanie, ponieważ można go usunąć w dowolnym momencie. Staraj się go unikać”. - z linku dev.moz, który dołączasz, aby zaoszczędzić innym kliknięcie.
Chris
2

Jako alternatywa dla odpowiedzi Alexa:

Zamiast rysunku na płótnie można użyć rysunku SVG. Tam możesz dodawać zdarzenia bezpośrednio do narysowanych obiektów DOM.

zobacz na przykład:

Uczynienie obiektu obrazu SVG klikalnym za pomocą onclick, unikając pozycjonowania bezwzględnego

Goswin
źródło
1
przepraszam za entuzjazm, po prostu miałem problem, dla którego użycie SVG jest oczywistym rozwiązaniem i nie pomyślałem o tym do twojego komentarza ^^
Elias Dorneles
1

Możesz także umieścić elementy DOM, divna przykład na płótnie, które będą reprezentować elementy twojego płótna i będą ustawione w ten sam sposób.

Teraz możesz dołączyć detektory zdarzeń do tych elementów div i uruchomić niezbędne działania.

Siergiej Baszarow
źródło
1

Jako kolejna tania alternatywa na nieco statycznym płótnie, użycie nakładającego się elementu img z definicją usemap jest szybkie i brudne. Działa szczególnie dobrze w przypadku elementów kanwy opartych na wielokątach, takich jak wykres kołowy.

TadLewis
źródło
0

Alex Answer jest całkiem fajny, ale podczas używania obracania kontekstu może być trudno prześledzić współrzędne x, y, więc przygotowałem Demo pokazujące, jak to śledzić.

Zasadniczo używam tej funkcji i podam jej kąt i odległość pokonaną przez tego anioła przed rysowaniem obiektu.

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
źródło