本篇文章給大家分享的是有關(guān)怎么在Canvas中添加事件,小編覺(jué)得挺實(shí)用的,因此分享給大家學(xué)習(xí),希望大家閱讀完這篇文章后可以有所收獲,話不多說(shuō),跟著小編一起來(lái)看看吧。
成都創(chuàng)新互聯(lián)公司于2013年成立,是專業(yè)互聯(lián)網(wǎng)技術(shù)服務(wù)公司,擁有項(xiàng)目成都做網(wǎng)站、網(wǎng)站制作網(wǎng)站策劃,項(xiàng)目實(shí)施與項(xiàng)目整合能力。我們以讓每一個(gè)夢(mèng)想脫穎而出為使命,1280元睢陽(yáng)做網(wǎng)站,已為上家服務(wù),為睢陽(yáng)各地企業(yè)和個(gè)人服務(wù),聯(lián)系電話:028-86922220
isPointInPath的作用:顧名思義,我們很直觀的可以知道該方法用以判斷點(diǎn)是否處于路徑當(dāng)中。
isPointInPath的入?yún)⒊鰠ⅲ篶tx.isPointInPath([path, ]x, y [, fillRule]),該方法的參數(shù)有4個(gè),其中path和fillRule為選填,x和y為必填。我們依次介紹4個(gè)參數(shù)。
path:看到這個(gè)參數(shù),我開(kāi)始以為是beginPath或者closePath的返回值,很可惜的是這兩個(gè)方法并沒(méi)有返回值,在查閱了資料后,發(fā)現(xiàn)是Path3D構(gòu)造函數(shù)new的對(duì)象。Path3D構(gòu)造函數(shù)具體用法。不過(guò)可惜的是該方法可能由于兼容性的問(wèn)題,目前看了一些開(kāi)源框架都還未使用。
x,y:這兩個(gè)參數(shù)很好理解,就是x軸和y軸的距離,需要注意的是,其相對(duì)位置是Canvas的左上角。
fillRule:nonzero(默認(rèn)),evenodd。非零環(huán)繞規(guī)則和奇偶規(guī)則是圖形學(xué)中判斷一個(gè)點(diǎn)是否處于多邊形內(nèi)的規(guī)則,其中非零環(huán)繞規(guī)則是Canvas的默認(rèn)規(guī)則。想具體了解這兩種規(guī)則的,可以自己去查閱資料,這里就不增加篇幅介紹了。
上面介紹完了入?yún)?,那么isPointInPath方法的出參想必大家都可以猜到了,就是true和false。
使用isPointInPath
上一節(jié)介紹完isPointInPath方法后,我們現(xiàn)在就來(lái)使用它吧。
先來(lái)一個(gè)簡(jiǎn)單的demo:
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= 'black' ctx.fill() ctx.closePath() canvas.addEventListener('click', function (e) { const canvasInfo = canvas.getBoundingClientRect() console.log(ctx.isPointInPath(e.clientX - canvasInfo.left, e.clientY - canvasInfo.top)) })
如圖所示,灰色部分為Canvas所占據(jù)的區(qū)域,黑色為我們實(shí)際添加事件的區(qū)域,在我們點(diǎn)擊黑色區(qū)域后,實(shí)際也的確如我們所愿,打印出來(lái)的值為true。貌似Canvas的事件監(jiān)聽(tīng)就這么簡(jiǎn)單的解決了,不過(guò)事情真有這么簡(jiǎn)單嗎。顯然是不可能的!我們?cè)賮?lái)舉個(gè)例子,這時(shí)候有兩個(gè)區(qū)域,并且我們需要分別給其綁定不同的事件:
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= 'black' ctx.fill() ctx.closePath() ctx.beginPath() ctx.moveTo(100, 100) ctx.lineTo(100, 150) ctx.lineTo(150, 150) ctx.lineTo(150, 100) ctx.fillStyle= 'red' ctx.fill() ctx.closePath() canvas.addEventListener('click', function (e) { const canvasInfo = canvas.getBoundingClientRect() console.log(ctx.isPointInPath(e.clientX - canvasInfo.left, e.clientY - canvasInfo.top)) })
這個(gè)時(shí)候,結(jié)果就不再如同我們所預(yù)計(jì)的一樣,當(dāng)點(diǎn)擊其中黑色區(qū)域時(shí),打印的值為false,點(diǎn)擊紅色區(qū)域時(shí),打印的值為true。
其實(shí)原因很簡(jiǎn)單,因?yàn)樯鲜龃a,我們實(shí)際創(chuàng)建了兩個(gè)Path,而isPointInPath方法實(shí)際只檢測(cè)當(dāng)前點(diǎn)是否處于最后一個(gè)Path當(dāng)中,而例子中紅色區(qū)域?yàn)樽詈笠粋€(gè)Path,所以只有點(diǎn)擊紅色區(qū)域時(shí),isPointInPath方法才能判斷為true?,F(xiàn)在我們改造一下代碼:
const canvas = document.getElementById('canvas') const ctx = canvas.getContext('2d') let drawArray = [] function draw1 () { ctx.beginPath() ctx.moveTo(10, 10) ctx.lineTo(10, 50) ctx.lineTo(50, 50) ctx.lineTo(50, 10) ctx.fillStyle= 'black' ctx.fill() } function draw2 () { ctx.beginPath() ctx.moveTo(100, 100) ctx.lineTo(100, 150) ctx.lineTo(150, 150) ctx.lineTo(150, 100) ctx.fillStyle= 'red' ctx.fill() ctx.closePath() } drawArray.push(draw1, draw2) drawArray.forEach(it => { it() }) canvas.addEventListener('click', function (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)) }) })
上面的代碼我們進(jìn)行了一個(gè)很大的改造,我們將每個(gè)Path放入到一個(gè)單獨(dú)的函數(shù)當(dāng)中,并將它們push到一個(gè)數(shù)組當(dāng)中。當(dāng)觸發(fā)點(diǎn)擊事件時(shí),我們清空Canvas,并遍歷數(shù)組重新繪制,每當(dāng)繪制一個(gè)Path進(jìn)行一次判斷,從而在調(diào)用isPointInPath方法時(shí),我們能實(shí)時(shí)的獲取當(dāng)前的最后一個(gè)Path,進(jìn)而判斷出當(dāng)前點(diǎn)所處的Path當(dāng)中。
現(xiàn)在我們已經(jīng)間接的實(shí)現(xiàn)了對(duì)每個(gè)Path的單獨(dú)事件監(jiān)聽(tīng),可是其實(shí)現(xiàn)的方式需要一次又一次的重繪,那么有辦法不需要重繪就能監(jiān)聽(tīng)事件嗎?
首先我們需要知道一次又一次重繪的原因是因?yàn)閕sPointInPath方法是監(jiān)聽(tīng)的最后一個(gè)Path,不過(guò)我們?cè)诮榻B這個(gè)方法的時(shí)候,說(shuō)過(guò)其第一個(gè)參數(shù)是一個(gè)Path對(duì)象,當(dāng)我們傳遞了這個(gè)參數(shù)后,Path就不再去取最后一個(gè)Path而是使用我們傳遞進(jìn)去的這個(gè)Path,現(xiàn)在我們來(lái)個(gè)demo來(lái)驗(yàn)證其可行性:
const canvas = document.getElementById('canvas') const ctx = canvas.getContext('2d') const path2 = new Path3D(); path2.rect(10, 10, 100,100); ctx.fill(path2) const path3 = new Path3D(); path3.moveTo(220, 60); path3.arc(170, 60, 50, 0, 2 * Math.PI); ctx.stroke(path3) canvas.addEventListener('click', function (e) { console.log(ctx.isPointInPath(path2, e.clientX, e.clientY)) console.log(ctx.isPointInPath(path3, e.clientX, e.clientY)) })
如上圖所示,我們點(diǎn)擊了左邊圖形,打印true,false;點(diǎn)擊右邊圖形,打印false,true。打印的結(jié)果表明是沒(méi)有問(wèn)題的,不過(guò)由于其兼容性還有待加強(qiáng),所以目前建議還是使用重繪方式來(lái)監(jiān)聽(tīng)事件。
結(jié)語(yǔ)
Canvas的事件監(jiān)聽(tīng)講到這里基本就差不多了,原理很簡(jiǎn)單,大家應(yīng)該都能掌握。
github地址,歡迎start
附錄
自己寫(xiě)的一個(gè)demo
const canvas = document.getElementById('canvas') class rectangular { constructor ( ctx, { top = 0, left = 0, width = 30, height = 50, background = 'red' } ) { this.ctx = ctx this.top = top this.left = left this.width = width this.height = height this.background = background } painting () { 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.background this.ctx.fill() this.ctx.closePath() } adjust (left, top) { this.left += left this.top += top } } class circle { constructor ( ctx, { center = [], radius = 10, background = 'blue' } ) { this.ctx = ctx this.center = [center[0] === undefined ? radius : center[0], center[1] === undefined ? radius : center[1]] this.radius = radius this.background = background } painting () { 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 (left, top) { this.center[0] += left this.center[1] += top } } class demo { constructor (canvas) { this.canvasInfo = canvas.getBoundingClientRect() this.renderList = [] this.ctx = canvas.getContext('2d') this.canvas = canvas this.rectangular = (config) => { let target = new rectangular(this.ctx, {...config}) this.addRenderList(target) return this } this.circle = (config) => { let target = new circle(this.ctx, {...config}) this.addRenderList(target) return this } this.addEvent() } addRenderList (target) { this.renderList.push(target) } itemToLast (index) { const lastItem = this.renderList.splice(index, 1)[0] this.renderList.push(lastItem) } painting () { this.ctx.clearRect(0, 0, this.canvasInfo.width, this.canvasInfo.height) this.renderList.forEach(it => it.painting()) } addEvent () { const that = this let startX, startY canvas.addEventListener('mousedown', e => { startX = e.clientX startY = e.clientY let choosedIndex = null this.renderList.forEach((it, index) => { it.painting() if (this.ctx.isPointInPath(startX, startY)) { choosedIndex = index } }) if (choosedIndex !== null) { this.itemToLast(choosedIndex) } document.addEventListener('mousemove', mousemoveEvent) document.addEventListener('mouseup', mouseupEvent) this.painting() }) function 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 = new demo(canvas) .rectangular({}) .rectangular({top: 60, left: 60, background: 'blue'}) .rectangular({top: 30, left: 20, background: 'green'}) .circle() .circle({center: [100, 30], background: 'red', radius: 5}) .painting()
以上就是怎么在Canvas中添加事件,小編相信有部分知識(shí)點(diǎn)可能是我們?nèi)粘9ぷ鲿?huì)見(jiàn)到或用到的。希望你能通過(guò)這篇文章學(xué)到更多知識(shí)。更多詳情敬請(qǐng)關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。