Als Frontend ist das Hinzufügen von Ereignissen zu Elementen üblich. Aber in Canvas ist alles, was darauf gezeichnet wird, nicht erreichbar, geschweige denn das Hinzufügen von Ereignissen. Sind wir also hilflos? Natürlich nicht! Wir müssen in unseren täglichen Projekten viele Canvas-Frameworks verwendet haben. Wir haben festgestellt, dass Ereignisse in diesen Frameworks sehr ausgereift verwendet wurden und es keine besonders schwerwiegenden Probleme gab. Wir können also sicher sein, dass Ereignisse in Canvas keine unantastbare Sache sind.
ein dummer AnsatzWir alle wissen, dass sich die Position der Maus grundsätzlich über dem Element befindet, wenn ein Element ein Ereignis auslöst. Daher denken wir natürlich darüber nach, die aktuelle Mausposition mit der Position zu vergleichen, die das Objekt einnimmt, damit wir die Position des Objekts ermitteln können das Ereignis auslösen. Diese Methode ist relativ einfach, daher werde ich sie nicht mit Code demonstrieren, aber da ich sie als Narrenmethode bezeichne, ist es offensichtlich, dass sie keine effektive Lösung ist. Da es nicht unbedingt einfach ist, die Position eines Objekts zu ermitteln, wenn es sich um ein Rechteck, einen Kreis usw. handelt, können wir seine Position jedoch auch bei Polygonen mit komplexen Punkten oder sogar einigen Seiten ermitteln Das Polygon ist gekrümmt. Offensichtlich ist es für uns äußerst kompliziert und schwierig, die Position zu ermitteln, die es zu diesem Zeitpunkt einnimmt. Daher eignet sich diese Methode nur für die Verwendung in einigen Demos und ist für die meisten Bedingungen nicht geeignet.
eine intelligentere Art und WeiseDa die obige Methode an ihre Grenzen stößt, können wir nur einen anderen Weg finden. Beim Durchsuchen der CanvasAPI habe ich die Methode isPointInPath gefunden, die die Medizin zu sein scheint, nach der wir suchen.
Wir stellen vor: isPointInPathDie Rolle von isPointInPath: Wie der Name schon sagt, können wir intuitiv erkennen, dass diese Methode verwendet wird, um zu bestimmen, ob sich ein Punkt auf einem Pfad befindet.
Die Eingabe- und Ausgabeparameter von isPointInPath sind: ctx.isPointInPath([path, ]x, y [, fillRule]). Diese Methode verfügt über 4 Parameter, von denen path und fillRule optional sind und x und y erforderlich sind. Wir führen der Reihe nach die 4 Parameter ein.
path: Als ich diesen Parameter sah, dachte ich zunächst, es sei der Rückgabewert von beginPath oder closePath. Leider gaben diese beiden Methoden keinen Wert zurück. Nach Überprüfung der Informationen stellte ich fest, dass es sich um das Objekt des neuen Path2D-Konstruktors handelte. Spezifische Verwendung des Path2D-Konstruktors. Es ist jedoch bedauerlich, dass diese Methode möglicherweise auf Kompatibilitätsprobleme zurückzuführen ist. Derzeit wurden einige Open-Source-Frameworks nicht verwendet.
x, y: Diese beiden Parameter sind leicht zu verstehen, sie sind der Abstand zwischen der x-Achse und der y-Achse. Es ist zu beachten, dass ihre relative Position die obere linke Ecke der Leinwand ist.
fillRule: ungleich Null (Standard), gerade, ungerade. Die Nicht-Null-Umbruchregel und die Ungerade-Gerade-Regel sind Regeln in Grafiken, um zu bestimmen, ob sich ein Punkt innerhalb eines Polygons befindet. Die Nicht-Null-Umbruchregel ist die Standardregel von Canvas. Wenn Sie mehr über diese beiden Regeln erfahren möchten, können Sie die Informationen selbst überprüfen, daher werde ich sie hier nicht vorstellen.
Nach der Einführung der obigen Eingabeparameter kann wahrscheinlich jeder die Ausgabeparameter der isPointInPath-Methode erraten, die wahr und falsch sind.
Verwenden von isPointInPathNachdem wir im vorherigen Abschnitt die Methode isPointInPath vorgestellt haben, verwenden wir sie jetzt.
Beginnen wir mit einer einfachen 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)) })
Wie in der Abbildung gezeigt, ist der graue Teil der Bereich, der von der Leinwand eingenommen wird, und der schwarze Teil ist der Bereich, in dem wir das Ereignis tatsächlich hinzugefügt haben. Nachdem wir auf den schwarzen Bereich geklickt haben, führt es tatsächlich das aus, was wir wollen, und zwar den gedruckten Wert ist wahr. Es scheint, dass die Canvas-Ereignisüberwachung einfach gelöst werden kann, aber ist das wirklich so einfach? Offensichtlich unmöglich! Nehmen wir ein anderes Beispiel. Derzeit gibt es zwei Bereiche, mit denen wir verschiedene Ereignisse verknüpfen müssen:
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 = 'rot' 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)) })
Zu diesem Zeitpunkt entspricht das Ergebnis nicht mehr unseren Erwartungen. Wenn Sie auf den schwarzen Bereich klicken, ist der gedruckte Wert falsch, und wenn Sie auf den roten Bereich klicken, ist der gedruckte Wert wahr.
Tatsächlich ist der Grund sehr einfach. Aufgrund des obigen Codes haben wir tatsächlich zwei Pfade erstellt, und die Methode isPointInPath erkennt tatsächlich nur, ob sich der aktuelle Punkt im letzten Pfad befindet. Daher kann die isPointInPath-Methode nur dann als wahr beurteilt werden, wenn auf den roten Bereich geklickt wird. Lassen Sie uns nun den Code umwandeln:
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)) }) })
Wir haben eine große Änderung am obigen Code vorgenommen. Wir haben jeden Pfad in eine separate Funktion eingefügt und sie in ein Array verschoben. Wenn das Klickereignis ausgelöst wird, löschen wir die Leinwand, durchlaufen das Array und zeichnen neu und fällen jedes Mal, wenn ein Pfad gezeichnet wird, eine Beurteilung. Daher können wir beim Aufrufen der isPointInPath-Methode den letzten aktuellen Pfad in Echtzeit abrufen Beurteilen Sie den aktuellen Punkt unter dem Pfad.
Jetzt haben wir indirekt eine separate Ereignisüberwachung für jeden Pfad implementiert, aber die Art und Weise, wie sie implementiert wird, erfordert immer wieder ein Neuzeichnen. Gibt es also eine Möglichkeit, Ereignisse ohne Neuzeichnen zu überwachen?
Zunächst müssen wir wissen, dass der Grund für das erneute Zeichnen darin besteht, dass die isPointInPath-Methode der letzte zu überwachende Pfad ist. Als wir diese Methode jedoch einführten, sagten wir, dass ihr erster Parameter ein Path-Objekt ist Wenn Sie diesen Parameter übergeben, verwendet Path nicht mehr den letzten Pfad, sondern den von uns übergebenen Pfad. Lassen Sie uns nun eine Demo durchführen, um die Machbarkeit zu überprüfen:
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); 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)) })
Wie im Bild oben gezeigt, haben wir auf die linke Grafik geklickt und „true“ und „false“ gedruckt; wir haben auf die rechte Grafik geklickt und „false“ und „true“ gedruckt. Die gedruckten Ergebnisse zeigen, dass kein Problem vorliegt. Da jedoch die Kompatibilität verbessert werden muss, wird derzeit empfohlen, die Neuzeichnungsmethode zum Abhören von Ereignissen zu verwenden.
AbschlussDie Ereignisüberwachung von Canvas wird hier grundsätzlich behandelt. Das Prinzip ist sehr einfach und sollte von jedem beherrscht werden.
Github-Adresse, willkommen zum Start
AnhangEine von mir selbst geschriebene Demo
const canvas = document.getElementById('canvas') class rechteckig { Konstruktor ( ctx, { oben = 0, links = 0, Breite = 30, Höhe = 50, Hintergrund = 'rot' } ) { this.ctx = ctx this. top = oben this.left = links this.width = Breite this.height = Höhe this.background = Hintergrund } 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 { constructionor ( ctx, { center = [], radius = 10, background = 'blue' } ) { this.ctx = ctx this.center = [center[0] === undefiniert ? Radius : center[0], center[1] === undefiniert ? radius : center[1]] this.radius = radius this.background = Hintergrund } Malerei () { 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 { Konstruktor (canvas) { this.canvasInfo = canvas.getBoundingClientRect() this.renderList = [] this.ctx = canvas.getContext('2d') this.canvas = Canvas this.rectangular = (config) => { let target = new rechteckig(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 selectedIndex = null this.renderList.forEach((it, index) => { it.painting() if (this.ctx.isPointInPath(startX, startY)) { selectedIndex = 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({oben: 60, links: 60, Hintergrund: 'blau'}) .rectangular({oben: 30, links: 20, Hintergrund: 'grün'}) .circle() .circle ({center: [100, 30], Hintergrund: 'rot', Radius: 5}) .painting()
Das Obige ist der gesamte Inhalt dieses Artikels. Ich hoffe, dass er für das Studium aller hilfreich ist. Ich hoffe auch, dass jeder das VeVb Wulin Network unterstützt.