En tant que frontal, l'ajout d'événements aux éléments est une chose courante. Mais dans Canvas, tout ce qui est dessiné dessus est impossible à obtenir, encore moins l'ajout d'événements, alors sommes-nous impuissants face à cela ? Bien sûr que non! Nous avons dû utiliser de nombreux frameworks Canvas dans nos projets quotidiens. Nous avons constaté que les événements ont été utilisés de manière très mature dans ces frameworks et qu'il n'y a pas eu de problèmes particulièrement graves. Ce dont nous pouvons donc être sûrs, c'est que les événements ne sont pas une chose intouchable dans Canvas.
une approche idioteNous savons tous que lorsqu'un élément déclenche un événement, la position de la souris est fondamentalement au-dessus de l'élément, nous pensons donc naturellement à comparer la position actuelle de la souris avec la position occupée par l'objet, afin de pouvoir déterminer si l'objet doit déclencher l'événement. événement. Cette méthode est relativement simple, je n'utiliserai donc pas de code pour la démontrer, mais comme je la qualifie de méthode idiote, il est évident que ce n'est pas une solution efficace. Car la position occupée par un objet n'est pas forcément très simple à obtenir. S'il s'agit d'un rectangle, d'un cercle, etc., on peut aussi obtenir sa position grâce à quelques formules simples cependant, dans des polygones comportant des points complexes, voire certains côtés de. les polygones sont courbes, évidemment, il est extrêmement compliqué et difficile pour nous d'obtenir la position qu'il occupe à ce moment, donc cette méthode ne convient que pour une utilisation dans certaines démos, et ne convient pas à la plupart des conditions.
une manière plus intelligentePuisque la méthode ci-dessus s’est heurtée à un mur, nous ne pouvons trouver qu’un autre moyen. En parcourant CanvasAPI, j'ai trouvé une méthode isPointInPath, qui semble être le médicament que nous recherchons.
Présentation d'isPointInPathLe rôle de isPointInPath : Comme son nom l’indique, on peut savoir intuitivement que cette méthode est utilisée pour déterminer si un point se trouve dans un chemin.
Les paramètres d'entrée et de sortie de isPointInPath sont : ctx.isPointInPath([path, ]x, y [, fillRule]). Cette méthode a 4 paramètres, dont path et fillRule sont facultatifs, et x et y sont obligatoires. Nous introduisons tour à tour les 4 paramètres.
path : Lorsque j'ai vu ce paramètre, j'ai d'abord pensé qu'il s'agissait de la valeur de retour de beginPath ou closePath. Malheureusement, ces deux méthodes n'ont renvoyé aucune valeur. Après avoir vérifié les informations, j'ai découvert qu'il s'agissait de l'objet du nouveau constructeur Path2D. Utilisation spécifique du constructeur Path2D. Il est cependant dommage que cette méthode puisse être due à des problèmes de compatibilité. Actuellement, certains frameworks open source ne sont pas utilisés.
x, y : Ces deux paramètres sont faciles à comprendre, ce sont la distance entre l'axe x et l'axe y. Il est à noter que leur position relative est le coin supérieur gauche du Canvas.
fillRule : différent de zéro (par défaut), pair impair. La règle d'enveloppement non nulle et la règle impaire-pair sont des règles graphiques permettant de déterminer si un point se trouve dans un polygone. La règle d'enveloppement non nulle est la règle par défaut de Canvas. Si vous souhaitez en savoir plus sur ces deux règles, vous pouvez vérifier les informations vous-même, je ne les présenterai donc pas ici.
Après avoir introduit les paramètres d'entrée ci-dessus, tout le monde peut probablement deviner les paramètres de sortie de la méthode isPointInPath, qui sont vrais et faux.
Utiliser isPointInPathAprès avoir présenté la méthode isPointInPath dans la section précédente, utilisons-la maintenant.
Commençons par une démo simple :
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= 'noir' 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)) })Comme le montre la figure, la partie grise est la zone occupée par le canevas et la partie noire est la zone où nous avons réellement ajouté l'événement. Après avoir cliqué sur la zone noire, elle fait réellement ce que nous voulons et la valeur imprimée. est vrai. Il semble que la surveillance des événements Canvas puisse être résolue simplement, mais est-ce vraiment aussi simple ? Evidemment impossible ! Prenons un autre exemple. Il existe actuellement deux zones auxquelles nous devons lier différents événements :
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= 'noir' ctx.fill() ctx.closePath() ctx.beginPath() ctx.moveTo(100, 100) ctx.lineTo(100, 150) ctx.lineTo(150, 150) ctx.lineTo(150, 100) ctx.fillStyle = 'rouge' 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)) })À ce stade, le résultat n'est plus celui attendu lorsque l'on clique sur la zone noire, la valeur imprimée est fausse, et lorsque l'on clique sur la zone rouge, la valeur imprimée est vraie.
En fait, la raison est très simple. En raison du code ci-dessus, nous avons en fait créé deux chemins, et la méthode isPointInPath détecte en fait uniquement si le point actuel se trouve dans le dernier chemin. Dans l'exemple, la zone rouge est le dernier chemin. Ainsi, ce n'est que lorsque l'on clique sur la zone rouge que la méthode isPointInPath peut être jugée comme vraie. Transformons maintenant le code :
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= 'noir' ctx.fill() } function draw2 () { ctx.beginPath() ctx.moveTo(100, 100) ctx.lineTo(100, 150) ctx.lineTo(150, 150) ctx.lineTo (150, 100) ctx.fillStyle= 'rouge' 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)) }) })Nous avons apporté une modification importante au code ci-dessus. Nous plaçons chaque chemin dans une fonction distincte et les plaçons dans un tableau. Lorsque l'événement click est déclenché, nous effaçons le canevas, parcourons le tableau et redessinons, et faisons un jugement à chaque fois qu'un chemin est dessiné. Par conséquent, lors de l'appel de la méthode isPointInPath, nous pouvons obtenir le dernier chemin actuel en temps réel, puis. juger le point actuel parmi le chemin.
Maintenant, nous avons indirectement implémenté une surveillance des événements distincte pour chaque chemin, mais la façon dont elle est implémentée nécessite de redessiner encore et encore. Existe-t-il donc un moyen de surveiller les événements sans redessiner ?
Tout d'abord, nous devons savoir que la raison de redessiner encore et encore est que la méthode isPointInPath est le dernier Path à surveiller. Cependant, lorsque nous avons introduit cette méthode, nous avons dit que son premier paramètre est un objet Path. passez ce paramètre, Path ne prendra plus le dernier Path mais utilisera le Path que nous avons transmis. Faisons maintenant une démo pour vérifier sa faisabilité :
const canvas = document.getElementById('canvas') const ctx = canvas.getContext('2d') const path1 = new Path2D(); nouveau Path2D(); chemin2.moveTo(220, 60); chemin2.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)) })Comme le montre l'image ci-dessus, nous avons cliqué sur le graphique de gauche et imprimé vrai et faux ; cliqué sur le graphique de droite et imprimé faux et vrai. Les résultats imprimés montrent qu'il n'y a pas de problème, mais comme sa compatibilité doit être améliorée, il est actuellement recommandé d'utiliser la méthode redraw pour écouter les événements.
ConclusionLa surveillance des événements de Canvas est essentiellement abordée ici. Le principe est très simple et tout le monde devrait pouvoir le maîtriser.
adresse github, bienvenue pour commencer
appendiceUne démo écrite par moi-même
const canvas = document.getElementById('canvas') class rectangulaire { constructeur ( ctx, { top = 0, left = 0, width = 30, height = 50, background = 'red' } ) { this.ctx = ctx this. top = haut this.left = gauche this.width = largeur this.height = hauteur 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() } ajuster (gauche, haut) { this.left += left this.top += top } } cercle de classe { constructeur ( ctx, { center = [], radius = 10, background = 'blue' } ) { this.ctx = ctx this.center = [center[0] === rayon non défini : centre[0], centre[1] === rayon non défini : centre[1]] this.radius = rayon this.background = background } peinture () { 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() } ajuster (gauche, haut) { this.center[0] += gauche this.center[1] += haut } } démo de classe { constructeur (canvas) { this.canvasInfo = canvas.getBoundingClientRect() this.renderList = [] this.ctx = canvas.getContext('2d') this.canvas = canvas this.rectangular = (config) => { let target = new rectangle(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)) {choseIndex = 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 oui = nouvelle démo (canvas) .rectangular({}) .rectangular({haut : 60, gauche : 60, arrière-plan : 'bleu'}) .rectangular({haut : 30, gauche : 20, arrière-plan : 'vert'}) .circle() .circle ({centre : [100, 30], arrière-plan : 'rouge', rayon : 5}) .painting()Ce qui précède représente l’intégralité du contenu de cet article. J’espère qu’il sera utile à l’étude de chacun. J’espère également que tout le monde soutiendra le réseau VeVb Wulin.