ในฐานะส่วนหน้า การเพิ่มเหตุการณ์ให้กับองค์ประกอบถือเป็นเรื่องปกติ แต่ใน Canvas สิ่งที่วาดบนนั้นหาไม่ได้ ไม่ต้องพูดถึงการเพิ่มกิจกรรม แล้วเราจะทำอะไรไม่ถูกกับมันล่ะ? ไม่แน่นอน! เราต้องใช้เฟรมเวิร์ก Canvas มากมายในโครงการรายวันของเรา เราพบว่ามีการใช้กิจกรรมต่างๆ อย่างเต็มที่ในเฟรมเวิร์กเหล่านี้ และไม่มีปัญหาร้ายแรงใดๆ เป็นพิเศษ ดังนั้นสิ่งที่เรามั่นใจได้ก็คือเหตุการณ์ต่างๆ ไม่ใช่สิ่งที่ไม่สามารถแตะต้องได้ใน Canvas
แนวทางของคนโง่เราทุกคนรู้ดีว่าเมื่อองค์ประกอบหนึ่งทำให้เกิดเหตุการณ์ ตำแหน่งของเมาส์โดยพื้นฐานแล้วจะอยู่เหนือองค์ประกอบ ดังนั้นเราจึงคิดโดยธรรมชาติของการเปรียบเทียบตำแหน่งปัจจุบันของเมาส์กับตำแหน่งที่วัตถุครอบครอง เพื่อที่เราจะได้ทราบว่าวัตถุควร กระตุ้นให้เกิดเหตุการณ์ วิธีนี้ค่อนข้างง่าย ดังนั้นฉันจะไม่ใช้โค้ดเพื่อสาธิต แต่เนื่องจากฉันเรียกมันว่าวิธีการของคนโง่ เห็นได้ชัดว่ามันไม่ใช่วิธีแก้ปัญหาที่มีประสิทธิภาพ เนื่องจากตำแหน่งที่วัตถุครอบครองนั้นไม่จำเป็นต้องได้มาง่ายๆ เสมอไป หากเป็นรูปสี่เหลี่ยมผืนผ้า วงกลม ฯลฯ เราก็สามารถหาตำแหน่งได้จากสูตรง่ายๆ บางอย่างเช่นกัน รูปหลายเหลี่ยมนั้นโค้ง เห็นได้ชัดว่ามันซับซ้อนมากและยากสำหรับเราที่จะได้ตำแหน่งที่ครอบครองในเวลานี้ ดังนั้นวิธีนี้จึงเหมาะสำหรับใช้ในการสาธิตบางรายการเท่านั้น และไม่เหมาะกับเงื่อนไขส่วนใหญ่
วิธีที่ชาญฉลาดยิ่งขึ้นเนื่องจากวิธีการข้างต้นทะลุกำแพงแล้ว เราจึงหาได้แค่วิธีอื่นเท่านั้น เมื่อเรียกดู CanvasAPI ฉันพบวิธีการ isPointInPath ซึ่งดูเหมือนว่าจะเป็นยาที่เรากำลังมองหา
ขอแนะนำ isPointInPathบทบาทของ isPointInPath: ตามชื่อที่แสดง เราสามารถรู้ได้โดยสัญชาตญาณว่าวิธีนี้ใช้เพื่อกำหนดว่าจุดนั้นอยู่ในเส้นทางหรือไม่
พารามิเตอร์อินพุตและเอาต์พุตของ isPointInPath คือ: ctx.isPointInPath([path, ]x, y [, fillRule]) วิธีนี้มีพารามิเตอร์ 4 ตัว โดยที่พาธและ fillRule เป็นทางเลือก และจำเป็นต้องมี x และ y เราแนะนำพารามิเตอร์ 4 ตัวตามลำดับ
เส้นทาง: เมื่อฉันเห็นพารามิเตอร์นี้ ตอนแรกฉันคิดว่ามันเป็นค่าที่ส่งคืนของ beginningPath หรือ closePath น่าเสียดาย ทั้งสองวิธีนี้ไม่ได้ส่งคืนค่า หลังจากตรวจสอบข้อมูลแล้ว ฉันพบว่ามันเป็นวัตถุของตัวสร้าง Path2D ใหม่ การใช้งานเฉพาะของตัวสร้าง Path2D อย่างไรก็ตาม น่าเสียดายที่วิธีนี้อาจเกิดจากปัญหาความเข้ากันได้ ปัจจุบันยังไม่มีการใช้เฟรมเวิร์กโอเพ่นซอร์สบางตัว
x, y: พารามิเตอร์ทั้งสองนี้เข้าใจง่าย คือระยะห่างระหว่างแกน x และแกน y ควรสังเกตว่าตำแหน่งสัมพัทธ์คือมุมซ้ายบนของ Canvas
fillRule: ไม่ใช่ศูนย์ (ค่าเริ่มต้น), เลขคู่ กฎการตัดคำที่ไม่ใช่ศูนย์และกฎเลขคู่เป็นกฎในกราฟิกสำหรับการพิจารณาว่าจุดอยู่ภายในรูปหลายเหลี่ยมหรือไม่ กฎการตัดคำที่ไม่ใช่ศูนย์เป็นกฎเริ่มต้นของ Canvas หากคุณต้องการทราบข้อมูลเพิ่มเติมเกี่ยวกับกฎสองข้อนี้ คุณสามารถตรวจสอบข้อมูลได้ด้วยตัวเอง ดังนั้นฉันจะไม่แนะนำกฎเหล่านี้ที่นี่
หลังจากแนะนำพารามิเตอร์อินพุตข้างต้น ทุกคนคงเดาพารามิเตอร์เอาท์พุตของเมธอด isPointInPath ได้ ซึ่งเป็นจริงและเท็จ
การใช้ isPointInPathหลังจากแนะนำเมธอด isPointInPath ไปแล้วในส่วนที่แล้ว เรามาใช้งานกันตอนนี้เลย
เริ่มต้นด้วยการสาธิตง่ายๆ:
const canvas = document.getElementById('canvas') const ctx = canvas.getContext('2d') ctx.beginPath() ctx.moveTo(10, 10) ctx.lineTo(10, 50) ctx.lineTo(50, 50) ) ctx.lineTo(50, 10) ctx.fillStyle= 'สีดำ' ctx.fill() ctx.closePath() canvas.addEventListener('คลิก', ฟังก์ชั่น (e) { const canvasInfo = canvas.getBoundingClientRect() console.log(ctx.isPointInPath(e.clientX - canvasInfo.left, e.clientY - canvasInfo.top)) })
ดังที่แสดงในภาพ ส่วนสีเทาคือพื้นที่ที่ถูกครอบครองโดย Canvas และส่วนสีดำคือพื้นที่ที่เราเพิ่มเหตุการณ์จริง ๆ หลังจากที่เราคลิกที่พื้นที่สีดำ มันจะทำตามที่เราต้องการจริง ๆ และค่าที่พิมพ์ออกมา เป็นจริง ดูเหมือนว่าการตรวจสอบเหตุการณ์ Canvas สามารถแก้ไขได้ง่ายๆ แต่มันง่ายขนาดนั้นจริงหรือ? เห็นได้ชัดว่าเป็นไปไม่ได้! มาดูอีกตัวอย่างหนึ่ง ในเวลานี้ มีสองส่วน และเราจำเป็นต้องเชื่อมโยงเหตุการณ์ต่างๆ เข้าด้วยกัน:
const canvas = document.getElementById('canvas') const ctx = canvas.getContext('2d') ctx.beginPath() ctx.moveTo(10, 10) ctx.lineTo(10, 50) ctx.lineTo(50, 50) ) ctx.lineTo(50, 10) ctx.fillStyle= 'สีดำ' ctx.fill() ctx.closePath() ctx.beginPath() ctx.moveTo(100, 100) ctx.lineTo(100, 150) ctx.lineTo(150, 150) ctx.lineTo(150, 100) ctx.fillStyle = 'สีแดง' ctx.fill() ctx.closePath() canvas.addEventListener('คลิก', ฟังก์ชั่น (e) { const canvasInfo = canvas.getBoundingClientRect() console.log(ctx.isPointInPath(e.clientX - canvasInfo.left, e.clientY - canvasInfo.top)) })
ในขณะนี้ ผลลัพธ์ไม่ใช่สิ่งที่เราคาดหวังอีกต่อไป เมื่อคลิกพื้นที่สีดำ ค่าที่พิมพ์จะเป็นเท็จ และเมื่อคลิกพื้นที่สีแดง ค่าที่พิมพ์จะเป็นจริง
จริงๆ แล้ว เหตุผลนั้นง่ายมาก เนื่องจากโค้ดข้างต้น เราจึงสร้าง Paths สองอันขึ้นมา และจริงๆ แล้วเมธอด isPointInPath จะตรวจจับได้ว่าจุดปัจจุบันอยู่ใน Path สุดท้ายหรือไม่ ในตัวอย่าง พื้นที่สีแดงคือ Path สุดท้าย ดังนั้นเมื่อมีการคลิกพื้นที่สีแดงเท่านั้น วิธี isPointInPath จึงสามารถตัดสินได้ว่าเป็นจริง ตอนนี้เรามาแปลงรหัสกัน:
const canvas = document.getElementById('canvas') const ctx = canvas.getContext('2d') ให้ DrawArray = [] ฟังก์ชั่น Draw1 () { ctx.beginPath() ctx.moveTo(10, 10) ctx.lineTo(10) , 50) ctx.lineTo(50, 50) ctx.lineTo(50, 10) ctx.fillStyle= 'สีดำ' ctx.fill() } ฟังก์ชั่น Draw2 () { ctx.beginPath() ctx.moveTo(100, 100) ctx.lineTo(100, 150) ctx.lineTo(150, 150) ctx.lineTo (150, 100) ctx.fillStyle= 'สีแดง' ctx.fill() ctx.closePath() } DrawArray.push(draw1, Draw2) DrawArray.forEach(it => { มัน() }) canvas.addEventListener('คลิก', ฟังก์ชั่น (e) { ctx.clearRect(0 , 0, 400, 750) const canvasInfo = canvas.getBoundingClientRect() DrawArray.forEach(it => { it() console.log(ctx.isPointInPath(e.clientX - canvasInfo.left, e.clientY - canvasInfo.top)) }) })
เราได้ทำการแก้ไขโค้ดด้านบนครั้งใหญ่ เราแยกแต่ละ Path ออกเป็นฟังก์ชันแยกกันและใส่เข้าไปในอาร์เรย์ เมื่อเหตุการณ์การคลิกถูกทริกเกอร์ เราจะล้าง Canvas สำรวจอาร์เรย์และวาดใหม่ และทำการตัดสินใจทุกครั้งที่วาด Path ดังนั้นเมื่อเรียกใช้เมธอด isPointInPath เราจะสามารถรับ Path ปัจจุบันล่าสุดแบบเรียลไทม์ได้ ตัดสินจุดปัจจุบันระหว่างเส้นทาง
ตอนนี้เราได้ดำเนินการติดตามเหตุการณ์โดยอ้อมสำหรับแต่ละเส้นทางแล้ว แต่วิธีการนำไปใช้นั้นจำเป็นต้องวาดใหม่ครั้งแล้วครั้งเล่า มีวิธีติดตามเหตุการณ์โดยไม่ต้องวาดใหม่หรือไม่?
ก่อนอื่น เราต้องรู้ว่าเหตุผลในการวาดซ้ำแล้วซ้ำเล่าคือเนื่องจากเมธอด isPointInPath เป็นพาธสุดท้ายที่ต้องตรวจสอบ อย่างไรก็ตาม เมื่อเราแนะนำวิธีนี้ เราบอกว่าพารามิเตอร์แรกของมันคือออบเจ็กต์ Path ผ่านพารามิเตอร์นี้ Path จะไม่ใช้ Path สุดท้ายอีกต่อไป แต่ใช้ Path ที่เราส่งเข้าไป ตอนนี้เรามาทำการสาธิตเพื่อตรวจสอบความเป็นไปได้:
const canvas = document.getElementById('canvas') const ctx = canvas.getContext('2d') const path1 = new Path2D(); path1.rect(10, 10, 100,100); ใหม่ Path2D(); path2.moveTo(220, 60); path2.arc(170, 60, 50, 0, 2 * Math.PI); ctx. stroke (path2) canvas.addEventListener ('คลิก', ฟังก์ชั่น (e) { console.log (ctx.isPointInPath (path1, e.clientX, e.clientY) ) console.log(ctx.isPointInPath(path2, e.clientX, e.clientY)) })
ดังที่แสดงในภาพด้านบน เราคลิกที่กราฟิกด้านซ้ายและพิมพ์จริงและเท็จ คลิกบนกราฟิกด้านขวาและพิมพ์เท็จและจริง ผลลัพธ์ที่พิมพ์ออกมาแสดงว่าไม่มีปัญหา แต่เนื่องจากต้องปรับปรุงความเข้ากันได้ จึงแนะนำให้ใช้วิธีวาดใหม่เพื่อฟังเหตุการณ์
บทสรุปโดยพื้นฐานแล้วจะครอบคลุมการติดตามเหตุการณ์ของ Canvas ไว้ที่นี่ และทุกคนควรจะเชี่ยวชาญมันได้
ที่อยู่ GitHub ยินดีต้อนรับสู่การเริ่มต้น
ภาคผนวกการสาธิตที่เขียนโดยฉันเอง
const canvas = document.getElementById('canvas') คลาสสี่เหลี่ยมผืนผ้า { ตัวสร้าง ( ctx, { top = 0, ซ้าย = 0, ความกว้าง = 30, ความสูง = 50, พื้นหลัง = 'สีแดง' } ) { this.ctx = ctx สิ่งนี้ top = top this.left = left this.width = width this.height = height this.พื้นหลัง = พื้นหลัง } ภาพวาด () { this.ctx.beginPath() this.ctx.moveTo(this.left, this.top) this.ctx.lineTo(this.left + this.width, this.top) this.ctx.lineTo(this.left + this.width, this.top + this.height) this.ctx.lineTo(this.left, this.top + this.height) this.ctx.fillStyle = this.พื้นหลัง this.ctx.fill() this.ctx.closePath() } ปรับ (ซ้าย, บน) { this.left += left this.top += top } } วงกลมคลาส { ตัวสร้าง ( ctx, { center = [], รัศมี = 10, พื้นหลัง = 'สีน้ำเงิน' } ) { this.ctx = ctx this.center = [center[0] === ไม่ได้กำหนด ? รัศมี : center[0], center[1] === ไม่ได้กำหนด ? รัศมี : center[1]] this.radius = รัศมี this.พื้นหลัง = พื้นหลัง } จิตรกรรม () { this.ctx.beginPath() this.ctx.arc(this.center[0], this.center[1], this.radius, 0, Math.PI * 2, false) this.ctx.fillStyle = this.พื้นหลัง this.ctx.fill() this.ctx.closePath() } ปรับ (ซ้าย, บน) { this.center[0] += ซ้าย this.center[1] += top } } การสาธิตคลาส { ตัวสร้าง (ผ้าใบ) { this.canvasInfo = canvas.getBoundingClientRect() this.renderList = [] this.ctx = canvas.getContext('2d') this.canvas = canvas this.rectangular = (config) => { ให้เป้าหมาย = สี่เหลี่ยมใหม่ (this.ctx, {...config}) this.addRenderList(target) ส่งคืนสิ่งนี้ } this.circle = (config) => { ให้เป้าหมาย = ใหม่ วงกลม (this.ctx, {...config}) this.addRenderList(เป้าหมาย) ส่งคืนสิ่งนี้ } this.addEvent() } addRenderList (เป้าหมาย) { this.renderList.push(target) } itemToLast (ดัชนี) { const LastItem = this.renderList.splice (ดัชนี, 1) [0] this.renderList.push (lastItem) } ภาพวาด () { this.ctx.clearRect (0, 0, this.canvasInfo.width, this.canvasInfo.height) this.renderList.forEach(it => it.painting()) } addEvent () { const that = this ให้ startX, startY canvas.addEventListener('mousedown', e => { startX = e.clientX startY = e.clientY ให้ chooseIndex = null this.renderList.forEach ((มัน ดัชนี) => { it.painting() if (this.ctx.isPointInPath(startX, startY)) { ChooseIndex = index } }) if (choosedIndex !== null) { this.itemToLast(choosedIndex) } document.addEventListener( 'mousemove', mousemoveEvent) document.addEventListener ('mouseup', mouseupEvent) this.painting () }) ฟังก์ชั่น 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() } ฟังก์ชั่น 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 = การสาธิตใหม่ (canvas) .สี่เหลี่ยม({}) .สี่เหลี่ยม({บน: 60, ซ้าย: 60, พื้นหลัง: 'สีน้ำเงิน'}) .สี่เหลี่ยม({บน: 30, ซ้าย: 20, พื้นหลัง: 'สีเขียว'}) .circle() .circle ({center: [100, 30], พื้นหลัง: 'red', รัศมี: 5}) .painting()
ข้างต้นคือเนื้อหาทั้งหมดของบทความนี้ ฉันหวังว่ามันจะเป็นประโยชน์ต่อการศึกษาของทุกคน ฉันหวังว่าทุกคนจะสนับสนุน VeVb Wulin Network