Drawing DOM objects into a canvas

Diese Übersetzung ist unvollständig. Bitte helfen Sie, diesen Artikel aus dem Englischen zu übersetzen.

Vor dem Start

Um diesen Artikel zu verstehen, sollte der Leser mit JavaScript, dem Canvas-API und dem DOM-API vertraut sein.

Noch besser ist es, wenn er sich mit SVG auskennt. 

Auch wenn es aus Sicherheitsgründen nicht trivial ist, besteht doch die Möglichkeit, DOM-Inhalte - etwa HTML - in ein canvas-Element zu zeichnen. Dieser Artikel, der auf einem Blogpost von Robert O'Callahan basiert, erklärt, wie Anwender das sicher und in Übereinstimmung mit der Spezifikation tun.

Ein Überblick

HTML lässt sich nicht einfach auf ein Canvas-Element zeichnen. Stattdessen benötigt man eine SVG-Grafik, welche die zu renderenden Inhalte enthält. Um HTML-Inhalte auf das Canvas zu zeichnen, greift man in der Regel zu einem <foreignObject>-SVG-Element, in dem das HTML steckt. Über den Umweg dieser SVG-Grafik zeichnet man das HTML dann auf das Canvas-Element.

Schritt für Schritt

Die einzige echte Herausforderung - und das ist wahrscheinlich noch übertrieben - besteht darin, das SVG für das Bild zu erstellen. Dazu genügt es, einen String zu generieren, der das XML für das SVG enthält und einen Blob mit den folgenden Elemente zu erzeugen:

  1. einen MIME-Type "image/svg+xml" für das Blob
  2. das <svg>-Element.
  3. in diesem das <foreignObject>-Element.
  4. das (korrekt formatierte) HTML, eingebettet in das <foreignObject>.

Indem man, wie oben beschrieben, eine Object-URL verwendet, integriert man das HTML, anstatt es aus einer externen Quelle zu laden. Ist ihr Ursprung identisch mit dem des Dokuments, lässt sich aber auch eine externe Quelle einsetzen.

Beispiele

HTML

<canvas id="canvas" style="border:2px solid black;" width="200" height="200">
</canvas>

Javascript

var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');

var data = '<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200">' +
           '<foreignObject width="100%" height="100%">' +
           '<div xmlns="http://www.w3.org/1999/xhtml" style="font-size:40px">' +
             '<em>I</em> like ' + 
             '<span style="color:white; text-shadow:0 0 2px blue;">' +
             'cheese</span>' +
           '</div>' +
           '</foreignObject>' +
           '</svg>';

var DOMURL = window.URL || window.webkitURL || window;

var img = new Image();
var svg = new Blob([data], {type: 'image/svg+xml'});
var url = DOMURL.createObjectURL(svg);

img.onload = function () {
  ctx.drawImage(img, 0, 0);
  DOMURL.revokeObjectURL(url);
}

img.src = url;

Der Beispielcode erzeugt das folgende

ScreenshotLive sample

Die data-Variable enthält in diesem Fall den Inhalt der SVG-Grafik (diese wiederum das HTML) die man auf das canvas-Element zeichnen möchte.

Dann wird das neue HTML-Element <img> erzeugt, indem man über new Image() ein neues Bild, dann ein neues SVG-Objekt mit den SVG-Blobdaten und schließlich ein url-Objekt erzeugt. Über drawImage() zeichnet man das Bild beim Laden der Javascript-Datei am Ende in den Kontext.

Sicherheit

Manche fragen sich sicherlich, wie sicher so ein Prozess wohl sein kann, wo es doch möglich ist, Daten aus dem Canvas zu lesen. Die Antwortet lautet: Diese Lösung macht sich den Umstand zunutze, dass SVG-Bilder sehr restriktiv implementiert sind. Solche Bilder dürfen zum Beispiel keine externen Ressourcen laden, selbst solche nicht, die von derselben Domain zu stammen scheinen. Andere Quellen, wie etwa Rastergrafiken (beispielsweise JPEGs) oder <iframe>s müssen SVGs als Daten-URIs integrieren.

Zudem lassen sich in eine SVG-Grafik keine Skripte integrieren. Auf diese Weise besteht kein Risiko, von anderen Skripten aus auf den DOM zuzugreifen. Nicht zuletzt empfangen DOM-Elemente in SVG-Grafiken keine Input-Events. Das verhindert die Möglichkeit, privilegierte Informationen in ein Formularfeld zu laden (etwa einen vollständigen Pfad in ein Dateieingabe-Element), um dieses zu rendern und dann die Information herauszufinden, indem man die Pixel abliest.

In SVG-Images gerenderte Links verzichten auf eine "Besuchte-Links"-Darstellung. So lassen sich keine Rückschlüsse auf in der Vergangenheit verwendete Links ziehen. SVG-Grafiken rendern zudem keine nativen Themes, was es schwieriger macht, die Plattformen der User zu bestimmen.

Das so erzeugte Canvas-Element sollte ursprünglich rein sein, was bedeutet, dass der Aufruf toBlob(function(blob){…}) ein Blob für das Canvas zurück gibt oder ein toDataURL() einen Base64-chiffrierten data: URI.

HTML zeichnen

Weil SVG gültiges XML sein soll, muss man HTML parsen, um eine korrekt formatierte Ausgabe des HTML-Parsers zu erhalten. Mit dem folgenden Code lässt sich HTML am einfachsten parsen:

var doc = document.implementation.createHTMLDocument("");
doc.write(html);

// You must manually set the xmlns if you intend to immediately serialize 
// the HTML document to a string as opposed to appending it to a 
// <foreignObject> in the DOM
doc.documentElement.setAttribute("xmlns", doc.documentElement.namespaceURI);

// Get well-formed markup
html = (new XMLSerializer).serializeToString(doc);

Siehe auch

Schlagwörter des Dokuments und Mitwirkende

 Mitwirkende an dieser Seite: surferboy250
 Zuletzt aktualisiert von: surferboy250,