Es gibt zwei beste Anwärter:
Erstellen Sie 1 × 1-Bilddaten, stellen Sie die Farbe ein und legen Sie putImageData
Folgendes fest:
var id = myContext.createImageData(1,1); // only do this once per page
var d = id.data; // only do this once per page
d[0] = r;
d[1] = g;
d[2] = b;
d[3] = a;
myContext.putImageData( id, x, y );
Verwenden Sie fillRect()
diese Option, um ein Pixel zu zeichnen (es sollten keine Aliasing-Probleme auftreten):
ctx.fillStyle = "rgba("+r+","+g+","+b+","+(a/255)+")";
ctx.fillRect( x, y, 1, 1 );
Sie können die Geschwindigkeit hier testen: http://jsperf.com/setting-canvas-pixel/9 oder hier https://www.measurethat.net/Benchmarks/Show/1664/1
Ich empfehle, mit Browsern zu testen, die Ihnen wichtig sind, um maximale Geschwindigkeit zu erzielen. Ab Juli 2017 fillRect()
ist Firefox v54 und Chrome v59 (Win7x64) 5-6 × schneller.
Andere, albernere Alternativen sind:
Verwenden getImageData()/putImageData()
auf der gesamten Leinwand; Dies ist ungefähr 100 × langsamer als bei anderen Optionen.
Erstellen eines benutzerdefinierten Bildes mithilfe einer Daten-URL und Anzeigen drawImage()
:
var img = new Image;
img.src = "data:image/png;base64," + myPNGEncoder(r,g,b,a);
// Writing the PNGEncoder is left as an exercise for the reader
Erstellen Sie ein weiteres Bild oder eine Leinwand, die mit allen gewünschten Pixeln gefüllt ist, und verwenden Sie drawImage()
diese Option, um nur die gewünschten Pixel zu verteilen. Dies wäre wahrscheinlich sehr schnell, hat jedoch die Einschränkung, dass Sie die benötigten Pixel vorberechnen müssen.
Beachten Sie, dass meine Tests nicht versuchen, den Canvas-Kontext zu speichern und wiederherzustellen fillStyle
. Dies würde die fillRect()
Leistung verlangsamen . Beachten Sie auch, dass ich nicht mit einer sauberen Tafel beginne oder für jeden Test genau den gleichen Satz von Pixeln teste.
fillRect()
kurzem fast 10-mal schneller als die 1x1-Putimagedata auf Chromev24. Also ... wenn Geschwindigkeit entscheidend ist und Sie Ihre Zielgruppe kennen, nehmen Sie nicht das Wort einer veralteten Antwort (auch meiner). Stattdessen: testen!Eine Methode, die nicht erwähnt wurde, ist die Verwendung von getImageData und dann putImageData.
Diese Methode ist gut geeignet, wenn Sie schnell viel auf einmal zeichnen möchten.
http://next.plnkr.co/edit/mfNyalsAR2MWkccr
quelle
Ich hatte nicht darüber nachgedacht
fillRect()
, aber die Antworten spornten mich an, es zu vergleichenputImage()
.Das Platzieren von 100.000 zufällig gefärbten Pixeln an zufälligen Orten mit Chrome 9.0.597.84 auf einem (alten) MacBook Pro dauert weniger als 100 ms
putImage()
, aber fast 900 msfillRect()
. (Benchmark-Code unter http://pastebin.com/4ijVKJcC ).Wenn ich stattdessen eine einzelne Farbe außerhalb der Schleifen auswähle und diese Farbe nur an zufälligen Stellen
putImage()
zeichne , dauert dies 59 ms gegenüber 102 msfillRect()
.Es scheint, dass der Aufwand für das Generieren und Parsen einer CSS-Farbspezifikation in der
rgb(...)
Syntax für den größten Teil des Unterschieds verantwortlich ist.Das direkte Einfügen von rohen RGB-Werten in einen
ImageData
Block erfordert hingegen keine Behandlung oder Analyse von Zeichenfolgen.quelle
quelle
putImageData()
nach dieser Funktion aufgerufen werden oder wird der Kontext durch Referenz aktualisiert?Da unterschiedliche Browser unterschiedliche Methoden zu bevorzugen scheinen, ist es möglicherweise sinnvoll, im Rahmen des Ladevorgangs einen kleineren Test mit allen drei Methoden durchzuführen, um herauszufinden, welche am besten zu verwenden ist, und diese dann in der gesamten Anwendung zu verwenden.
quelle
Es scheint seltsam, aber dennoch unterstützt HTML5 das Zeichnen von Linien, Kreisen, Rechtecken und vielen anderen Grundformen. Es gibt nichts, was zum Zeichnen des Grundpunkts geeignet ist. Die einzige Möglichkeit, dies zu tun, besteht darin, Punkte mit allem zu simulieren, was Sie haben.
Grundsätzlich gibt es also 3 mögliche Lösungen:
Jeder von ihnen hat seine Nachteile
Linie
Denken Sie daran, dass wir in Richtung Südosten ziehen. Wenn dies der Rand ist, kann es ein Problem geben. Sie können aber auch in eine andere Richtung zeichnen.
Rechteck
oder schneller mit fillRect, da die Render-Engine nur ein Pixel ausfüllt.
Kreis
Eines der Probleme mit Kreisen ist, dass es für eine Engine schwieriger ist, sie zu rendern
die gleiche Idee wie mit Rechteck, die Sie mit Füllung erreichen können.
Probleme mit all diesen Lösungen:
Wenn Sie sich fragen: "Was ist der beste Weg, um einen Punkt zu zeichnen? ", Würde ich mit einem gefüllten Rechteck gehen. Sie können mein jsperf hier mit Vergleichstests sehen .
quelle
Was ist mit einem Rechteck? Das muss effizienter sein als das Erstellen eines
ImageData
Objekts.quelle
putImageData
, ist es 10x schneller alsfillRect
in Chrome. (Siehe meine Antwort für mehr.)Zeichne ein Rechteck wie sdleihssirhc sagte!
^ - sollte ein 1x1-Rechteck bei x: 10, y: 10 zeichnen
quelle
Hmm, Sie können auch einfach eine 1 Pixel breite Linie mit einer Länge von 1 Pixel erstellen und die Richtung entlang einer einzelnen Achse verschieben.
quelle
Um Phrogz sehr gründliche Antwort zu vervollständigen, gibt es einen kritischen Unterschied zwischen
fillRect()
undputImageData()
.Der erste Einsatzkontext zu ziehen über durch das Hinzufügen eines Rechtecks (nicht um ein Pixel), die unter Verwendung von fillStyle Alpha - Wert und den Kontext globalAlpha und die Transformationsmatrix , Zeile Kappen etc ..
Die zweiten ersetzt eine ganze Reihe von Pixeln (vielleicht ein, aber warum ?)
Das Ergebnis ist anders, wie Sie auf jsperf sehen können .
Niemand möchte jeweils ein Pixel einstellen (dh auf dem Bildschirm zeichnen). Aus diesem Grund gibt es keine spezifische API, um dies zu tun (und das zu Recht).
In Bezug auf die Leistung möchten Sie, wenn das Ziel darin besteht, ein Bild zu generieren (z. B. eine Raytracing-Software), immer ein Array verwenden, das von
getImageData()
einem optimierten Uint8Array erhalten wird. Dann rufen SieputImageData()
EINMAL oder einige Male pro Sekunde mit ansetTimeout/seTInterval
.quelle
fillRect
war schmerzhaft, da die H / W-Beschleunigung von Chrome die einzelnen Aufrufe der erforderlichen GPU nicht bewältigen kann. Am Ende musste ich Pixeldaten im Verhältnis 1: 1 verwenden und dann die CSS-Skalierung verwenden, um die gewünschte Ausgabe zu erhalten. Es ist hässlich :(get/putImageData
ausführen, erhalte ich nur 168 Ops / Sek. , Aber 194.893 fürfillRect
.1x1 image data
beträgt 125.102 Ops / Sek. SofillRect
gewinnt bei weitem in Firefox. Zwischen 2012 und heute haben sich die Dinge also sehr verändert. Verlassen Sie sich wie immer niemals auf alte Benchmark-Ergebnisse.Schneller HTML-Demo-Code: Basierend auf dem, was ich über die SFML C ++ - Grafikbibliothek weiß:
Speichern Sie diese als HTML-Datei mit UTF-8-Codierung und führen Sie sie aus. Fühlen Sie sich frei zu refaktorieren, ich mag es einfach, japanische Variablen zu verwenden, weil sie präzise sind und nicht viel Platz beanspruchen
In seltenen Fällen möchten Sie EIN beliebiges Pixel einstellen und auf dem Bildschirm anzeigen. Also benutze die
Verfahren zum Zeichnen zahlreicher beliebiger Pixel in einen Rückpuffer. (billige Anrufe)
Wenn Sie bereit sind zu zeigen, rufen Sie die
Methode zum Anzeigen der Änderungen. (teurer Anruf)
Vollständiger HTML-Dateicode unten:
quelle
Wenn Sie über die Geschwindigkeit besorgt sind, können Sie auch WebGL in Betracht ziehen.
quelle
HANDY und Vorschlag der Put-Pixel (pp) -Funktion (ES6) ( Lesepixel hier ):
Code-Snippet anzeigen
Diese Funktion verwendet
putImageData
und hat Initialisierungsteil (erste lange Zeile).s='.myCanvas'
Verwenden Sie zu Beginn stattdessen Ihren CSS-Selektor für Ihre Leinwand.Wenn Sie die Parameter auf einen Wert von 0-1 normalisieren möchten, sollten Sie den Standardwert
a=255
in änderna=1
und mit:id.data.set([r,g,b,a]),ctx.putImageData(id, x, y)
bisid.data.set([r*255,g*255,b*255,a*255]),ctx.putImageData(id, x*c.width, y*c.height)
Der obige praktische Code eignet sich gut zum Ad-hoc-Testen von Grafikalgorithmen oder zum Proof of Concept. Er eignet sich jedoch nicht für die Verwendung in der Produktion, in der Code lesbar und klar sein sollte.
quelle
putImageData
ist wahrscheinlich schneller alsfillRect
nativ. Ich denke, das liegt daran, dass der fünfte Parameter auf verschiedene Arten zugewiesen werden kann (die Rechteckfarbe), wobei eine Zeichenfolge verwendet wird, die interpretiert werden muss.Angenommen, Sie tun das:
Also die Linie
ist das schwerste zwischen allen. Das fünfte Argument im
fillRect
Aufruf ist eine etwas längere Zeichenfolge.quelle
context.fillStyle = ...
stattdessen verwenden. developer.mozilla.org/en-US/docs/Web/API/…