Sebagai front-end, menambahkan event ke elemen adalah hal yang biasa. Namun di Canvas, apa pun yang digambar di atasnya tidak dapat diperoleh, apalagi menambahkan peristiwa, jadi apakah kita tidak berdaya menghadapinya? Tentu saja tidak! Kami pasti telah menggunakan banyak kerangka kerja Canvas dalam proyek sehari-hari kami. Kami menemukan bahwa peristiwa telah digunakan dengan sangat matang dalam kerangka kerja ini, dan tidak ada masalah yang serius. Jadi yang bisa kami pastikan adalah bahwa event bukanlah hal yang tidak bisa disentuh di Canvas.
pendekatan yang bodohKita semua tahu bahwa ketika sebuah elemen memicu suatu peristiwa, posisi mouse pada dasarnya berada di atas elemen tersebut, jadi secara alami kita berpikir untuk membandingkan posisi mouse saat ini dengan posisi yang ditempati oleh objek, sehingga kita bisa mendapatkan apakah objek tersebut harus memicu peristiwa tersebut. peristiwa. Metode ini relatif sederhana, jadi saya tidak akan menggunakan kode untuk mendemonstrasikannya, tetapi karena saya menyebutnya metode bodoh, jelas ini bukanlah solusi yang efektif. Karena posisi yang ditempati suatu benda belum tentu mudah didapat. Jika berbentuk persegi panjang, lingkaran, dan sebagainya, kita juga dapat memperoleh posisinya melalui beberapa rumus sederhana, namun pada poligon dengan titik-titik kompleks, atau bahkan beberapa sisi poligonnya Melengkung, tentunya sangat rumit dan sulit bagi kami untuk mendapatkan posisi yang ditempatinya saat ini, jadi metode ini hanya cocok untuk digunakan di beberapa demo, dan tidak cocok untuk sebagian besar Kondisi.
cara yang lebih cerdasKarena metode di atas menemui jalan buntu, kita hanya dapat menemukan cara lain. Saat menjelajahi CanvasAPI, saya menemukan metode isPointInPath, yang sepertinya merupakan obat yang kami cari.
Memperkenalkan isPointInPathPeran isPointInPath: Seperti namanya, kita secara intuitif dapat mengetahui bahwa metode ini digunakan untuk menentukan apakah suatu titik berada dalam suatu jalur.
Parameter input dan output isPointInPath adalah: ctx.isPointInPath([path, ]x, y [, fillRule]). Metode ini memiliki 4 parameter, yang mana path dan fillRule bersifat opsional, dan x dan y diperlukan. Kami memperkenalkan 4 parameter secara bergantian.
path: Ketika saya melihat parameter ini, awalnya saya mengira itu adalah nilai kembalian dari BeginPath atau ClosePath. Sayangnya, kedua metode ini tidak mengembalikan nilai. Setelah memeriksa informasinya, saya menemukan bahwa itu adalah objek dari konstruktor Path2D yang baru. Penggunaan khusus konstruktor Path2D. Namun sayang sekali metode ini mungkin disebabkan oleh masalah kompatibilitas. Saat ini, beberapa kerangka kerja sumber terbuka belum digunakan.
x, y: Kedua parameter ini mudah dipahami, yaitu jarak antara sumbu x dan sumbu y. Perlu diperhatikan bahwa posisi relatifnya adalah pojok kiri atas Canvas.
fillRule: bukan nol (default), ganjil genap. Aturan pembungkusan bukan nol dan aturan ganjil genap adalah aturan dalam grafik untuk menentukan apakah suatu titik berada dalam poligon. Aturan pembungkusan bukan nol adalah aturan default Canvas. Jika Anda ingin tahu lebih banyak tentang kedua aturan ini, Anda dapat memeriksa sendiri informasinya, jadi saya tidak akan memperkenalkannya di sini.
Setelah memasukkan parameter masukan di atas, semua orang mungkin dapat menebak parameter keluaran dari metode isPointInPath, mana yang benar dan salah.
Menggunakan isPointInPathSetelah memperkenalkan metode isPointInPath di bagian sebelumnya, mari kita gunakan sekarang.
Mari kita mulai dengan demo sederhana:
const kanvas = document.getElementById('canvas') const ctx = kanvas.getContext('2d') ctx.beginPath() ctx.moveTo(10, 10) ctx.lineTo(10, 50) ctx.lineTo(50, 50 ) ctx.lineTo(50, 10) ctx.fillStyle= 'hitam' ctx.fill() ctx.closePath() canvas.addEventListener('click', function (e) { const canvasInfo = canvas.getBoundingClientRect() console.log(ctx.isPointInPath(e.clientX - canvasInfo.left, e.clientY - kanvasInfo.top)) })
Seperti yang ditunjukkan pada gambar, bagian abu-abu adalah area yang ditempati oleh Canvas, dan bagian hitam adalah area di mana kita menambahkan acara. Setelah kita klik pada area hitam, itu benar-benar melakukan apa yang kita inginkan, dan nilai yang dicetak itu benar. Tampaknya pemantauan peristiwa Canvas dapat diselesaikan dengan sederhana, tetapi apakah sesederhana itu? Jelas mustahil! Mari kita ambil contoh lain. Ada dua area saat ini, dan kita perlu mengikat peristiwa yang berbeda ke area tersebut:
const kanvas = document.getElementById('canvas') const ctx = kanvas.getContext('2d') ctx.beginPath() ctx.moveTo(10, 10) ctx.lineTo(10, 50) ctx.lineTo(50, 50 ) ctx.lineTo(50, 10) ctx.fillStyle= 'hitam' ctx.fill() ctx.closePath() ctx.beginPath() ctx.moveTo(100, 100) ctx.lineTo(100, 150) ctx.lineTo(150, 150) ctx.lineTo(150, 100) ctx.fillStyle = 'merah' ctx.fill() ctx.closePath() canvas.addEventListener('klik', fungsi (e) { const canvasInfo = canvas.getBoundingClientRect() console.log(ctx.isPointInPath(e.clientX - canvasInfo.left, e.clientY - canvasInfo.top)) })
Saat ini hasilnya tidak lagi seperti yang kita harapkan. Jika area hitam diklik, nilai yang dicetak salah, dan jika area merah diklik, nilai yang dicetak benar.
Sebenarnya alasannya sangat sederhana. Karena kode di atas, sebenarnya kita membuat dua Path, dan metode isPointInPath sebenarnya hanya mendeteksi apakah titik saat ini berada di Path terakhir. jadi hanya ketika area merah diklik, metode isPointInPath dapat dinilai benar. Sekarang mari kita ubah kodenya:
const kanvas = document.getElementById('canvas') const ctx = kanvas.getContext('2d') biarkan drawArray = [] function draw1 () { ctx.beginPath() ctx.moveTo(10, 10) ctx.lineTo(10 , 50) ctx.lineTo(50, 50) ctx.lineTo(50, 10) ctx.fillStyle= 'hitam' ctx.fill() } fungsi draw2 () { ctx.beginPath() ctx.moveTo(100, 100) ctx.lineTo(100, 150) ctx.lineTo(150, 150) ctx.lineTo (150, 100) ctx.fillStyle= 'merah' ctx.fill() ctx.closePath() } drawArray.push(draw1, draw2) drawArray.forEach(it => { it() }) canvas.addEventListener('klik', function (e) { ctx.clearRect(0 , 0, 400, 750) const canvasInfo = kanvas.getBoundingClientRect() drawArray.forEach(it => { it() console.log(ctx.isPointInPath(e.clientX - canvasInfo.left, e.clientY - canvasInfo.top)) }) })
Kami telah membuat modifikasi besar pada kode di atas. Kami menempatkan setiap Path ke dalam fungsi terpisah dan memasukkannya ke dalam array. Ketika peristiwa klik dipicu, kita menghapus Canvas, melintasi array dan menggambar ulang, dan membuat penilaian setiap kali Path diambil. Oleh karena itu, saat memanggil metode isPointInPath, kita bisa mendapatkan Path terakhir saat ini secara real time, dan kemudian menilai titik saat ini.
Sekarang kami secara tidak langsung telah menerapkan pemantauan peristiwa terpisah untuk setiap Jalur, namun cara penerapannya memerlukan penggambaran ulang berulang kali. Jadi apakah ada cara untuk memantau peristiwa tanpa menggambar ulang?
Pertama-tama, kita perlu mengetahui bahwa alasan menggambar ulang berulang kali adalah karena metode isPointInPath adalah Path terakhir yang dipantau. Namun, saat kami memperkenalkan metode ini, kami mengatakan bahwa parameter pertamanya adalah objek Path lewati parameter ini, Path tidak akan lagi mengambil Path terakhir tetapi menggunakan Path yang kita lewati. Sekarang mari kita lakukan demo untuk memverifikasi kelayakannya:
const kanvas = document.getElementById('kanvas') const ctx = kanvas.getContext('2d') const path1 = new Path2D(); jalur2D(); jalur2.moveTo(220, 60); 60, 50, 0, 2 * Math.PI); ctx.stroke(path2) canvas.addEventListener('klik', function (e) { console.log(ctx.isPointInPath(path1, e.clientX, e.clientY) ) console.log(ctx.isPointInPath(path2, e.clientX, e.clientY)) })
Seperti terlihat pada gambar di atas, kita mengklik grafik kiri dan mencetak benar dan salah; mengklik grafik kanan dan mencetak salah dan benar. Hasil cetakan menunjukkan tidak ada masalah, namun karena kompatibilitasnya perlu ditingkatkan, saat ini disarankan untuk menggunakan metode menggambar ulang untuk mendengarkan kejadian.
KesimpulanPemantauan peristiwa Canvas pada dasarnya dibahas di sini. Prinsipnya sangat sederhana dan setiap orang harus bisa menguasainya.
alamat github, selamat datang untuk memulai
lampiranDemo yang saya tulis sendiri
const kanvas = document.getElementById('kanvas') kelas persegi panjang { konstruktor ( ctx, { atas = 0, kiri = 0, lebar = 30, tinggi = 50, latar belakang = 'merah' } ) { ini.ctx = ctx ini. atas = atas ini.kiri = kiri ini.lebar = lebar ini.tinggi = tinggi ini.latar belakang = latar belakang } lukisan () { ini.ctx.beginPath() ini.ctx.moveTo(ini.kiri, ini.atas) ini.ctx.lineTo(ini.kiri + ini.lebar, ini.atas) ini.ctx.lineTo(ini.kiri + ini.lebar, ini.atas + ini.tinggi) ini.ctx.lineTo(ini.kiri, ini.atas + ini.tinggi) ini.ctx.fillStyle = ini.latar belakang ini.ctx.fill() ini.ctx.closePath() } sesuaikan (kiri, atas) { this.left += kiri this.top += top } } lingkaran kelas { konstruktor ( ctx, { center = [], radius = 10, background = 'biru' } ) { this.ctx = ctx this.center = [center[0] === tidak terdefinisi ? radius : center[0], center[1] === tidak terdefinisi ? radius : center[1]] this.radius = radius this.background = latar belakang } lukisan () { this.ctx.beginPath() this.ctx.arc(this.center[0], this.center[1], this.radius, 0, Math.PI * 2, false) this.ctx.fillStyle = this.background this.ctx.fill() this.ctx.closePath() } adjust (kiri, atas) { this.center[0] += kiri this.center[1] += top } } demo kelas { konstruktor (kanvas) { this.canvasInfo = canvas.getBoundingClientRect() this.renderList = [] this.ctx = canvas.getContext('2d') this.canvas = kanvas this.rectangular = (config) => { biarkan target = persegi panjang baru(ini.ctx, {...config}) ini.addRenderList(target) kembalikan ini } ini.lingkaran = (config) => { biarkan target = baru lingkaran(ini.ctx, {...config}) ini.addRenderList(target) kembalikan ini } ini.addEvent() } addRenderList (target) { this.renderList.push(target) } itemToLast (indeks) { const lastItem = this.renderList.splice(index, 1)[0] this.renderList.push(lastItem) } lukisan () { this.ctx.clearRect(0, 0, this.canvasInfo.width, this.canvasInfo.height) this.renderList.forEach(it => it.painting()) } addEvent () { const that = ini biarkan startX, startY canvas.addEventListener('mousedown', e => { startX = e.clientX startY = e.clientY biarkan dipilihIndex = null this.renderList.forEach((it, indeks) => { it.painting() if (this.ctx.isPointInPath(startX, startY)) { controlledIndex = indeks } }) if (choosedIndex !== null) { this.itemToLast(choosedIndex) } document.addEventListener( 'mousemove', mousemoveEvent) document.addEventListener('mouseup', mouseupEvent) this.painting() }) fungsi mousemoveEvent (e) { const target = that.renderList[that.renderList.length - 1] const currentX = e.clientX const currentY = e.clientY target.adjust(currentX - startX, currentY - startY) startX = currentX startY = currentY that.painting() } function mouseupEvent (e) { const target = that.renderList[that.renderList.length - 1] const currentX = e.clientX const currentY = e.clientY target.adjust(currentX - startX, currentY - startY) startX = currentX startY = currentY that.painting() document.removeEventListener('mousemove', mousemoveEvent) document.removeEventListener ('mouseup', mouseupEvent) } } } const yes = demo baru(kanvas) .rectangular({}) .rectangular({atas: 60, kiri: 60, latar belakang: 'biru'}) .rectangular({atas: 30, kiri: 20, latar belakang: 'hijau'}) .circle() .circle ({pusat: [100, 30], latar belakang: 'merah', radius: 5}) .painting()
Di atas adalah keseluruhan isi artikel ini, saya harap dapat bermanfaat untuk pembelajaran semua orang. Saya juga berharap semua orang mendukung VeVb Wulin Network.