Dans un projet récent, je souhaite implémenter une planche à dessin de style pixel, dans laquelle de petites grilles de pixels peuvent être effacées, les sélections de cadres changent de couleur et divers graphiques peuvent être effacés. Un si petit projet peut sembler simple, mais il contient beaucoup de choses. des choses.
Dessiner une grille de pixelsDéfinissons d’abord la classe de grille de pixels
Pixel = fonction (option) { this.x = option.x; this.y = option.y; this.shape = option.shape; this.size = option.size ||
x et y représentent les coordonnées du point central. C'est ce que j'ai fait au début. Définissez d'abord le chemin.
createPath : function (ctx) {if (this.shape === 'circle') {this.createCircle(ctx);} else if (this.shape === 'rect') {this.createRect(ctx);} else {this.createCircle(ctx);}},createCircle : function (ctx) {var radius = this.size / 2;ctx.arc(this.x,this.y,radius,0,Math.PI*2);},createRect : function (ctx) {var points = this.getPoints(); , i) { ctx[i == 0 ? 'moveTo' : 'lineTo'](point.x, point.y }) ctx.lineTo(points[0].x, points[0].y);},
La grille de pixels prend en charge les cercles et les rectangles. Une fois le chemin défini, il est ensuite dessiné.
draw : function (ctx) {ctx.save();ctx.lineWidth=this.lineWidth;ctx.StrokeStyle=this.StrokeStyle;ctx.fillStyle=this.fillStyle;ctx.beginPath();this.createPath(ctx); ctx.stroke();if(this.isFill){ctx.fill();}ctx.restore();}
Créez ensuite la grille de pixels par lots via une boucle :
pour (var i = stepX + .5; i < canvas.width; i+=stepX) {for (var j = stepY + .5; j < canvas.height; j+=stepY) {var pixel = new Pixel({x : i,y: j,forme: 'cercle'})box.push(pixel);pixel.draw(ctx);}}
Cela semble parfait, mais il y a un énorme inconvénient. Chaque fois qu'un pixel est dessiné, il est ramené au contexte et l'état du canevas est modifié à chaque fois, ce qui entraînera de mauvaises performances de rendu car il y en a beaucoup. pixels. Si le canevas est relativement grand, les performances sont très préoccupantes et il y a certaines opérations sur la planche à dessin. Il est inapproprié de changer l'état du canevas si fréquemment.
Par conséquent, l'approche correcte est la suivante : nous devons définir tous les chemins, et il est préférable de les dessiner dans le canevas par lots à la fois ;
//Définir la position du pixel pour (var i = stepX + .5; i < canvas.width; i+=stepX) {for (var j = stepY + .5; j < canvas.height; j+=stepY) { var pixel = nouveau Pixel ({x : je, y : j, forme : 'circle'})box.push(pixel);}}//Dessin par lots console.time('time');ctx.beginPath();for (var c = 0; c < box.length; c++) {var cercle = boîte[c];ctx.moveTo(cercle.x + 3, circle.y);circle.createPath(ctx);}ctx.closePath();ctx.Stroke();console.timeEnd('time');
Vous pouvez voir que cette efficacité de rendu est très rapide, et l'état du canevas est modifié le moins possible, car chaque fois que l'état du contexte est modifié, le canevas sera redessiné, et cet état est un état global.
Interaction avec la grille de pixelsL'exigence du projet est que les pixels puissent être effacés en appuyant et en déplaçant la souris sur le canevas. Cela contient deux points de connaissance, l'un est de savoir comment obtenir la grille de pixels sur le chemin de déplacement de la souris, et le second concerne les problèmes de performances, en raison de. nos besoins L'exigence est de dessiner 80 000 points, sans parler de rien d'autre, le simple bouclage prendra des dizaines ou des centaines de millisecondes, sans parler du dessin et du rendu. Examinons d'abord la première question :
Obtenez la grille sous le chemin de déplacement de la sourisVoyant ce problème, nous pouvons facilement penser à écrire une fonction pour obtenir la position de la souris à travers la position de la souris, qui contient la grille, puis à réactualiser le calcul de la position à chaque déplacement. peut être réalisé, mais si la souris passe dessus, il est impossible de le faire rapidement. La position de chaque point peut être calculée et l'effet sera incohérent. Changeons notre façon de penser. Nous pouvons clairement connaître les points de départ et d'arrivée du chemin parcouru par la souris. Nous imaginons l'ensemble du chemin de dessin comme un segment de ligne. Le problème devient alors un algorithme pour croiser le segment de ligne avec la ligne d'origine. segment est L'épaisseur du pinceau et le chemin parcouru par le segment de ligne sont les chemins du mouvement de la souris, et les cercles qui les croisent sont les grilles qui doivent être modifiées. La conversion en code est la suivante :
function sqr(x) { return x * x } function dist2(p1, p2) { return sqr(p1.x - p2.x) + sqr(p1.y - p2.y) } function distToSegmentSquared(p, v, w ) { var l2 = dist2(v, w); si (l2 == 0) renvoie dist2(p, v); ((px - vx) * (wx - vx) + (py - vy) * (wy - vy)) / l2; si (t < 0) renvoie dist2(p, v) ; (p, w); return dist2(p, { x : vx + t * (wx - vx), y : vy + t * (wy - vy) } }/** * @description Calculer si le segment de droite coupe le cercle* @param {x: num, y: num} p point central du cercle* @param {x: num, y: num} v point de départ du segment de droite* @param {x : num, y: num } w point final du segment de ligne*/ function distToSegment(p, v, w) { var offset = pathHeight; var minX = Math.min(vx, wx) - offset; var maxX = Math.max(vx, wx) + offset; var minY = Math.min(vy, wy) - offset; var maxY = Math.max(vy, wy) + offset si ((px < minX; || px > maxX) && (py < minY || py > maxY)) { return Number.MAX_VALUE } return Math.sqrt(distToSegmentSquared(p, v, w)); }
La logique spécifique ne sera pas élaborée. Les lecteurs peuvent lire le code par eux-mêmes. Ensuite, en obtenant la grille qui se croise, supprimez les données dans la boîte et effectuez un nouveau rendu, vous pouvez voir l'effet.
De la même manière, nous pouvons créer un effet de teinture, puis implémenter une petite démo de la planche à dessin en pixels sur toile. Cependant, pour créer un effet de teinture, vous devez utiliser la première méthode de dessin. Chaque pixel doit être un objet, car l'état de chaque objet est indépendant. Cependant, il n'y a pas lieu de s'inquiéter des performances. il n'y aura pratiquement aucune sensation de décalage. L’effet est à peu près le suivant :
J'ai été un peu paresseux ces derniers temps, donc je vais laisser ça comme ça pour le moment, j'aurai le temps d'ajouter une fonction pour télécharger des images, pixeliser des images et exporter des fonctions plus tard.
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.