Bagaimana cara menambahkan event handler onClick sederhana ke elemen kanvas?

128

Saya seorang programmer Java yang berpengalaman tetapi saya melihat beberapa hal JavaScript / HTML5 untuk pertama kalinya dalam sekitar satu dekade. Saya benar-benar bingung tentang apa yang seharusnya menjadi hal paling sederhana yang pernah ada.

Sebagai contoh, saya hanya ingin menggambar sesuatu dan menambahkan event handler ke dalamnya. Saya yakin saya melakukan sesuatu yang bodoh, tetapi saya sudah mencari di mana-mana dan tidak ada yang disarankan (misalnya jawaban untuk pertanyaan ini: Tambahkan properti onclick untuk diinput dengan JavaScript ) berfungsi. Saya menggunakan Firefox 10.0.1. Kode saya berikut. Anda akan melihat beberapa baris yang dikomentari dan di akhir masing-masing adalah deskripsi tentang apa (atau apa yang tidak) terjadi.

Apa sintaks yang benar di sini? Saya menjadi gila!

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

Yer
sumber
8
Gunakan onclicksebagai gantionClick
noob
1
Untuk menguraikan mengapa upaya itu tidak berhasil ... Pasangan pertama komentar segera menampilkan lansiran karena Anda menelepon alert()langsung <script>, alih-alih mendefinisikan fungsi yang akan memanggil alert(). Sisanya tidak melakukan apa-apa karena kapitalisasi onclick.
LarsH
Anda dapat menggunakan ini lib jsfiddle.net/user/zlatnaspirala/fiddles , terlihat di bitbucket.org/nikola_l/visual-js . Anda akan mendapatkan banyak fitur +
Nikola Lukic

Jawaban:

223

Saat Anda menggambar suatu canvaselemen, Anda hanya menggambar bitmap dalam mode langsung .

The elemen (bentuk, garis, gambar) yang diambil tidak memiliki perwakilan selain piksel yang mereka gunakan dan warna mereka.

Oleh karena itu, untuk mendapatkan acara klik pada canvas elemen (bentuk), Anda perlu menangkap acara klik pada canvaselemen HTML dan menggunakan beberapa matematika untuk menentukan elemen mana yang diklik, asalkan Anda menyimpan lebar / tinggi elemen dan offset x / y elemen .

Untuk menambahkan clickacara ke canvaselemen Anda , gunakan ...

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

Untuk menentukan elemen mana yang diklik ...

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 .

Kode ini melampirkan clickperistiwa ke canvaselemen, dan kemudian mendorong satu bentuk (disebut elementkode saya) ke elementsarray. Anda dapat menambahkan sebanyak yang Anda inginkan di sini.

Tujuan membuat array objek adalah agar kami dapat menanyakan properti mereka nanti. Setelah semua elemen didorong ke dalam array, kita lewati dan render masing-masing berdasarkan propertinya.

Ketika clickacara dipicu, kode akan melewati elemen dan menentukan apakah klik di atas salah satu elemen dalam elementsarray. Jika demikian, ini akan mengaktifkan alert(), yang dapat dengan mudah dimodifikasi untuk melakukan sesuatu seperti menghapus item array, dalam hal ini Anda memerlukan fungsi render terpisah untuk memperbarui canvas.


Untuk kelengkapan, mengapa upaya Anda tidak berhasil ...

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

Ini menugaskan nilai kembali alert()ke onClickproperti dari elem. Segera memohon alert().

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

Dalam JavaScript, 'dan "secara semantik identik, lexer mungkin menggunakan ['"]tanda kutip.

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

Anda menetapkan string ke onClickproperti dari elem.

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

JavaScript peka huruf besar-kecil. The onclickproperti adalah metode kuno melampirkan event handler. Ini hanya memungkinkan satu acara untuk dilampirkan dengan properti dan acara dapat hilang ketika membuat serial HTML.

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

Lagi ' === ",.

alex
sumber
Hmm ... maafkan saya jika saya salah paham - tetapi apakah saya tidak menangkap peristiwa klik pada elemen kanvas? Saya tidak yakin apa yang Anda maksud dengan elemen apa yang diklik. Hanya ada satu elemen di halaman ini, kan?
Yer
Maksudnya "elemen mana di dalam kanvas" - itulah "bentuk" yang bisa dikatakan.
Jovan Perovic
1
Terima kasih, sangat banyak - meskipun saya tidak sepenuhnya jelas pada yang terakhir. Saya mengikuti contoh di sini: developer.mozilla.org/en/DOM/element.onclick (menggunakan fungsi anonim yang mereka gambarkan) dan berfungsi, bahkan ketika saya mengubah rentang ke kanvas. Jadi tidak sulit bagi saya untuk perlahan-lahan mengubah contoh mereka menjadi milik saya untuk melihat apa yang saya lakukan salah. Terima kasih atas tanggapan terperinci! Penjelasan mengapa berbagai upaya saya salah sangat membantu.
Yer
1
@ Jer. Jangan khawatir, jika Anda memiliki pertanyaan lain, jangan ragu untuk mengirim dan mengirim saya pesan ke Twitter, @alexdickson .
alex
1
@HashRocketSyntax Seharusnya berfungsi dengan baik, cukup perbarui posisi objek Anda dalam memori dan gunakan definisi tersebut untuk membuat dari
alex
4

Mungkin sangat terlambat untuk jawabannya tetapi saya baru saja membaca ini sambil mempersiapkan 70-480ujian saya , dan ternyata ini berhasil -

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

Perhatikan acara itu sebagai onclickganti onClick.

Contoh JS Bin .

Soham Dasgupta
sumber
3

Saya merekomendasikan artikel berikut: Hit Deteksi Wilayah Untuk Kanvas HTML5 Dan Cara Mendengarkan Klik Acara Pada Bentuk Kanvas yang melewati berbagai situasi.

Namun, itu tidak mencakup addHitRegionAPI, yang harus menjadi cara terbaik (menggunakan fungsi matematika dan / atau perbandingan cukup rentan kesalahan). Pendekatan ini dirinci pada developer.mozilla

RUser4512
sumber
"[ addHitRegion] sudah usang. Meskipun mungkin masih berfungsi di beberapa browser, penggunaannya tidak disarankan karena dapat dihapus kapan saja. Cobalah untuk menghindari menggunakannya." - dari tautan dev.moz yang Anda sertakan, untuk menyimpan klik orang lain.
Chris
2

Sebagai alternatif jawaban alex:

Anda bisa menggunakan gambar SVG alih-alih gambar Kanvas. Di sana Anda dapat menambahkan acara langsung ke objek DOM yang ditarik.

lihat misalnya:

Membuat objek gambar svg dapat diklik dengan onclick, menghindari penentuan posisi absolut

Goswin
sumber
1
maaf untuk antusiasme, hanya saja saya punya masalah yang menggunakan SVG adalah solusi yang jelas, dan saya belum memikirkannya sampai komentar Anda ^^
Elias Dorneles
1

Anda juga dapat menempatkan elemen DOM, seperti divdi atas kanvas yang akan mewakili elemen kanvas Anda dan diposisikan dengan cara yang sama.

Sekarang Anda dapat melampirkan pendengar acara ke div ini dan menjalankan tindakan yang diperlukan.

Sergei Basharov
sumber
1

Sebagai alternatif murah lain pada kanvas agak statis, menggunakan elemen img overlay dengan definisi usemap cepat dan kotor. Bekerja sangat baik pada elemen kanvas berbasis poligon seperti diagram lingkaran.

TadLewis
sumber
0

Jawaban Alex cukup rapi tetapi ketika menggunakan rotate konteks bisa sulit untuk melacak koordinat x, y, jadi saya telah membuat Demo yang menunjukkan cara melacaknya.

Pada dasarnya saya menggunakan fungsi ini & memberinya sudut & jumlah jarak yang ditempuh malaikat itu sebelum menggambar objek.

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
sumber