婷婷综合国产,91蜜桃婷婷狠狠久久综合9色 ,九九九九九精品,国产综合av

主頁 > 知識庫 > 詳解如何在Canvas中添加事件的方法

詳解如何在Canvas中添加事件的方法

熱門標簽:江西ai電銷機器人如何 通遼地圖標注app 地圖標注沿海城市房價 西安金倫外呼系統 威海語音外呼系統平臺 地圖標注員工作內容 中國地圖標注城市的 高德地圖標注廁所 智能語音電銷機器人客戶端

作為一個前端,給元素添加事件是一件司空見慣的事情。可是在Canvas中,其所畫的任何東西都是無法獲取的,更別說添加事件,那么我們對其就束手無策了嗎?當然不是的!我們在平時項目中肯定都用過許多Canvas的框架,我們發現事件在這些框架中已經使用的十分成熟了,而且并沒有出現特別嚴重的問題。那么我們可以肯定的是,事件在Canvas中并不是一個無法觸及的事情。

一個傻瓜式的方式

我們都知道一個元素在觸發一個事件時,其鼠標的位置基本處于該元素之上,那么我們就自然而然的想到通過當前鼠標的位置以及物體所占據的位置進行比對,從而我們就能得出該物體是否應觸發事件。這種方式比較簡單,我就不用代碼演示了,不過既然我叫它傻瓜式的方式,很明顯它不是一個有效的解決方式。因為物體所占據的位置并不一定是十分容易獲取,如果是矩形、圓形等我們還能通過一些簡單的公式獲取其占據的位置,可是在復雜點的多邊形,甚至是多邊形的某些邊是弧線的,顯而易見,我們這時候再獲取其所占據的位置時是一件極其復雜且難度極大的事情,所以這種方式只適合自己在做一些demo中使用,并不適用于大多數的情況。

一個較聰明的方式

既然上面這種方式碰壁了,那么我們只能另辟蹊徑。在翻閱CanvasAPI的時候,找到了一個方法isPointInPath,貌似正是我們苦苦尋找的良藥。

介紹isPointInPath

isPointInPath的作用:顧名思義,我們很直觀的可以知道該方法用以判斷點是否處于路徑當中。

isPointInPath的入參出參:ctx.isPointInPath([path, ]x, y [, fillRule]),該方法的參數有4個,其中path和fillRule為選填,x和y為必填。我們依次介紹4個參數。

path:看到這個參數,我開始以為是beginPath或者closePath的返回值,很可惜的是這兩個方法并沒有返回值,在查閱了資料后,發現是Path2D構造函數new的對象。Path2D構造函數具體用法。不過可惜的是該方法可能由于兼容性的問題,目前看了一些開源框架都還未使用。

x,y:這兩個參數很好理解,就是x軸和y軸的距離,需要注意的是,其相對位置是Canvas的左上角。

fillRule:nonzero(默認),evenodd。非零環繞規則和奇偶規則是圖形學中判斷一個點是否處于多邊形內的規則,其中非零環繞規則是Canvas的默認規則。想具體了解這兩種規則的,可以自己去查閱資料,這里就不增加篇幅介紹了。

上面介紹完了入參,那么isPointInPath方法的出參想必大家都可以猜到了,就是true和false。

使用isPointInPath

上一節介紹完isPointInPath方法后,我們現在就來使用它吧。

先來一個簡單的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所占據的區域,黑色為我們實際添加事件的區域,在我們點擊黑色區域后,實際也的確如我們所愿,打印出來的值為true。貌似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= '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))
  })

這個時候,結果就不再如同我們所預計的一樣,當點擊其中黑色區域時,打印的值為false,點擊紅色區域時,打印的值為true。

其實原因很簡單,因為上述代碼,我們實際創建了兩個Path,而isPointInPath方法實際只檢測當前點是否處于最后一個Path當中,而例子中紅色區域為最后一個Path,所以只有點擊紅色區域時,isPointInPath方法才能判斷為true。現在我們改造一下代碼:

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

上面的代碼我們進行了一個很大的改造,我們將每個Path放入到一個單獨的函數當中,并將它們push到一個數組當中。當觸發點擊事件時,我們清空Canvas,并遍歷數組重新繪制,每當繪制一個Path進行一次判斷,從而在調用isPointInPath方法時,我們能實時的獲取當前的最后一個Path,進而判斷出當前點所處的Path當中。

現在我們已經間接的實現了對每個Path的單獨事件監聽,可是其實現的方式需要一次又一次的重繪,那么有辦法不需要重繪就能監聽事件嗎?

首先我們需要知道一次又一次重繪的原因是因為isPointInPath方法是監聽的最后一個Path,不過我們在介紹這個方法的時候,說過其第一個參數是一個Path對象,當我們傳遞了這個參數后,Path就不再去取最后一個Path而是使用我們傳遞進去的這個Path,現在我們來個demo來驗證其可行性:

  const canvas = document.getElementById('canvas')
  const ctx = canvas.getContext('2d')

  const path1 = new Path2D();
  path1.rect(10, 10, 100,100);
  ctx.fill(path1)
  const path2 = new Path2D();
  path2.moveTo(220, 60);
  path2.arc(170, 60, 50, 0, 2 * Math.PI);
  ctx.stroke(path2)

  canvas.addEventListener('click', function (e) {
    console.log(ctx.isPointInPath(path1, e.clientX, e.clientY))
    console.log(ctx.isPointInPath(path2, e.clientX, e.clientY))
  })

如上圖所示,我們點擊了左邊圖形,打印true,false;點擊右邊圖形,打印false,true。打印的結果表明是沒有問題的,不過由于其兼容性還有待加強,所以目前建議還是使用重繪方式來監聽事件。

結語

Canvas的事件監聽講到這里基本就差不多了,原理很簡單,大家應該都能掌握。

github地址 歡迎start

附錄

自己寫的一個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中添加事件的方法的文章就介紹到這了,更多相關Canvas添加事件內容請搜索腳本之家以前的文章或繼續瀏覽下面的相關文章,希望大家以后多多支持腳本之家!

標簽:眉山 河池 崇左 北海 阜陽 營口 晉中 青海

巨人網絡通訊聲明:本文標題《詳解如何在Canvas中添加事件的方法》,本文關鍵詞  詳解,如,何在,Canvas,中,添加,;如發現本文內容存在版權問題,煩請提供相關信息告之我們,我們將及時溝通與處理。本站內容系統采集于網絡,涉及言論、版權與本站無關。
  • 相關文章
  • 下面列出與本文章《詳解如何在Canvas中添加事件的方法》相關的同類信息!
  • 本頁收集關于詳解如何在Canvas中添加事件的方法的相關信息資訊供網民參考!
  • 推薦文章
    婷婷综合国产,91蜜桃婷婷狠狠久久综合9色 ,九九九九九精品,国产综合av
    中文字幕一区视频| 国产精品成人免费| 欧美aa在线视频| 亚洲欧美电影一区二区| 亚洲精品国产精华液| 日韩伦理免费电影| 中文字幕一区二区三区在线不卡 | 欧美激情自拍偷拍| 日韩码欧中文字| 亚洲精品成人在线| 亚洲一区二区三区在线播放 | 欧美日韩精品系列| 欧美揉bbbbb揉bbbbb| 国内精品久久久久影院色| 欧美日韩亚洲高清一区二区| 91在线视频免费91| 91亚洲精品久久久蜜桃| 一区二区三区日韩欧美精品| 国产不卡在线播放| 天天爽夜夜爽夜夜爽精品视频| 国产精品色婷婷久久58| 国产乱妇无码大片在线观看| voyeur盗摄精品| 豆国产96在线|亚洲| 亚洲欧美日韩系列| 精品三级在线观看| 日本成人在线网站| 中文字幕一区在线观看| 欧美人与z0zoxxxx视频| 91亚洲国产成人精品一区二区三| 国产女同互慰高潮91漫画| 成人不卡免费av| 波多野结衣视频一区| 亚洲制服欧美中文字幕中文字幕| 一区二区三区欧美| 午夜精品福利在线| 91激情在线视频| 国产亚洲欧美日韩在线一区| 经典三级视频一区| 欧美成人一区二区三区在线观看| 国产乱妇无码大片在线观看| 国产精品人成在线观看免费| 成人免费高清在线观看| 一区二区在线观看视频| 亚洲va欧美va天堂v国产综合| 麻豆精品精品国产自在97香蕉| 欧美在线观看一区二区| 亚洲欧美日韩久久| 伦理电影国产精品| 激情综合色播五月| 欧美r级在线观看| 日韩国产精品91| 欧美日本一区二区三区四区| 成人精品免费视频| 欧美二区乱c少妇| 亚洲国产成人自拍| 蜜臀av性久久久久av蜜臀妖精| 国产精品久久久久影视| 亚洲人被黑人高潮完整版| 欧美亚洲一区二区三区四区| www.亚洲在线| 日韩伦理电影网| 视频精品一区二区| 久久久三级国产网站| 色综合久久六月婷婷中文字幕| 国产成人a级片| 国产婷婷一区二区| 日本大香伊一区二区三区| 国产精品一区二区x88av| 午夜天堂影视香蕉久久| 亚洲h在线观看| 丁香天五香天堂综合| 9191成人精品久久| 欧美一区二区三区成人| 成人黄色电影在线| 成人免费毛片aaaaa**| 日韩精品乱码av一区二区| 日本女优在线视频一区二区| 26uuu国产日韩综合| 国产乱一区二区| 91麻豆精品91久久久久同性| 一区二区三区成人在线视频| 99精品偷自拍| 91麻豆精品国产| 免费观看成人鲁鲁鲁鲁鲁视频| 欧美视频日韩视频在线观看| 亚洲综合视频网| 欧美午夜一区二区三区免费大片| 亚洲综合网站在线观看| 91成人在线精品| 亚洲一区二区三区四区在线免费观看 | 日日夜夜精品视频免费| 亚洲高清中文字幕| 久久av资源网| 国产亚洲福利社区一区| 国内成人精品2018免费看| 欧美成人综合网站| 成人三级在线视频| 国产日韩精品一区二区三区| av高清不卡在线| 亚洲综合av网| 日韩精品专区在线| 97se亚洲国产综合自在线观| 欧美激情一区二区三区| 欧美在线短视频| 国产精品综合网| 一区二区三区美女| 日韩欧美国产午夜精品| a4yy欧美一区二区三区| 亚洲国产精品成人久久综合一区| 色噜噜狠狠成人网p站| 精品一区二区三区在线播放视频| 国产精品成人午夜| 日韩欧美一区二区久久婷婷| 91美女精品福利| 六月丁香婷婷久久| 亚洲蜜臀av乱码久久精品蜜桃| 日韩一卡二卡三卡| 91成人免费电影| 国产精品白丝av| 香蕉成人伊视频在线观看| 中文一区一区三区高中清不卡| 欧美性生活久久| a级精品国产片在线观看| 极品美女销魂一区二区三区 | 成人aa视频在线观看| 极品瑜伽女神91| 亚洲夂夂婷婷色拍ww47| 久久精品一区二区三区av | 亚洲乱码中文字幕| 日韩午夜激情视频| 91成人看片片| 成人av免费在线| 粉嫩在线一区二区三区视频| 亚洲成在人线免费| 亚洲一区二区三区中文字幕在线| 欧美国产精品一区二区| 精品毛片乱码1区2区3区| 91精品办公室少妇高潮对白| 国产99精品在线观看| 激情图片小说一区| 韩国理伦片一区二区三区在线播放| 亚洲一区二区三区视频在线播放| 国产精品久久久久久妇女6080| 欧美变态tickling挠脚心| 欧美一区二区三区视频在线| 制服丝袜国产精品| 欧美一区二区三区影视| 欧美日本在线一区| 欧美三级电影精品| 欧美视频一区二区| 91麻豆精品国产91久久久资源速度| 欧美性xxxxxxxx| 欧美三区免费完整视频在线观看| 国产aⅴ综合色| 日韩成人av影视| 欧美大片在线观看一区二区| 成人午夜激情视频| 婷婷久久综合九色综合绿巨人| 精品盗摄一区二区三区| 91精品视频网| 欧美性xxxxx极品少妇| 色哟哟在线观看一区二区三区| 国产剧情在线观看一区二区| 久久精品噜噜噜成人av农村| 蜜臀av性久久久久av蜜臀妖精| 国产精品国产三级国产普通话三级| 亚洲精品免费一二三区| 亚洲欧洲美洲综合色网| 综合色中文字幕| 日韩有码一区二区三区| 日本视频在线一区| 激情文学综合网| 日本三级亚洲精品| 亚洲国产一区视频| 日本韩国精品在线| 国产精品99久久久久久久女警| 亚洲成a人在线观看| 一级女性全黄久久生活片免费| 久色婷婷小香蕉久久| 精品国产乱码久久久久久浪潮 | 国产激情91久久精品导航 | 欧美视频在线一区| 亚洲精品一线二线三线| 久久久91精品国产一区二区精品| 亚洲色图.com| 一区二区三区中文免费| 国产精品66部| 国产麻豆精品在线| 99久久精品国产一区二区三区| 99久久免费精品| 色美美综合视频| 欧美亚男人的天堂| 在线不卡免费欧美| 欧美伦理电影网| 成人美女视频在线观看| 国内久久精品视频| 国产高清久久久久| eeuss鲁一区二区三区| 欧美午夜精品久久久|