Como front-end, adicionar eventos a elementos é algo comum. Mas no Canvas, qualquer coisa desenhada nele é inalcançável, muito menos adicionar eventos, então estamos impotentes quanto a isso? Claro que não! Devemos ter usado muitas estruturas do Canvas em nossos projetos diários. Descobrimos que os eventos foram usados com muita maturidade nessas estruturas e não houve problemas particularmente sérios. Então o que podemos ter certeza é que eventos não são algo intocável no Canvas.
abordagem de um toloTodos nós sabemos que quando um elemento aciona um evento, a posição do mouse está basicamente acima do elemento, então naturalmente pensamos em comparar a posição atual do mouse com a posição ocupada pelo objeto, para que possamos obter se o objeto deve acionar o evento. Esse método é relativamente simples, então não usarei código para demonstrá-lo, mas como o chamo de método tolo, é óbvio que não é uma solução eficaz. Como a posição ocupada por um objeto não é necessariamente muito fácil de obter se for um retângulo, círculo, etc., também podemos obter sua posição através de algumas fórmulas simples porém em polígonos com pontos complexos, ou mesmo alguns lados de. os polígonos são Curvos, obviamente, é extremamente complicado e difícil para nós obtermos a posição que ocupa neste momento, portanto este método só é adequado para uso em algumas demonstrações, e não é adequado para a maioria das Condições.
uma maneira mais inteligenteComo o método acima atingiu um muro, só podemos encontrar outro caminho. Ao navegar no CanvasAPI, encontrei um método isPointInPath, que parece ser o remédio que procuramos.
Apresentando isPointInPathA função de isPointInPath: Como o nome sugere, podemos saber intuitivamente que este método é usado para determinar se um ponto está em um caminho.
Os parâmetros de entrada e saída de isPointInPath são: ctx.isPointInPath([path, ]x, y [, fillRule]). Este método possui 4 parâmetros, dos quais path e fillRule são opcionais e x e y são obrigatórios. Apresentamos os 4 parâmetros sucessivamente.
path: Quando vi esse parâmetro, inicialmente pensei que fosse o valor de retorno de BeginPath ou closePath. Infelizmente, esses dois métodos não retornaram um valor. Depois de verificar as informações, descobri que era o objeto do novo construtor Path2D. Uso específico do construtor Path2D. No entanto, é uma pena que este método possa ser devido a problemas de compatibilidade. Atualmente, algumas estruturas de código aberto não foram utilizadas.
x, y: Esses dois parâmetros são fáceis de entender, são a distância entre o eixo x e o eixo y. Deve-se observar que sua posição relativa é o canto superior esquerdo do Canvas.
fillRule: diferente de zero (padrão), par ímpar. A regra de quebra automática e a regra ímpar-par são regras em gráficos para determinar se um ponto está dentro de um polígono. A regra de quebra automática é a regra padrão do Canvas. Se quiser saber mais sobre essas duas regras, você mesmo pode conferir as informações, por isso não vou apresentá-las aqui.
Depois de introduzir os parâmetros de entrada acima, todos provavelmente poderão adivinhar os parâmetros de saída do método isPointInPath, que são verdadeiros e falsos.
Usando isPointInPathDepois de apresentar o método isPointInPath na seção anterior, vamos usá-lo agora.
Vamos começar com uma demonstração simples:
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= 'preto' 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)) })
Conforme mostrado na figura, a parte cinza é a área ocupada pelo Canvas, e a parte preta é a área onde realmente adicionamos o evento. Depois de clicarmos na área preta, ele realmente faz o que queremos, e o valor impresso. é verdade. Parece que o monitoramento de eventos do Canvas pode ser resolvido de forma simples, mas será que é realmente tão simples assim? Obviamente impossível! Vejamos outro exemplo. Existem duas áreas neste momento e precisamos vincular eventos diferentes a elas:
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= 'preto' ctx.fill() ctx.closePath() ctx.beginPath() ctx.moveTo(100, 100) ctx.lineTo(100, 150) ctx.lineTo(150, 150) ctx.lineTo(150, 100) ctx.fillStyle = 'vermelho' 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)) })
Neste momento, o resultado não é mais o que esperávamos. Quando a área preta é clicada, o valor impresso é falso, e quando a área vermelha é clicada, o valor impresso é verdadeiro.
Na verdade, o motivo é muito simples. Por causa do código acima, criamos dois caminhos, e o método isPointInPath detecta apenas se o ponto atual está no último caminho. portanto, somente quando a área vermelha é clicada, o método isPointInPath pode ser considerado verdadeiro. Agora vamos transformar o código:
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= 'preto' ctx.fill() } function draw2 () { ctx.beginPath() ctx.moveTo(100, 100) ctx.lineTo(100, 150) ctx.lineTo(150, 150) ctx.lineTo (150, 100) ctx.fillStyle= 'vermelho' 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)) }) })
Fizemos uma grande modificação no código acima. Colocamos cada Path em uma função separada e os colocamos em um array. Quando o evento click é acionado, limpamos o Canvas, percorremos o array e redesenhamos, e fazemos um julgamento toda vez que um Path é desenhado. Portanto, ao chamar o método isPointInPath, podemos obter o último Path atual em tempo real, e então. julgar o ponto atual entre o caminho.
Agora implementamos indiretamente o monitoramento de eventos separados para cada caminho, mas a forma como ele é implementado requer redesenhar continuamente. Então, existe uma maneira de monitorar eventos sem redesenhar?
Em primeiro lugar, precisamos saber que o motivo para redesenhar repetidamente é porque o método isPointInPath é o último Path a ser monitorado. No entanto, quando introduzimos esse método, dissemos que seu primeiro parâmetro é um objeto Path. passe este parâmetro, Path não usará mais o último Path, mas usará o Path que passamos. Agora vamos fazer uma demonstração para verificar sua viabilidade:
const canvas = document.getElementById('canvas') const ctx = canvas.getContext('2d') const path1 = new Path2D(); novo Path2D(); path2.moveTo(220, 60); 60, 50, 0, 2 * Math.PI); ) console.log(ctx.isPointInPath(path2, e.clientX, e.clientY)) })
Conforme mostrado na imagem acima, clicamos no gráfico esquerdo e imprimimos verdadeiro e falso; clicamos no gráfico direito e imprimimos falso e verdadeiro; Os resultados impressos mostram que não há problema, mas como sua compatibilidade precisa ser melhorada, atualmente é recomendado utilizar o método redraw para escutar eventos.
ConclusãoO monitoramento de eventos do Canvas é basicamente abordado aqui. O princípio é muito simples e todos devem ser capazes de dominá-lo.
endereço do github, bem-vindo para começar
apêndiceUma demonstração escrita por mim
const canvas = document.getElementById ('canvas') class retangular { construtor (ctx, { top = 0, left = 0, width = 30, height = 50, background = 'red' } ) { this.ctx = ctx this. top = top this.left = left this.width = largura this.height = altura this.background = fundo } pintura () { 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() } ajustar (esquerda, topo) { this.left += left this.top += top } } class círculo { construtor (ctx, { center = [], radius = 10, background = 'blue' } ) { this.ctx = ctx this.center = [center[0] === indefinido? raio: centro[0], centro[1] === raio indefinido: centro[1]] this.radius = raio this.background = fundo } pintura () { 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() } ajustar (esquerda, topo) { this.center[0] += esquerda this.center[1] += topo } } classe demo { construtor (canvas) { this.canvasInfo = canvas.getBoundingClientRect() this.renderList = [] this.ctx = canvas.getContext('2d') this.canvas = canvas this.rectangular = (config) => { let target = novo retangular(this.ctx, {...config}) this.addRenderList(target) retorne isto } this.circle = (config) => { let target = new círculo(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) } pintura () { 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 ChooseIndex = null this.renderList.forEach((it, índice) => { 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() }) 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 sim = nova demonstração(canvas) .rectangular({}) .rectangular({topo: 60, esquerda: 60, fundo: 'azul'}) .rectangular({topo: 30, esquerda: 20, fundo: 'verde'}) .circle() .circle ({centro: [100, 30], fundo: 'vermelho', raio: 5}) .painting()
O texto acima é todo o conteúdo deste artigo. Espero que seja útil para o estudo de todos. Também espero que todos apoiem a Rede VeVb Wulin.