In einem aktuellen Projekt möchte ich ein Zeichenbrett im Pixelstil implementieren, auf dem kleine Pixelraster gelöscht, Rahmenauswahlen ihre Farbe geändert und verschiedene Grafiken gelöscht werden können. Ein so kleines Projekt mag einfach erscheinen, enthält aber viel Dinge.
Zeichnen Sie ein PixelrasterDefinieren wir zunächst die Pixelrasterklasse
Pixel = function (option) { this.x = option.x; this.y = option.y; this.size = option.size 8;}
x und y stellen die Mittelpunktkoordinaten dar. Dies habe ich am Anfang getan.
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 point = this.getPoints(); points.forEach(function (point , i) { ctx[i == 0 ? 'moveTo' : 'lineTo'](point.x, point.y }) ctx.lineTo(points[0].x, Punkte[0].y);},
Das Pixelraster unterstützt Kreise und Rechtecke. Nachdem der Pfad definiert wurde, wird er gezeichnet.
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();}
Dann erstellen Sie das Pixelraster stapelweise über eine Schleife:
for (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,shape: 'circle'})box.push(pixel);pixel.draw(ctx);}}
Dies scheint perfekt zu sein, aber es gibt einen großen Nachteil: Jedes Mal, wenn ein Pixel gezeichnet wird, wird er in den Kontext zurückgezogen und der Zustand der Leinwand wird jedes Mal geändert. Dies führt zu einer schlechten Renderleistung, da es viele gibt Wenn die Leinwand relativ groß ist, ist die Leistung sehr besorgniserregend und es ist unangemessen, den Status der Leinwand so häufig zu ändern.
Daher ist der richtige Ansatz: Wir sollten alle Pfade definieren und sie am besten stapelweise auf einmal in die Leinwand zeichnen.
//Definieren Sie die Position des Pixels for (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,shape: 'circle'})box.push(pixel);}}//Batch-Zeichnung console.time('time');ctx.beginPath();for (var c = 0; c < box.length; c++) {var Kreis = Box[c];ctx.moveTo(Kreis.x + 3, Circle.y);circle.createPath(ctx);}ctx.closePath();ctx.Stroke();console.timeEnd('time');
Sie können sehen, dass diese Rendering-Effizienz sehr schnell ist und der Status der Leinwand so wenig wie möglich geändert wird, da die Leinwand jedes Mal neu gezeichnet wird, wenn sich der Status des Kontexts ändert, und dieser Status ein globaler Status ist.
Interaktion mit dem PixelrasterDie Anforderung des Projekts besteht darin, dass Pixel durch Drücken und Bewegen der Maus auf der Leinwand gelöscht werden können. Dies beinhaltet zwei Wissenspunkte: Zum einen, wie man das Pixelraster auf dem Mausbewegungspfad erhält, und zum anderen, weil es Leistungsprobleme gibt Unsere Anforderungen bestehen darin, 80.000 Punkte zu zeichnen. Ganz zu schweigen davon, dass das Schleifen allein Dutzende oder Hunderte von Millisekunden dauern wird, ganz zu schweigen vom Zeichnen und Rendern. Schauen wir uns zunächst die erste Frage an:
Holen Sie sich das Raster unter den MausbewegungspfadAngesichts dieses Problems können wir uns leicht vorstellen, eine Funktion zu schreiben, um die Position der Maus anhand der Position der Maus zu ermitteln, die das Raster enthält, und dann die Positionsberechnung bei jeder Bewegung erneut zu aktualisieren kann erfüllt werden, aber wenn die Maus darüber bewegt wird, ist dies nicht schnell möglich. Die Position jedes Punkts kann berechnet werden, und der Effekt ist inkonsistent. Lassen Sie uns unser Denken ändern. Wir können uns den gesamten Zeichenpfad als Liniensegment vorstellen Segment ist Die Dicke des Pinsels und der Pfad, den das Liniensegment durchläuft, sind die Pfade der Mausbewegung, und die Kreise, die sie schneiden, sind die Gitter, die geändert werden müssen. In Code umgewandelt ist wie folgt:
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); if (l2 == 0) return dist2(p, v); ((px - vx) * (wx - vx) + (py - vy) * (wy - vy)) / l2; if (t < 0) return dist2(p, v); if (t > 1) return dist2 (p, w); return dist2(p, { x: vx + t * (wx - vx), y: vy + t * (wy - vy) }/** * @description Berechnen Sie, ob das Liniensegment den Kreis schneidet* @param {x: num, y: num} p Kreismittelpunkt* @param {x: num, y: num} v Startpunkt des Liniensegments* @param {x : num, y: num } w Endpunkt des Liniensegments*/ 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; if ((px < minX ||. px > maxX) && (py < minY || py > maxY)) { return Number.MAX_VALUE } return Math.sqrt(distToSegmentSquared(p, v, w));
Die spezifische Logik wird nicht näher erläutert. Leser können den Code selbst lesen. Wenn Sie dann das Schnittgitter abrufen, die Daten im Feld löschen und erneut rendern, können Sie den Effekt sehen.
Auf die gleiche Weise können wir einen Färbeeffekt erzeugen und dann eine kleine Demo des Canvas-Pixel-Zeichenbretts implementieren. Um einen Färbeeffekt zu erzielen, müssen Sie jedoch die erste Zeichenmethode verwenden, da der Status jedes Objekts unabhängig ist. Es besteht jedoch kein Grund zur Sorge Es wird im Grunde kein Verzögerungsgefühl geben. Der Effekt ist ungefähr wie folgt:
Da ich in letzter Zeit etwas faul war, belasse ich es erst einmal so. Ich habe später Zeit, eine Funktion zum Hochladen von Bildern, zum Verpixeln von Bildern und zum Exportieren hinzuzufügen.
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.