Grafiken zeichnen
Der Browser enthält einige sehr leistungsfähige Werkzeuge für die Grafikprogrammierung, von der Sprache Scalable Vector Graphics (SVG) bis hin zu APIs zum Zeichnen auf HTML-<canvas>-Elementen (siehe die Canvas API und WebGL). Dieser Artikel bietet eine Einführung in Canvas sowie weiterführende Ressourcen, mit denen Sie mehr lernen können.
| Voraussetzungen: | Vertrautheit mit HTML, CSS und JavaScript, insbesondere mit den Grundlagen von JavaScript-Objekten und grundlegenden APIs wie DOM-Scripting und Netzwerkanfragen. |
|---|---|
| Lernergebnisse: |
|
Grafiken im Web
Das Web bestand ursprünglich nur aus Text, was sehr langweilig war. Daher wurden Bilder eingeführt — zunächst über das <img>-Element und später über CSS-Eigenschaften wie background-image sowie SVG.
Das reichte jedoch noch nicht aus. Zwar konnten Sie CSS und JavaScript verwenden, um SVG-Vektorbilder zu animieren (und anderweitig zu manipulieren) — da sie durch Markup dargestellt werden —, aber für Bitmap-Bilder gab es weiterhin keine entsprechende Möglichkeit, und die verfügbaren Werkzeuge waren eher begrenzt. Das Web bot noch immer keine effektive Möglichkeit, Animationen, Spiele, 3D-Szenen und andere Anforderungen zu erstellen, die üblicherweise von Low-Level-Sprachen wie C++ oder Java verarbeitet werden.
Die Situation begann sich zu verbessern, als Browser 2004 damit begannen, das <canvas>-Element und die zugehörige Canvas API zu unterstützen. Wie Sie unten sehen werden, bietet Canvas einige nützliche Werkzeuge zum Erstellen von 2D-Animationen, Spielen, Datenvisualisierungen und anderen Arten von Anwendungen, insbesondere in Kombination mit einigen anderen APIs der Webplattform. Es kann jedoch schwierig oder unmöglich sein, Inhalte zugänglich zu machen.
Das folgende Beispiel zeigt eine einfache, Canvas-basierte 2D-Animation mit springenden Bällen, die wir ursprünglich in unserem Modul Einführung in JavaScript-Objekte kennengelernt haben:
Um 2006–2007 begann Mozilla mit der Arbeit an einer experimentellen 3D-Canvas-Implementierung. Daraus entstand WebGL, das bei Browserherstellern Anklang fand und um 2009–2010 standardisiert wurde. Mit WebGL können Sie echte 3D-Grafiken in Ihrem Webbrowser erstellen.
Dieser Artikel konzentriert sich hauptsächlich auf 2D-Canvas, da roher WebGL-Code sehr komplex ist. Wir zeigen jedoch, wie Sie eine WebGL-Bibliothek verwenden können, um einfacher eine 3D-Szene zu erstellen. Ein Tutorial zu rohem WebGL finden Sie an anderer Stelle — siehe Erste Schritte mit WebGL.
Erste Schritte mit einem <canvas>
Wenn Sie eine 2D- oder 3D-Szene auf einer Webseite erstellen möchten, benötigen Sie zunächst ein HTML-<canvas>-Element. Dieses Element wird verwendet, um den Bereich auf der Seite festzulegen, in dem das Bild gezeichnet wird. Das ist so einfach wie das Einfügen des Elements auf der Seite:
<canvas width="320" height="240"></canvas>
Dadurch wird ein Canvas mit einer Größe von 320 mal 240 Pixeln auf der Seite erstellt.
Sie sollten zwischen den <canvas>-Tags Fallback-Inhalte einfügen. Diese sollten den Canvas-Inhalt für Nutzende von Browsern, die Canvas nicht unterstützen, oder für Nutzende von Screenreadern beschreiben.
<canvas width="320" height="240">
<p>Description of the canvas for those unable to view it.</p>
</canvas>
Der Fallback sollte nützliche alternative Inhalte für den Canvas-Inhalt bereitstellen. Wenn Sie beispielsweise ein ständig aktualisiertes Diagramm von Aktienkursen darstellen, könnte der Fallback-Inhalt ein statisches Bild des neuesten Aktiendiagramms sein, mit alt-Text, der die Kurse als Text angibt, oder eine Liste von Links zu einzelnen Aktienseiten.
Hinweis:
Canvas-Inhalte sind für Screenreader nicht zugänglich. Fügen Sie beschreibenden Text als Wert des Attributs aria-label direkt auf dem Canvas-Element hinzu oder fügen Sie Fallback-Inhalte innerhalb der öffnenden und schließenden <canvas>-Tags ein. Canvas-Inhalte sind kein Teil des DOM, verschachtelte Fallback-Inhalte jedoch schon.
Unser Canvas erstellen und dimensionieren
Beginnen wir damit, unsere eigene Canvas-Vorlage zu erstellen, in der wir zukünftig experimentieren können.
-
Erstellen Sie zunächst auf Ihrer lokalen Festplatte ein Verzeichnis namens
canvas-template. -
Erstellen Sie im Verzeichnis eine neue Datei namens
index.htmlund speichern Sie darin die folgenden Inhalte:html<!doctype html> <html lang="en-US"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width" /> <title>Canvas</title> <script src="script.js" defer></script> <link href="style.css" rel="stylesheet" /> </head> <body> <canvas class="myCanvas"> <p>Add suitable fallback here.</p> </canvas> </body> </html> -
Erstellen Sie im Verzeichnis eine neue Datei namens
style.cssund speichern Sie darin die folgende CSS-Regel:cssbody { margin: 0; overflow: hidden; } -
Erstellen Sie im Verzeichnis eine neue Datei namens
script.js. Lassen Sie diese Datei vorerst leer. -
Öffnen Sie nun
script.jsund fügen Sie die folgenden JavaScript-Zeilen hinzu:jsconst canvas = document.querySelector(".myCanvas"); const width = (canvas.width = window.innerWidth); const height = (canvas.height = window.innerHeight);Hier haben wir eine Referenz auf das Canvas in der Konstante
canvasgespeichert. In der zweiten Zeile setzen wir sowohl eine neue Konstantewidthals auch diewidth-Eigenschaft des Canvas aufWindow.innerWidth(was uns die Breite des Viewports liefert). In der dritten Zeile setzen wir sowohl eine neue Konstanteheightals auch dieheight-Eigenschaft des Canvas aufWindow.innerHeight(was uns die Höhe des Viewports liefert). Jetzt haben wir also ein Canvas, das die gesamte Breite und Höhe des Browserfensters ausfüllt!Sie werden auch feststellen, dass wir Zuweisungen mit mehreren Gleichheitszeichen verketten — dies ist in JavaScript zulässig und eine gute Technik, wenn Sie mehrere Variablen auf denselben Wert setzen möchten. Wir wollten die Breite und Höhe des Canvas in den Variablen
widthundheightleicht zugänglich machen, da diese später nützliche Werte sind (beispielsweise, wenn Sie etwas genau in der Mitte der Canvas-Breite zeichnen möchten).
Hinweis: Im Allgemeinen sollten Sie die Größe des Canvas mit HTML-Attributen oder DOM-Eigenschaften festlegen, wie oben erläutert. Sie könnten CSS verwenden, allerdings wird die Größenanpassung dann erst vorgenommen, nachdem das Canvas gerendert wurde. Wie jedes andere Bild könnte das Canvas dadurch verpixelt oder verzerrt werden.
Den Canvas-Kontext abrufen und die endgültige Einrichtung
Wir müssen noch eine letzte Sache erledigen, bevor unsere Canvas-Vorlage fertig ist. Um auf das Canvas zeichnen zu können, müssen wir eine spezielle Referenz auf den Zeichenbereich abrufen, die als Kontext bezeichnet wird. Dies geschieht mit der Methode HTMLCanvasElement.getContext(), die für die grundlegende Verwendung einen einzelnen String als Parameter entgegennimmt, der den Typ des abzurufenden Kontexts darstellt.
In diesem Fall möchten wir ein 2D-Canvas, also fügen Sie die folgende JavaScript-Zeile unter den anderen in script.js hinzu:
const ctx = canvas.getContext("2d");
Hinweis:
Andere Kontextwerte, die Sie wählen könnten, sind unter anderem webgl für WebGL und webgpu für WebGPU, aber diese benötigen wir in diesem Artikel nicht.
Das war's — unser Canvas ist jetzt vorbereitet und bereit zum Zeichnen! Die Variable ctx enthält nun ein CanvasRenderingContext2D-Objekt, und alle Zeichenoperationen auf dem Canvas umfassen die Manipulation dieses Objekts.
Lassen Sie uns noch eine letzte Sache erledigen, bevor wir fortfahren. Wir färben den Canvas-Hintergrund schwarz, um Ihnen einen ersten Eindruck von der Canvas API zu vermitteln. Fügen Sie am Ende Ihres JavaScript die folgenden Zeilen hinzu:
ctx.fillStyle = "black";
ctx.fillRect(0, 0, width, height);
Hier legen wir mithilfe der fillStyle-Eigenschaft des Canvas eine Füllfarbe fest (sie akzeptiert Farbwerte genau wie CSS-Eigenschaften). Anschließend zeichnen wir mit der Methode fillRect ein Rechteck, das den gesamten Bereich des Canvas abdeckt. Die ersten beiden Parameter sind die Koordinaten der oberen linken Ecke des Rechtecks; die letzten beiden sind die Breite und Höhe, mit denen das Rechteck gezeichnet werden soll — wir haben Ihnen gesagt, dass diese Variablen width und height nützlich sein würden!
OK, unsere Vorlage ist fertig und es ist Zeit weiterzumachen.
Grundlagen von 2D-Canvas
Wie oben erwähnt, werden alle Zeichenoperationen durch die Manipulation eines CanvasRenderingContext2D-Objekts (in unserem Fall ctx) ausgeführt. Viele Operationen benötigen Koordinaten, um genau festzulegen, wo etwas gezeichnet werden soll — oben links im Canvas liegt Punkt (0, 0), die horizontale (x-)Achse verläuft von links nach rechts und die vertikale (y-)Achse von oben nach unten.

Formen werden üblicherweise mithilfe der Rechteck-Grundform gezeichnet oder indem eine Linie entlang eines bestimmten Pfads nachgezogen und die Form anschließend gefüllt wird. Im Folgenden zeigen wir Ihnen beides.
Einfache Rechtecke
Beginnen wir mit einigen einfachen Rechtecken.
-
Erstellen Sie zunächst eine Kopie Ihres neu programmierten Canvas-Vorlagenverzeichnisses.
-
Fügen Sie am Ende Ihrer JavaScript-Datei die folgenden Zeilen hinzu:
jsctx.fillStyle = "red"; ctx.fillRect(50, 50, 100, 150);Wenn Sie Ihr HTML im Browser laden, sollten Sie sehen, dass ein rotes Rechteck auf Ihrem Canvas erscheint. Seine obere linke Ecke ist 50 Pixel vom oberen und linken Rand des Canvas entfernt (wie durch die ersten beiden Parameter definiert), und es ist 100 Pixel breit sowie 150 Pixel hoch (wie durch den dritten und vierten Parameter definiert).
-
Fügen wir noch ein weiteres Rechteck hinzu — diesmal ein grünes. Fügen Sie am Ende Ihres JavaScript Folgendes hinzu:
jsctx.fillStyle = "green"; ctx.fillRect(75, 75, 100, 100);Speichern und aktualisieren Sie die Seite, dann sehen Sie Ihr neues Rechteck. Dies verdeutlicht einen wichtigen Punkt: Grafikoperationen wie das Zeichnen von Rechtecken, Linien und Ähnlichem werden in der Reihenfolge ausgeführt, in der sie auftreten. Stellen Sie es sich wie das Streichen einer Wand vor, bei dem jede Farbschicht die darunterliegende überlappt und möglicherweise sogar verdeckt. Sie können daran nichts ändern, daher müssen Sie sorgfältig über die Reihenfolge nachdenken, in der Sie die Grafiken zeichnen.
-
Beachten Sie, dass Sie halbtransparente Grafiken zeichnen können, indem Sie eine halbtransparente Farbe angeben, zum Beispiel mit
rgb(). Der „Alpha-Kanal“ definiert, wie transparent die Farbe ist. Je höher sein Wert ist, desto stärker verdeckt die Farbe alles, was sich dahinter befindet. Fügen Sie Folgendes zu Ihrem Code hinzu:jsctx.fillStyle = "rgb(255 0 255 / 75%)"; ctx.fillRect(25, 100, 175, 50); -
Versuchen Sie nun, weitere eigene Rechtecke zu zeichnen; viel Spaß!
Konturen und Linienbreiten
Bisher haben wir gefüllte Rechtecke betrachtet, aber Sie können auch Rechtecke zeichnen, die nur Umrisse haben (im Grafikdesign als Konturen bezeichnet). Um die gewünschte Konturfarbe festzulegen, verwenden Sie die Eigenschaft strokeStyle; ein Rechteck mit Kontur wird mit strokeRect gezeichnet.
-
Fügen Sie dem vorherigen Beispiel erneut unter den vorherigen JavaScript-Zeilen Folgendes hinzu:
jsctx.strokeStyle = "white"; ctx.strokeRect(25, 25, 175, 200); -
Die Standardbreite von Konturen beträgt 1 Pixel. Sie können den Wert der Eigenschaft
lineWidthanpassen, um dies zu ändern (sie erwartet eine Zahl, die die Breite der Kontur in Pixeln darstellt). Fügen Sie die folgende Zeile zwischen den vorherigen beiden Zeilen hinzu:jsctx.lineWidth = 5;
Jetzt sollten Sie sehen, dass Ihr weißer Umriss deutlich dicker geworden ist! Das war es vorerst. Ihr Beispiel sollte nun wie folgt aussehen:
Sie können die Schaltfläche Play drücken, um das Beispiel in MDN Playground zu öffnen und den Quellcode zu bearbeiten.
Pfade zeichnen
Wenn Sie etwas Komplexeres als ein Rechteck zeichnen möchten, müssen Sie einen Pfad zeichnen. Grundsätzlich bedeutet dies, Code zu schreiben, der genau festlegt, welchen Pfad der Stift auf Ihrem Canvas entlanglaufen soll, um die gewünschte Form nachzuzeichnen. Canvas enthält Funktionen zum Zeichnen gerader Linien, Kreise, Bézierkurven und mehr.
Beginnen Sie diesen Abschnitt mit einer neuen Kopie Ihrer Canvas-Vorlage, in der Sie das neue Beispiel zeichnen können.
Wir verwenden in allen folgenden Abschnitten einige gemeinsame Methoden und Eigenschaften:
beginPath()— beginnt das Zeichnen eines Pfads an dem Punkt, an dem sich der Stift aktuell auf dem Canvas befindet. Bei einem neuen Canvas startet der Stift bei (0, 0).moveTo()— bewegt den Stift zu einem anderen Punkt auf dem Canvas, ohne die Linie aufzuzeichnen oder nachzuziehen; der Stift „springt“ zur neuen Position.fill()— zeichnet eine gefüllte Form, indem der bisher nachgezogene Pfad ausgefüllt wird.stroke()— zeichnet eine Umrissform, indem entlang des bisher gezeichneten Pfads eine Kontur gezogen wird.- Sie können mit Pfaden ebenso Funktionen wie
lineWidthundfillStyle/strokeStyleverwenden wie mit Rechtecken.
Eine typische, einfache Pfad-Zeichenoperation könnte etwa so aussehen:
ctx.fillStyle = "red";
ctx.beginPath();
ctx.moveTo(50, 50);
// draw your path
ctx.fill();
Linien zeichnen
Zeichnen wir ein gleichseitiges Dreieck auf dem Canvas.
-
Fügen Sie zunächst die folgende Hilfsfunktion am Ende Ihres Codes hinzu. Sie wandelt Gradwerte in Bogenmaß um, was nützlich ist, da ein Winkelwert in JavaScript fast immer im Bogenmaß angegeben werden muss, Menschen aber üblicherweise in Grad denken.
jsfunction degToRad(degrees) { return (degrees * Math.PI) / 180; } -
Beginnen Sie als Nächstes Ihren Pfad, indem Sie unter Ihrer vorherigen Ergänzung Folgendes hinzufügen. Hier legen wir eine Farbe für unser Dreieck fest, beginnen einen Pfad zu zeichnen und bewegen den Stift dann zu (50, 50), ohne etwas zu zeichnen. Dort beginnen wir, unser Dreieck zu zeichnen.
jsctx.fillStyle = "red"; ctx.beginPath(); ctx.moveTo(50, 50); -
Fügen Sie nun die folgenden Zeilen am Ende Ihres Skripts hinzu:
jsctx.lineTo(150, 50); const triHeight = 50 * Math.tan(degToRad(60)); ctx.lineTo(100, 50 + triHeight); ctx.lineTo(50, 50); ctx.fill();Gehen wir dies der Reihe nach durch:
Zuerst zeichnen wir eine Linie zu (150, 50) — unser Pfad verläuft nun 100 Pixel nach rechts entlang der x-Achse.
Zweitens berechnen wir die Höhe unseres gleichseitigen Dreiecks mithilfe einfacher Trigonometrie. Grundsätzlich zeichnen wir das Dreieck mit der Spitze nach unten. Die Winkel in einem gleichseitigen Dreieck betragen immer 60 Grad. Um die Höhe zu berechnen, können wir es in der Mitte in zwei rechtwinklige Dreiecke aufteilen, die jeweils Winkel von 90 Grad, 60 Grad und 30 Grad haben. Hinsichtlich der Seiten:
- Die längste Seite wird als Hypotenuse bezeichnet.
- Die Seite neben dem 60-Grad-Winkel wird als Ankathete bezeichnet — wir wissen, dass sie 50 Pixel lang ist, da sie die Hälfte der gerade gezeichneten Linie ist.
- Die Seite gegenüber dem 60-Grad-Winkel wird als Gegenkathete bezeichnet. Das ist die Höhe des Dreiecks, die wir berechnen möchten.

Eine der grundlegenden trigonometrischen Formeln besagt, dass die Länge der Ankathete multipliziert mit dem Tangens des Winkels gleich der Gegenkathete ist. Daher erhalten wir
50 * Math.tan(degToRad(60)). Wir verwenden unsere FunktiondegToRad(), um 60 Grad in Bogenmaß umzuwandeln, daMath.tan()einen Eingabewert im Bogenmaß erwartet. -
Nachdem die Höhe berechnet wurde, zeichnen wir eine weitere Linie zu
(100, 50 + triHeight). Die X-Koordinate ist einfach; sie muss genau zwischen den beiden vorher festgelegten X-Werten liegen. Der Y-Wert hingegen muss 50 plus die Dreieckshöhe betragen, da wir wissen, dass die Spitze des Dreiecks 50 Pixel vom oberen Rand des Canvas entfernt liegt. -
Die nächste Zeile zeichnet eine Linie zurück zum Startpunkt des Dreiecks.
-
Zum Schluss führen wir
ctx.fill()aus, um den Pfad abzuschließen und die Form zu füllen.
Kreise zeichnen
Sehen wir uns nun an, wie Sie einen Kreis im Canvas zeichnen. Dies geschieht mit der Methode arc(), die einen gesamten Kreis oder einen Teil davon an einem angegebenen Punkt zeichnet.
-
Fügen wir unserem Canvas einen Kreisbogen hinzu — fügen Sie am Ende Ihres Codes Folgendes hinzu:
jsctx.fillStyle = "blue"; ctx.beginPath(); ctx.arc(150, 106, 50, degToRad(0), degToRad(360), false); ctx.fill();arc()akzeptiert sechs Parameter. Die ersten beiden geben die Position des Mittelpunkts des Kreisbogens an (jeweils X und Y). Der dritte ist der Radius des Kreises, der vierte und fünfte sind die Start- und Endwinkel, bei denen der Kreis gezeichnet wird (die Angabe von 0 und 360 Grad ergibt also einen vollständigen Kreis), und der sechste Parameter bestimmt, ob der Kreis gegen den Uhrzeigersinn oder im Uhrzeigersinn gezeichnet werden soll (falsesteht für im Uhrzeigersinn).Hinweis: 0 Grad liegt horizontal nach rechts.
-
Versuchen wir, einen weiteren Kreisbogen hinzuzufügen:
jsctx.fillStyle = "yellow"; ctx.beginPath(); ctx.arc(200, 106, 50, degToRad(-45), degToRad(45), true); ctx.lineTo(200, 106); ctx.fill();Das Muster ist hier sehr ähnlich, weist aber zwei Unterschiede auf:
- Wir haben den letzten Parameter von
arc()auftruegesetzt, was bedeutet, dass der Kreisbogen gegen den Uhrzeigersinn gezeichnet wird. Das bedeutet, dass wir den Kreisbogen um die 270 Grad außerhalb dieses Bereichs zeichnen, obwohl er bei -45 Grad beginnen und bei 45 Grad enden soll. Wenn Sietrueinfalseändern und den Code erneut ausführen, wird nur der 90-Grad-Sektor des Kreises gezeichnet. - Vor dem Aufruf von
fill()zeichnen wir eine Linie zum Mittelpunkt des Kreises. Das bedeutet, dass wir den recht ansehnlichen Ausschnitt im Pac-Man-Stil erhalten. Wenn Sie diese Zeile entfernen (probieren Sie es aus!) und den Code erneut ausführen, erhalten Sie lediglich einen abgeschnittenen Rand des Kreises zwischen Start- und Endpunkt des Kreisbogens. Dies veranschaulicht einen weiteren wichtigen Punkt von Canvas: Wenn Sie versuchen, einen unvollständigen Pfad (also einen nicht geschlossenen Pfad) zu füllen, zeichnet der Browser eine gerade Linie zwischen Start- und Endpunkt und füllt ihn anschließend aus.
- Wir haben den letzten Parameter von
Das war es vorerst; Ihr fertiges Beispiel sollte wie folgt aussehen:
Sie können die Schaltfläche Play drücken, um das Beispiel in MDN Playground zu öffnen und den Quellcode zu bearbeiten.
Hinweis: Weitere Informationen über erweiterte Funktionen zum Zeichnen von Pfaden, etwa Bézierkurven, finden Sie in unserem Tutorial Formen mit Canvas zeichnen.
Text
Canvas bietet auch Funktionen zum Zeichnen von Text. Sehen wir uns diese kurz an. Erstellen Sie zunächst eine weitere neue Kopie Ihrer Canvas-Vorlage, in der Sie das neue Beispiel zeichnen können.
Text wird mit zwei Methoden gezeichnet:
fillText()— zeichnet gefüllten Text.strokeText()— zeichnet Text als Umriss (Kontur).
In ihrer grundlegenden Verwendung akzeptieren beide drei Eigenschaften: den zu zeichnenden Text-String sowie die X- und Y-Koordinaten des Punkts, an dem mit dem Zeichnen des Textes begonnen werden soll. Dieser Punkt ist die untere linke Ecke des Textfelds (also des Felds, das den gezeichneten Text umgibt). Das könnte Sie verwirren, da andere Zeichenoperationen üblicherweise von der oberen linken Ecke ausgehen — behalten Sie dies im Hinterkopf.
Es gibt außerdem eine Reihe von Eigenschaften zur Steuerung der Textdarstellung, etwa font, mit der Sie Schriftfamilie, Größe usw. angeben können. Als Wert akzeptiert sie dieselbe Syntax wie die CSS-Eigenschaft font.
Canvas-Inhalte sind für Screenreader nicht zugänglich. Auf das Canvas gezeichneter Text ist nicht für das DOM verfügbar, muss aber verfügbar gemacht werden, um zugänglich zu sein. In diesem Beispiel schließen wir den Text als Wert für aria-label ein.
Versuchen Sie, den folgenden Block am Ende Ihres JavaScript hinzuzufügen:
ctx.strokeStyle = "white";
ctx.lineWidth = 1;
ctx.font = "36px arial";
ctx.strokeText("Canvas text", 50, 50);
ctx.fillStyle = "red";
ctx.font = "48px georgia";
ctx.fillText("Canvas text", 50, 150);
canvas.setAttribute("aria-label", "Canvas text");
Hier zeichnen wir zwei Textzeilen, eine als Umriss und die andere als Kontur. Das Beispiel sollte wie folgt aussehen:
Drücken Sie die Schaltfläche Play, um das Beispiel in MDN Playground zu öffnen und den Quellcode zu bearbeiten. Probieren Sie es aus und sehen Sie, was Sie erstellen können! Weitere Informationen zu den verfügbaren Optionen für Canvas-Text finden Sie unter Text zeichnen.
Bilder auf das Canvas zeichnen
Es ist möglich, externe Bilder auf Ihrem Canvas darzustellen. Dies können einfache Bilder, Frames aus Videos oder die Inhalte anderer Canvases sein. Vorerst betrachten wir nur den Fall, einfache Bilder auf unserem Canvas zu verwenden.
-
Erstellen Sie wie zuvor eine weitere neue Kopie Ihrer Canvas-Vorlage, in der Sie das neue Beispiel zeichnen können.
Bilder werden mit der Methode
drawImage()auf das Canvas gezeichnet. Die einfachste Version akzeptiert drei Parameter — eine Referenz auf das darzustellende Bild sowie die X- und Y-Koordinaten der oberen linken Ecke des Bildes. -
Beginnen wir damit, eine Bildquelle abzurufen, die wir in unser Canvas einbetten können. Fügen Sie die folgenden Zeilen am Ende Ihres JavaScript hinzu:
jsconst image = new Image(); image.src = "https://mdn.github.io/shared-assets/images/examples/fx-nightly-512.png";Hier erstellen wir mithilfe des Konstruktors
Image()ein neuesHTMLImageElement-Objekt. Das zurückgegebene Objekt hat denselben Typ wie das Objekt, das zurückgegeben wird, wenn Sie eine Referenz auf ein vorhandenes<img>-Element abrufen. Anschließend setzen wir dessensrc-Attribut auf unser Firefox-Logo-Bild. An diesem Punkt beginnt der Browser, das Bild zu laden. -
Wir könnten nun versuchen, das Bild mit
drawImage()einzubetten, müssen jedoch zunächst sicherstellen, dass die Bilddatei geladen wurde, da der Code andernfalls fehlschlägt. Das können wir mit demload-Ereignis erreichen, das erst ausgelöst wird, wenn das Bild vollständig geladen wurde. Fügen Sie unter dem vorherigen Block Folgendes hinzu:jsimage.addEventListener("load", () => ctx.drawImage(image, 20, 20));Wenn Sie Ihr Beispiel nun im Browser laden, sollten Sie das Bild im Canvas eingebettet sehen, wenn auch ziemlich groß.
-
Aber es gibt noch mehr! Was ist, wenn wir nur einen Teil des Bildes anzeigen oder seine Größe ändern möchten? Beides ist mit der komplexeren Version von
drawImage()möglich. Aktualisieren Sie Ihrectx.drawImage()-Zeile wie folgt:jsctx.drawImage(image, 0, 0, 512, 512, 50, 40, 185, 185);- Der erste Parameter ist wie zuvor die Bildreferenz.
- Parameter 2 und 3 definieren die Koordinaten der oberen linken Ecke des Bereichs, den Sie aus dem geladenen Bild ausschneiden möchten, relativ zur oberen linken Ecke des Bildes selbst. Nichts links vom ersten Parameter oder oberhalb des zweiten wird gezeichnet.
- Parameter 4 und 5 definieren Breite und Höhe des Bereichs, den wir aus dem geladenen Originalbild ausschneiden möchten.
- Parameter 6 und 7 definieren die Koordinaten, an denen Sie die obere linke Ecke des ausgeschnittenen Bildausschnitts zeichnen möchten, relativ zur oberen linken Ecke des Canvas.
- Parameter 8 und 9 definieren die Breite und Höhe, mit denen der ausgeschnittene Bereich des Bildes gezeichnet wird. In diesem Fall haben wir dieselben Abmessungen wie für den ursprünglichen Ausschnitt angegeben, Sie könnten die Größe jedoch ändern, indem Sie andere Werte angeben.
-
Wenn das Bild inhaltlich aktualisiert wird, muss auch die Beschreibung aktualisiert werden.
jscanvas.setAttribute("aria-label", "Firefox Logo");
Das fertige Beispiel sollte wie folgt aussehen:
Drücken Sie die Schaltfläche Play, um das Beispiel in MDN Playground zu öffnen und den Quellcode zu bearbeiten.
Schleifen und Animationen
Bisher haben wir einige sehr grundlegende Einsatzmöglichkeiten von 2D-Canvas behandelt, aber Sie werden die volle Leistung von Canvas erst erleben, wenn Sie es auf irgendeine Weise aktualisieren oder animieren. Schließlich stellt Canvas skriptfähige Bilder bereit! Wenn Sie nichts ändern möchten, können Sie ebenso gut statische Bilder verwenden und sich die ganze Arbeit sparen.
Eine Schleife erstellen
Das Arbeiten mit Schleifen in Canvas macht viel Spaß — Sie können Canvas-Befehle innerhalb einer for-Schleife (oder einer anderen Art von Schleife) ausführen, genau wie jeden anderen JavaScript-Code.
Erstellen wir ein Beispiel.
-
Erstellen Sie eine weitere neue Kopie Ihrer Canvas-Vorlage.
-
Fügen Sie am Ende Ihres JavaScript die folgende Zeile hinzu. Sie enthält eine neue Methode,
translate(), die den Ursprungspunkt des Canvas verschiebt:jsctx.translate(width / 2, height / 2);Dadurch wird der Koordinatenursprung (0, 0) in die Mitte des Canvas verschoben, statt sich in der oberen linken Ecke zu befinden. Das ist in vielen Situationen sehr nützlich, etwa in dieser, in der unser Design relativ zur Mitte des Canvas gezeichnet werden soll.
-
Fügen Sie nun den folgenden Code am Ende des JavaScript hinzu:
jsfunction degToRad(degrees) { return (degrees * Math.PI) / 180; } function rand(min, max) { return Math.floor(Math.random() * (max - min + 1)) + min; } let length = 250; let moveOffset = 20;Hier implementieren wir dieselbe Funktion
degToRad(), die wir oben im Dreiecksbeispiel gesehen haben, eine Funktionrand(), die eine Zufallszahl zwischen angegebenen unteren und oberen Grenzen zurückgibt, sowie die VariablenlengthundmoveOffset(über die wir später mehr erfahren). -
Die Idee ist, dass wir innerhalb der
for-Schleife etwas auf dem Canvas zeichnen und bei jeder Iteration verändern, um etwas Interessantes zu erstellen. Fügen Sie den folgenden Code innerhalb Ihrerfor-Schleife hinzu:jsfor (let i = 0; i < length; i++) { ctx.fillStyle = `rgb(${255 - length} 0 ${255 - length} / 90%)`; ctx.beginPath(); ctx.moveTo(moveOffset, moveOffset); ctx.lineTo(moveOffset + length, moveOffset); const triHeight = (length / 2) * Math.tan(degToRad(60)); ctx.lineTo(moveOffset + length / 2, moveOffset + triHeight); ctx.lineTo(moveOffset, moveOffset); ctx.fill(); length--; moveOffset += 0.7; ctx.rotate(degToRad(5)); }Bei jeder Iteration führen wir also Folgendes aus:
- Wir setzen
fillStyleauf einen leicht transparenten Violettton, der sich jedes Mal entsprechend dem Wert vonlengthändert. Wie Sie später sehen werden, wird die Länge bei jedem Schleifendurchlauf kleiner. Der Effekt besteht also darin, dass die Farbe mit jedem weiteren gezeichneten Dreieck heller wird. - Wir beginnen den Pfad.
- Wir bewegen den Stift zu einer Koordinate von
(moveOffset, moveOffset). Diese Variable definiert, wie weit wir bei jedem Zeichnen eines neuen Dreiecks verschieben möchten. - Wir zeichnen eine Linie zu einer Koordinate von
(moveOffset+length, moveOffset). Dadurch wird eine Linie mit der Längelengthparallel zur X-Achse gezeichnet. - Wir berechnen wie zuvor die Höhe des Dreiecks.
- Wir zeichnen eine Linie zur nach unten gerichteten Ecke des Dreiecks und anschließend eine Linie zurück zum Startpunkt des Dreiecks.
- Wir rufen
fill()auf, um das Dreieck zu füllen. - Wir aktualisieren die Variablen, welche die Abfolge der Dreiecke beschreiben, damit wir das nächste zeichnen können. Wir verringern den Wert
lengthum 1, wodurch die Dreiecke jedes Mal kleiner werden; wir erhöhenmoveOffsetum einen kleinen Betrag, sodass jedes aufeinanderfolgende Dreieck etwas weiter entfernt liegt; und wir verwenden eine weitere neue Funktion,rotate(), mit der wir das gesamte Canvas drehen können! Wir drehen es um 5 Grad, bevor wir das nächste Dreieck zeichnen.
- Wir setzen
Das war's! Das fertige Beispiel sollte wie folgt aussehen:
Drücken Sie die Schaltfläche Play, um das Beispiel in MDN Playground zu öffnen und den Quellcode zu bearbeiten. Wir möchten Sie dazu ermutigen, mit dem Beispiel zu experimentieren und es zu Ihrem eigenen zu machen! Zum Beispiel:
- Zeichnen Sie statt Dreiecken Rechtecke oder Kreisbögen oder betten Sie sogar Bilder ein.
- Experimentieren Sie mit den Werten
lengthundmoveOffset. - Fügen Sie mithilfe der oben eingefügten, aber nicht verwendeten Funktion
rand()einige Zufallszahlen hinzu.
Animationen
Das oben erstellte Schleifenbeispiel hat Spaß gemacht, aber für ernsthafte Canvas-Anwendungen (etwa Spiele und Echtzeitvisualisierungen) benötigen Sie wirklich eine konstante Schleife, die immer weiterläuft. Wenn Sie Ihr Canvas als Film betrachten, möchten Sie die Anzeige in jedem Frame aktualisieren, um die aktualisierte Ansicht zu zeigen. Idealerweise beträgt die Bildwiederholrate 60 Frames pro Sekunde, damit Bewegungen für das menschliche Auge angenehm flüssig erscheinen.
Es gibt einige JavaScript-Funktionen, mit denen Sie Funktionen mehrmals pro Sekunde wiederholt ausführen können. Die beste für unsere Zwecke ist window.requestAnimationFrame(). Sie akzeptiert einen Parameter — den Namen der Funktion, die für jeden Frame ausgeführt werden soll. Wenn der Browser das nächste Mal bereit ist, den Bildschirm zu aktualisieren, wird Ihre Funktion aufgerufen. Wenn diese Funktion die neue Aktualisierung für Ihre Animation zeichnet und dann kurz vor Ende der Funktion erneut requestAnimationFrame() aufruft, läuft die Animationsschleife weiter. Die Schleife endet, wenn Sie requestAnimationFrame() nicht mehr aufrufen oder wenn Sie nach dem Aufruf von requestAnimationFrame(), aber vor dem Aufruf des Frames, window.cancelAnimationFrame() aufrufen.
Hinweis:
Es ist eine bewährte Vorgehensweise, cancelAnimationFrame() aus Ihrem Hauptcode aufzurufen, wenn Sie mit der Animation fertig sind, um sicherzustellen, dass keine Aktualisierungen mehr auf die Ausführung warten.
Der Browser kümmert sich um komplexe Details, beispielsweise darum, dass die Animation mit einer gleichmäßigen Geschwindigkeit ausgeführt wird und keine Ressourcen für die Animation von Dingen verschwendet werden, die nicht sichtbar sind.
Um zu sehen, wie dies funktioniert, betrachten wir noch einmal kurz unser Beispiel mit springenden Bällen. Der Code für die Schleife, die alles in Bewegung hält, sieht folgendermaßen aus:
function loop() {
ctx.fillStyle = "rgb(0 0 0 / 25%)";
ctx.fillRect(0, 0, width, height);
for (const ball of balls) {
ball.draw();
ball.update();
ball.collisionDetect();
}
requestAnimationFrame(loop);
}
loop();
Wir führen die Funktion loop() am Ende des Codes einmal aus, um den Zyklus zu starten und den ersten Animationsframe zu zeichnen. Die Funktion loop() übernimmt dann den Aufruf von requestAnimationFrame(loop), um immer wieder den nächsten Frame der Animation auszuführen.
Beachten Sie, dass wir bei jedem Frame das Canvas vollständig leeren und alles neu zeichnen. Für jeden vorhandenen Ball zeichnen wir ihn, aktualisieren seine Position und prüfen, ob er mit anderen Bällen kollidiert. Sobald Sie eine Grafik auf ein Canvas gezeichnet haben, gibt es keine Möglichkeit, diese Grafik einzeln zu manipulieren, wie Sie es mit DOM-Elementen können. Sie können nicht jeden Ball auf dem Canvas verschieben, denn sobald er gezeichnet wurde, ist er Teil des Canvas und kein einzelnes zugängliches Element oder Objekt. Stattdessen müssen Sie löschen und neu zeichnen — entweder indem Sie den gesamten Frame löschen und alles neu zeichnen oder indem Sie Code haben, der genau weiß, welche Bereiche gelöscht werden müssen, und nur den minimal erforderlichen Bereich des Canvas löscht und neu zeichnet.
Die Optimierung der Grafikanimation ist ein eigenes Fachgebiet der Programmierung, in dem viele clevere Techniken verfügbar sind. Diese gehen jedoch über das hinaus, was wir für unser Beispiel benötigen!
Im Allgemeinen umfasst der Prozess einer Canvas-Animation die folgenden Schritte:
- Den Canvas-Inhalt löschen, beispielsweise mit
fillRect()oderclearRect(). - Den Zustand bei Bedarf mit
save()speichern — dies ist nötig, wenn Sie Einstellungen speichern möchten, die Sie auf dem Canvas aktualisiert haben, bevor Sie fortfahren. Das ist für fortgeschrittenere Anwendungen nützlich. - Die Grafiken zeichnen, die Sie animieren.
- Die in Schritt 2 gespeicherten Einstellungen mit
restore()wiederherstellen. requestAnimationFrame()aufrufen, um das Zeichnen des nächsten Animationsframes zu planen.
Hinweis:
Wir behandeln save() und restore() hier nicht, aber sie werden in unserem Tutorial Transformationen (und den darauf folgenden Tutorials) gut erklärt.
Animation eines gehenden Objekts
Erstellen wir nun unsere eigene einfache Animation — wir animieren ein sich über den Bildschirm bewegendes Objekt mit einem Sprite-Sheet.
-
Erstellen Sie eine weitere neue Kopie unserer Canvas-Vorlage und öffnen Sie sie in Ihrem Code-Editor.
-
Aktualisieren Sie das Fallback-HTML so, dass es das Bild widerspiegelt:
html<canvas class="myCanvas"> <p>A cat walking.</p> </canvas> -
Dieses Mal färben wir den Hintergrund nicht schwarz. Malen Sie daher nach dem Abrufen der Variable
ctxden Hintergrund stattdessen hellgrau:jsctx.fillStyle = "#e5e6e9"; ctx.fillRect(0, 0, width, height); -
Fügen Sie am Ende des JavaScript die folgende Zeile hinzu, um den Koordinatenursprung wieder in die Mitte des Canvas zu setzen:
jsctx.translate(width / 2, height / 2); -
Erstellen wir nun ein neues
HTMLImageElement-Objekt, setzen dessensrcauf das zu ladende Bild und fügen einenonload-Ereignishandler hinzu, der die Funktiondraw()ausführt, wenn das Bild geladen wurde:jsconst image = new Image(); image.src = "https://developer.mozilla.org/shared-assets/images/examples/web-animations/cat_sprite.png"; image.onload = draw; -
Nun fügen wir einige Variablen hinzu, um die Position nachzuverfolgen, an der das Sprite auf dem Bildschirm gezeichnet werden soll, und die Nummer des anzuzeigenden Sprites.
jslet spriteIndex = 0; let posX = 0; const spriteWidth = 300; const spriteHeight = 150; const totalSprites = 12;Das Sprite-Bild wurde von Rachel Nabors erstellt und mit deren freundlicher Genehmigung für ihre Dokumentationsarbeit an der Web Animations API geteilt. Es sieht wie folgt aus:

Es hat drei Spalten. Jede Spalte ist eine Sequenz, die eine Katze in einem anderen Tempo zeigt (gehend, trabend und galoppierend). Jede Sequenz enthält entweder 12 oder 13 Sprites — jedes ist 300 Pixel breit und 150 Pixel hoch. Wir verwenden die linke Gehsequenz, die 12 Sprites enthält. Um jedes Sprite sauber darzustellen, müssen wir
drawImage()verwenden, um ein einzelnes Sprite-Bild aus dem Sprite-Sheet auszuschneiden und nur diesen Teil anzuzeigen, so wie wir es oben mit dem Firefox-Logo getan haben. Die X- und Y-Koordinaten des Ausschnitts müssen jeweils ein Vielfaches vonspriteWidthundspriteHeightsein. Da wir die am weitesten links liegende Sequenz verwenden, ist die X-Koordinate immer 0. Die Größe des Ausschnitts ist immerspriteWidthmalspriteHeight. -
Fügen wir nun am Ende des Codes eine leere Funktion
draw()ein, die wir anschließend mit Code füllen können:jsfunction draw() {} -
Der Rest des Codes in diesem Abschnitt kommt in
draw(). Fügen Sie zunächst die folgende Zeile hinzu, die das Canvas löscht, um es für das Zeichnen jedes Frames vorzubereiten. Beachten Sie, dass wir die obere linke Ecke des Rechtecks als-(width / 2), -(height / 2)angeben müssen, da wir die Ursprungsposition zuvor aufwidth/2, height/2gesetzt haben.jsctx.fillRect(-(width / 2), -(height / 2), width, height); -
Als Nächstes zeichnen wir unser Bild mit der Version von
drawImagemit 9 Parametern. Fügen Sie Folgendes hinzu:jsctx.drawImage( image, 0, spriteIndex * spriteHeight, spriteWidth, spriteHeight, 0 + posX, -spriteHeight / 2, spriteWidth, spriteHeight, );Wie Sie sehen können:
- Wir geben
imageals einzubettendes Bild an. - Parameter 2 und 3 geben die obere linke Ecke des aus dem Quellbild auszuschneidenden Ausschnitts an. Der X-Wert ist 0 (für die äußerste linke Spalte), und der Y-Wert durchläuft Vielfache von
spriteHeight. Sie können den X-Wert durchspriteWidthoder2 * spriteWidthersetzen, um die anderen Spalten auszuwählen. - Parameter 4 und 5 geben die Größe des auszuschneidenden Ausschnitts an —
spriteWidthundspriteHeight. - Parameter 6 und 7 geben die obere linke Ecke des Felds an, in das der Ausschnitt auf dem Canvas gezeichnet werden soll. Die X-Position ist 0 +
posX, was bedeutet, dass wir die Zeichenposition durch Änderung des WertsposXverändern können. Die Y-Position ist-spriteHeight / 2, was bedeutet, dass das Bild vertikal auf dem Canvas zentriert wird. - Parameter 8 und 9 geben die Größe des Bilds auf dem Canvas an. Wir möchten die Originalgröße beibehalten, daher geben wir
spriteWidthundspriteHeightals Breite und Höhe an.
- Wir geben
-
Nun ändern wir nach jedem Zeichnen den Wert
spriteIndex— nun ja, zumindest nach einigen von ihnen. Fügen Sie den folgenden Block am Ende der Funktiondraw()hinzu:jsif (posX % 11 === 0) { if (spriteIndex === totalSprites - 1) { spriteIndex = 0; } else { spriteIndex++; } }Wir schließen den gesamten Block in
if (posX % 11 === 0) { }ein. Wir verwenden den Modulo-Operator (%), auch bekannt als Restoperator, um zu prüfen, ob der Wert vonposXohne Rest durch 11 teilbar ist. Wenn dies der Fall ist, wechseln wir zum nächsten Sprite, indem wirspriteIndexerhöhen (und nach dem letzten wieder auf 0 zurücksetzen). Das bedeutet praktisch, dass wir das Sprite nur bei jedem 11. Frame aktualisieren, also ungefähr 6 Mal pro Sekunde (requestAnimationFrame()ruft uns nach Möglichkeit bis zu 60 Mal pro Sekunde auf). Wir verlangsamen die Framerate absichtlich, weil wir nur 12 Sprites zur Verfügung haben und sich unser Objekt viel zu schnell bewegen würde, wenn wir eines pro Sechzigstelsekunde anzeigen würden!Innerhalb des äußeren Blocks verwenden wir eine
if...else-Anweisung, um zu überprüfen, ob der WertspriteIndexbeim letzten Sprite angekommen ist. Wenn wir bereits das letzte Sprite anzeigen, setzen wirspriteIndexwieder auf 0 zurück; andernfalls erhöhen wir es einfach um 1. -
Als Nächstes müssen wir herausfinden, wie der Wert von
posXbei jedem Frame geändert wird — fügen Sie den folgenden Codeblock direkt unter Ihrem letzten hinzu.jsif (posX < -width / 2 - spriteWidth) { const newStartPos = width / 2; posX = Math.ceil(newStartPos); } else { posX -= 2; }Wir verwenden eine weitere
if...else-Anweisung, um festzustellen, ob der Wert vonposXkleiner geworden ist als-width/2 - spriteWidth, was bedeutet, dass unsere Katze über den linken Bildschirmrand hinausgelaufen ist. Wenn dies der Fall ist, berechnen wir eine Position, durch die die Katze gerade rechts vom rechten Bildschirmrand platziert wird.Wenn unsere Katze noch nicht über den Bildschirmrand hinausgelaufen ist, verringern wir
posXum 2. Dadurch bewegt sie sich beim nächsten Zeichnen ein kleines Stück nach links. -
Schließlich müssen wir die Animationsschleife erstellen, indem wir am Ende der Funktion
draw()requestAnimationFrame()aufrufen:jswindow.requestAnimationFrame(draw);
Das war's! Das fertige Beispiel sollte wie folgt aussehen:
Sie können die Schaltfläche Play drücken, um das Beispiel in MDN Playground zu öffnen und den Quellcode zu bearbeiten.
Eine einfache Zeichenanwendung
Als letztes Animationsbeispiel möchten wir Ihnen eine sehr einfache Zeichenanwendung zeigen, um zu veranschaulichen, wie die Animationsschleife mit Benutzereingaben kombiniert werden kann — in diesem Fall mit Mausbewegungen. Wir werden Sie nicht durch den Aufbau dieser Anwendung führen; stattdessen betrachten wir nur die interessantesten Teile des Codes.
Sie können unten direkt mit dem Beispiel experimentieren. Außerdem können Sie auf die Schaltfläche Play klicken, um es im MDN Playground zu öffnen, wo Sie den Quellcode bearbeiten können:
Sehen wir uns die interessantesten Teile an. Zunächst verfolgen wir die X- und Y-Koordinaten der Maus sowie die Information, ob sie geklickt wird, mit drei Variablen: curX, curY und pressed. Wenn sich die Maus bewegt, lösen wir eine Funktion aus, die als onmousemove-Ereignishandler festgelegt ist und die aktuellen X- und Y-Werte erfasst. Außerdem verwenden wir die Ereignishandler onmousedown und onmouseup, um den Wert von pressed beim Drücken der Maustaste auf true und beim Loslassen wieder auf false zu setzen.
let curX;
let curY;
let pressed = false;
// update mouse pointer coordinates
document.addEventListener("mousemove", (e) => {
curX = e.pageX;
curY = e.pageY;
});
canvas.addEventListener("mousedown", () => (pressed = true));
canvas.addEventListener("mouseup", () => (pressed = false));
Wenn die Schaltfläche „Clear canvas“ gedrückt wird, führen wir eine einfache Funktion aus, die das gesamte Canvas wieder schwarz löscht, auf dieselbe Weise, die wir zuvor gesehen haben:
clearBtn.addEventListener("click", () => {
ctx.fillStyle = "black";
ctx.fillRect(0, 0, width, height);
});
Die Zeichenschleife ist diesmal recht einfach — wenn pressed den Wert true hat, zeichnen wir einen Kreis mit einem Füllstil, der dem Wert in der Farbauswahl entspricht, und einem Radius, der dem im Bereichseingabefeld festgelegten Wert entspricht. Wir müssen den Kreis 85 Pixel oberhalb der Stelle zeichnen, an der wir ihn gemessen haben, da die vertikale Messung vom oberen Rand des Viewports aus vorgenommen wird, wir den Kreis aber relativ zum oberen Rand des Canvas zeichnen, das unterhalb der 85 Pixel hohen Werkzeugleiste beginnt. Wenn wir nur curY als y-Koordinate verwenden würden, erschiene der Kreis 85 Pixel tiefer als die Mausposition.
function draw() {
if (pressed) {
ctx.fillStyle = colorPicker.value;
ctx.beginPath();
ctx.arc(
curX,
curY - 85,
sizePicker.value,
degToRad(0),
degToRad(360),
false,
);
ctx.fill();
}
requestAnimationFrame(draw);
}
draw();
Alle <input>-Typen werden gut unterstützt. Wenn ein Browser einen Eingabetyp nicht unterstützt, fällt er auf einfache Textfelder zurück.
WebGL
Jetzt ist es Zeit, 2D hinter uns zu lassen und einen kurzen Blick auf 3D-Canvas zu werfen. 3D-Canvas-Inhalte werden mit der WebGL API angegeben, einer vollständig getrennten API von der 2D-Canvas-API, auch wenn beide auf <canvas>-Elementen rendern.
WebGL basiert auf OpenGL (Open Graphics Library) und ermöglicht Ihnen die direkte Kommunikation mit der GPU des Computers. Daher ähnelt das Schreiben von rohem WebGL eher Low-Level-Sprachen wie C++ als gewöhnlichem JavaScript; es ist ziemlich komplex, aber unglaublich leistungsstark.
Eine Bibliothek verwenden
Wegen seiner Komplexität schreiben die meisten Personen 3D-Grafikcode mithilfe einer JavaScript-Bibliothek eines Drittanbieters wie Three.js, PlayCanvas oder Babylon.js. Die meisten funktionieren auf ähnliche Weise und bieten Funktionen zum Erstellen grundlegender und benutzerdefinierter Formen, zum Positionieren von Kameras und Beleuchtung, zum Überziehen von Oberflächen mit Texturen und mehr. Sie übernehmen WebGL für Sie, sodass Sie auf einer höheren Ebene arbeiten können.
Ja, die Verwendung einer solchen Bibliothek bedeutet, eine weitere neue API zu lernen — in diesem Fall eine API eines Drittanbieters —, aber sie sind wesentlich einfacher als das Codieren von rohem WebGL.
Ein sich drehender Würfel
Sehen wir uns ein Beispiel an, wie Sie mit einer WebGL-Bibliothek etwas erstellen können. Wir wählen Three.js, da es eine der beliebtesten Bibliotheken ist. In diesem Tutorial erstellen wir einen sich drehenden 3D-Würfel.
-
Erstellen Sie zunächst auf Ihrer lokalen Festplatte einen neuen Ordner namens
webgl-cube. -
Erstellen Sie darin eine neue Datei namens
index.htmlund fügen Sie den folgenden Inhalt hinzu:html<!doctype html> <html lang="en-US"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width" /> <title>Three.js basic cube example</title> <script src="https://cdn.jsdelivr.net/npm/three-js@79.0.0/three.min.js"></script> <script src="script.js" defer></script> <link href="style.css" rel="stylesheet" /> </head> <body></body> </html> -
Erstellen Sie als Nächstes eine weitere neue Datei namens
script.js, wieder im selben Ordner wie zuvor. Lassen Sie sie vorerst leer. -
Erstellen Sie nun eine weitere neue Datei namens
style.css, wieder im selben Ordner, und fügen Sie den folgenden Inhalt hinzu:csshtml, body { margin: 0; } body { overflow: hidden; } -
Wir haben
three.jsin unsere Seite eingebunden (das geschieht durch das erste<script>-Element in unserem HTML), daher können wir jetzt inscript.jsJavaScript schreiben, das darauf zurückgreift. Beginnen wir mit dem Erstellen einer neuen Szene — fügen Sie Folgendes in Ihre Dateiscript.jsein:jsconst scene = new THREE.Scene();Der Konstruktor
Scene()erstellt eine neue Szene, die die gesamte 3D-Welt darstellt, die wir anzeigen möchten. -
Als Nächstes benötigen wir eine Kamera, damit wir die Szene sehen können. In Bezug auf 3D-Bilder stellt die Kamera die Position einer betrachtenden Person in der Welt dar. Um eine Kamera zu erstellen, fügen Sie als Nächstes die folgenden Zeilen hinzu:
jsconst camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000, ); camera.position.z = 5;Der Konstruktor
PerspectiveCamera()akzeptiert vier Argumente:- Das Sichtfeld: Wie breit der Bereich vor der Kamera ist, der auf dem Bildschirm sichtbar sein soll, in Grad.
- Das Seitenverhältnis: Üblicherweise ist dies das Verhältnis der Breite der Szene dividiert durch ihre Höhe. Bei Verwendung eines anderen Werts wird die Szene verzerrt — was möglicherweise gewünscht ist, aber normalerweise nicht.
- Die Nah-Clipping-Ebene: Wie nah Objekte an der Kamera sein können, bevor wir aufhören, sie auf dem Bildschirm zu rendern. Denken Sie daran, dass Sie Ihre Fingerspitze irgendwann nicht mehr sehen können, wenn Sie sie immer näher an den Bereich zwischen Ihren Augen bewegen.
- Die Fern-Clipping-Ebene: Wie weit Dinge von der Kamera entfernt sein können, bevor sie nicht mehr gerendert werden.
Außerdem setzen wir die Position der Kamera auf 5 Entfernungseinheiten entlang der Z-Achse, die — wie in CSS — aus dem Bildschirm heraus zu Ihnen als betrachtende Person zeigt.
-
Der dritte entscheidende Bestandteil ist ein Renderer. Dabei handelt es sich um ein Objekt, das eine bestimmte Szene rendert, betrachtet durch eine bestimmte Kamera. Wir erstellen zunächst einen mit dem Konstruktor
WebGLRenderer(), verwenden ihn aber erst später. Fügen Sie als Nächstes die folgenden Zeilen hinzu:jsconst renderer = new THREE.WebGLRenderer(); renderer.setSize(window.innerWidth, window.innerHeight); document.body.appendChild(renderer.domElement);Die erste Zeile erstellt einen neuen Renderer, die zweite Zeile legt die Größe fest, in der der Renderer die Kameraansicht zeichnet, und die dritte Zeile hängt das vom Renderer erstellte
<canvas>-Element an das<body>des Dokuments an. Alles, was der Renderer zeichnet, wird nun in unserem Fenster angezeigt. -
Als Nächstes möchten wir den Würfel erstellen, den wir auf dem Canvas anzeigen. Fügen Sie am Ende Ihres JavaScript den folgenden Codeabschnitt hinzu:
jslet cube; const loader = new THREE.TextureLoader(); loader.load( "https://mdn.github.io/shared-assets/images/examples/learn/metal003.png", (texture) => { texture.wrapS = THREE.RepeatWrapping; texture.wrapT = THREE.RepeatWrapping; texture.repeat.set(2, 2); const geometry = new THREE.BoxGeometry(2.4, 2.4, 2.4); const material = new THREE.MeshLambertMaterial({ map: texture }); cube = new THREE.Mesh(geometry, material); scene.add(cube); draw(); }, );Hier gibt es etwas mehr zu erfassen, daher gehen wir es schrittweise durch:
- Zuerst erstellen wir eine globale Variable
cube, damit wir von überall im Code auf unseren Würfel zugreifen können. - Als Nächstes erstellen wir ein neues
TextureLoader-Objekt und rufen dann daraufload()auf.load()akzeptiert in diesem Fall zwei Parameter, obwohl es auch mehr akzeptieren kann: die zu ladende Textur (ein PNG) und eine Funktion, die ausgeführt wird, wenn die Textur geladen wurde. - Innerhalb dieser Funktion verwenden wir Eigenschaften des
texture-Objekts, um festzulegen, dass wir eine 2-mal-2-Wiederholung des Bilds wünschen, die um alle Seiten des Würfels gewickelt wird. Anschließend erstellen wir ein neuesBoxGeometry-Objekt und ein neuesMeshLambertMaterial-Objekt und kombinieren sie in einemMesh, um unseren Würfel zu erstellen. Ein Objekt benötigt typischerweise eine Geometrie (welche Form es hat) und ein Material (wie seine Oberfläche aussieht). - Zum Schluss fügen wir unseren Würfel zur Szene hinzu und rufen dann unsere Funktion
draw()auf, um die Animation zu starten.
- Zuerst erstellen wir eine globale Variable
-
Bevor wir
draw()definieren, fügen wir der Szene noch ein paar Lichter hinzu, um sie etwas zu beleben. Fügen Sie als Nächstes die folgenden Blöcke hinzu:jsconst light = new THREE.AmbientLight("white"); // soft white light scene.add(light); const spotLight = new THREE.SpotLight("white"); spotLight.position.set(100, 1000, 1000); spotLight.castShadow = true; scene.add(spotLight);Ein
AmbientLight-Objekt ist eine Art weiches Licht, das die gesamte Szene etwas aufhellt, ähnlich wie die Sonne, wenn Sie sich im Freien befinden. DasSpotLight-Objekt hingegen ist ein gerichteter Lichtstrahl, eher wie eine Taschenlampe (oder tatsächlich ein Scheinwerfer). -
Fügen wir schließlich unsere Funktion
draw()am Ende des Codes hinzu:jsfunction draw() { cube.rotation.x += 0.01; cube.rotation.y += 0.01; renderer.render(scene, camera); requestAnimationFrame(draw); }Dies ist recht intuitiv: Bei jedem Frame drehen wir unseren Würfel leicht um seine X- und Y-Achse, rendern dann die Szene aus Sicht unserer Kamera und rufen schließlich
requestAnimationFrame()auf, um das Zeichnen des nächsten Frames zu planen.
Das fertige Ergebnis sollte wie folgt aussehen:
Hinweis:
In unserem GitHub-Repository finden Sie außerdem ein weiteres interessantes 3D-Würfelbeispiel — Three.js Video Cube (auch live ansehen). Dieses verwendet getUserMedia(), um einen Videostream von einer Computer-Webcam aufzunehmen und ihn als Textur auf die Seite des Würfels zu projizieren!
Zusammenfassung
An diesem Punkt sollten Sie eine nützliche Vorstellung von den Grundlagen der Grafikprogrammierung mit Canvas und WebGL haben, davon, was Sie mit diesen APIs tun können, sowie eine gute Vorstellung davon, wo Sie weiterführende Informationen finden. Viel Spaß!
Siehe auch
Hier haben wir nur die absoluten Grundlagen von Canvas behandelt — es gibt noch so viel mehr zu lernen! Die folgenden Artikel führen Sie weiter.
- Canvas-Tutorial — Eine sehr ausführliche Tutorialreihe, die deutlich detaillierter als hier erklärt, was Sie über 2D-Canvas wissen sollten. Unverzichtbare Lektüre.
- WebGL-Tutorial — Eine Reihe, die die Grundlagen der Programmierung mit rohem WebGL vermittelt.
- Eine grundlegende Demo mit Three.js erstellen — grundlegendes Three.js-Tutorial. Wir haben auch entsprechende Leitfäden für PlayCanvas und Babylon.js.
- Spieleentwicklung — die Einstiegsseite für die Entwicklung von Webspielen auf MDN. Hier sind einige wirklich nützliche Tutorials und Techniken zu 2D- und 3D-Canvas verfügbar — siehe die Menüoptionen Techniken und Tutorials.
Beispiele
- Violent theremin — Verwendet die Web Audio API, um Ton zu erzeugen, und Canvas, um eine ansprechende Visualisierung dazu zu generieren.
- Voice change-o-matic — Verwendet ein Canvas, um Echtzeit-Audiodaten aus der Web Audio API zu visualisieren.