Warum wird meine Leinwand nach der Konvertierung in das Bild leer?

8

Ich versuche, das canvas Element auf dieser Seite mithilfe des folgenden Snippets in ein png zu konvertieren ( zB in die JavaScript-Konsole eingeben):

%Vor%

Leider ist das PNG, das ich bekomme, völlig leer. Beachten Sie auch, dass der ursprüngliche Zeichenbereich nach der Größenänderung der Seite leer bleibt.

Warum wird canvas leer? Wie kann ich dieses canvas in ein png umwandeln?

    
Randomblue 21.09.2012, 21:10
quelle

2 Antworten

14

Kevin Reids preserveDrawingBuffer Vorschlag ist der richtige, aber es gibt (normalerweise) eine bessere Option. Der tl; dr ist der Code am Ende.

Es kann teuer sein, die endgültigen Pixel einer gerenderten Webseite zusammenzustellen und dies mit dem Rendering von WebGL-Inhalten noch besser zu koordinieren. Der übliche Ablauf ist:

  1. JavaScript gibt Zeichenbefehle an den WebGL-Kontext aus
  2. JavaScript kehrt zurück und gibt die Steuerung an die Ereignisschleife des Haupt-Browsers zurück
  3. Der WebGL-Kontext schaltet den Zeichnungspuffer (oder seinen Inhalt) in den Compositor zur Integration in die Webseite um, die gerade auf dem Bildschirm gerendert wird
  4. Seite mit WebGL-Inhalt, die auf dem Bildschirm angezeigt wird

Beachten Sie, dass sich dies von den meisten OpenGL-Anwendungen unterscheidet. In diesen werden gerenderte Inhalte in der Regel direkt angezeigt, anstatt mit einer Menge anderer Elemente auf einer Seite zusammengesetzt zu werden, von denen einige tatsächlich über dem WebGL-Inhalt liegen und mit diesem vermischt sind.

Die WebGL-Spezifikation wurde geändert, um den Zeichenpuffer nach Schritt 3 als im Wesentlichen leer zu behandeln. Der Code, den Sie in devtools ausführen, kommt nach Schritt 4, weshalb Sie einen leeren Puffer erhalten. Diese Änderung an der Spezifikation ermöglichte große Leistungsverbesserungen auf Plattformen, bei denen das Ausblenden nach Schritt 3 im Grunde das ist, was tatsächlich in der Hardware geschieht (wie in vielen mobilen GPUs). Wenn Sie möchten, dass nach dem dritten Schritt manchmal Kopien des WebGL-Inhalts erstellt werden, müsste der Browser immer vor Schritt 3 eine Kopie des Zeichenpuffers erstellen, wodurch die Bildfrequenz sinken würde steil auf einigen Plattformen.

Sie können genau das tun und den Browser zwingen, die Kopie zu erstellen und den Bildinhalt zugänglich zu halten, indem Sie preserveDrawingBuffer auf true setzen. Aus der Spezifikation:

  

Dieses Standardverhalten kann durch Festlegen des preserveDrawingBuffer-Attributs des WebGLContextAttributes-Objekts geändert werden. Wenn dieses Flag wahr ist, soll der Inhalt des Zeichenpuffers beibehalten werden, bis der Autor sie löscht oder überschreibt. Wenn dieses Flag falsch ist, kann der Versuch, Vorgänge auszuführen, die diesen Kontext als Quellbild verwenden, nachdem die Renderfunktion zurückgegeben wurde, zu undefiniertem Verhalten führen. Dazu gehören readPixels oder toDataURL-Aufrufe oder die Verwendung dieses Kontexts als Quellbild des Aufrufs texImage2D oder drawImage eines anderen Kontexts.

In dem von Ihnen bereitgestellten Beispiel ändert der Code lediglich die Kontexterstellungszeile:

%Vor%

Denken Sie daran, dass der langsamere Pfad in einigen Browsern und die Leistung darunter leiden werden, je nachdem, was und wie Sie rendern. Sie sollten in den meisten Desktop-Browsern, in denen die Kopie nicht wirklich gemacht werden muss, gut sein, und diese machen die überwiegende Mehrheit der WebGL-fähigen Browser aus ... aber nur für den Moment.

Allerdings gibt es eine andere Option (die im nächsten Abschnitt der Spezifikation etwas verwirrend erwähnt wird).

Im Wesentlichen erstellen Sie die Kopie selbst vor Schritt 2: nachdem alle Zeichenaufrufe beendet wurden, aber bevor Sie die Kontrolle über Ihren Code an den Browser zurückgeben. Dies ist der Fall, wenn der WebGL-Zeichnungspuffer noch intakt und zugänglich ist und Sie dann problemlos auf die Pixel zugreifen können. Sie verwenden die gleichen toDataUrl oder readPixels Aufrufe, die Sie sonst verwenden würden, es ist nur das Timing, das wichtig ist.

Hier bekommen Sie das Beste aus beiden Welten. Sie erhalten eine Kopie des Zeichenpuffers, aber Sie zahlen nicht für jeden Frame, auch nicht für diejenigen, in denen Sie keine Kopie benötigen (was die meisten sein können), wie Sie es mit preserveDrawingBuffer auf gesetzt haben wahr.

Fügen Sie in dem von Ihnen angegebenen Beispiel Ihren Code an den unteren Rand von drawScene und Sie sollten die Kopie der Leinwand direkt darunter sehen:

%Vor%     
Brendan Kenny 23.09.2012, 00:12
quelle
2

Hier sind einige Dinge zu versuchen. Ich weiß nicht, ob einer von diesen sollte notwendig sein, um diese Arbeit zu machen, aber sie könnten einen Unterschied machen.

  • Fügen Sie preserveDrawingBuffer: true zu den getContext Attributen hinzu.
  • Versuchen Sie das mit einem späteren Tutorial, das Animation macht; d. h. wiederholt mehr als einmal auf der Leinwand.
Kevin Reid 22.09.2012 01:27
quelle

Tags und Links