Canvas
Bei einem Canvas (engl. für Leinwand) handelt es sich um ein HTML-Element, mit welchem wir eine Zeichenfläche
(also sozusagen eine Leinwand) definieren können. Die Zeichenfläche wird dabei in HTML über das canvas
-Element
angelegt. Der Zeichenvorgang erfolgt dann mittels JavaScript.
Das canvas
-Element ist zweiteilig, wovon zwischen den Tags ein Text angegeben werden kann, welcher angezeigt
werden soll, wenn das Element nicht unterstützt wird. Die Größe der Zeichenfläche wird in Pixel durch die Attribute
width
und height
(Angabe ohne px
-Suffix) angegeben. Beide Attribute sollten dabei
für ein korrektes Verhalten angegeben werden. Um das canvas
-Element anzusprechen, können wir diesem z. B.
eine ID geben (siehe Beispiel).
<canvas id="zeichnung" width="400" height="200"></canvas>
Um in JavaScript nun auf die Zeichenfläche zuzugreifen, müssen wir das Node
-Objekt des Elements laden (im
Beispiel mittels getElementById()
) und die Funktion getContext()
mit der Zeichenkette
"2d"
als Parameter aufrufen.
var ctx = document.getElementById("zeichnung").getContext("2d");
Übrigens: Der oft verwendete Variablenname ctx
, im Bezug auf Zeichenflächen, stellt die Abkürzung für
context dar.
Bevor wir im nächsten Schritt damit beginnen, unsere ersten Rechtecke zu zeichnen, wollen wir zuerst noch ein paar
Eigenschaften vorstellen. Der Eigenschaft strokeStyle
kann ein Farbwert zugewiesen werden und stellt somit
die Linienfarbe dar. Als Angabe sind, so wie in CSS auch, Farbnamen, RGB-Werte, RGBA-Werte, Hex-Werte, HSL-Werte
und HSLA-Werte möglich. Die Eigenschaft fillStyle
legt die Farbe zum Ausfüllen (Füllfarbe) fest. Beide
Eigenschaften können während des kompletten Zeichenvorgangs mehrmals geändert werden, sodass z. B. die eine Linie rot gefärbt
wird und die andere blau. Mit Hilfe der Eigenschaft lineWidth
lässt sich die Breite der Linie (Strichstärke)
ändern. Die Angabe erfolgt als Pixel-Wert (ohne Einheit), wovon 1
die Standardeinstellung ist. Natürlich gibt es
noch viele weitere Eigenschaften, diese sind für den Anfang jedoch nicht relevant und werden nur selten benutzt.
Rechtecke
Um Rechtecke ohne Pfade zu zeichnen (dazu im nächsten Schritt mehr), gibt es die Funktionen strokeRect()
und
fillRect()
. Die Funktion strokeRect()
erzeugt ein Rechteck ohne „Füllung“ ggf. jedoch
mit einem Rahmen (je nach Einstellung von strokeStyle
und lineWidth
). Die Funktion
fillRect()
hingegen erzeugt ein ausgefülltes Rechteck (jedoch ohne Rahmen). Zur Einstellung der
Farbe dient, wie oben bereits beschrieben, die Eigenschaft fillStyle
. Beiden Funktionen werden 4 Parameter
übergeben, wovon der erste die X-Position und der zweite Y-Position festlegen. Beide Positionen beziehen sich
dabei auf die Ecke oben links. Der dritte und vierte Parameter legt die Breite und Höhe fest. Auch hier werden
alle Parameterwerte in Pixel als Zahl angegeben.
var ctx = document.getElementById("zeichnung").getContext("2d"); ctx.strokeStyle = "red"; ctx.strokeRect(50, 50, 50, 50); ctx.strokeStyle = "lime"; ctx.lineWidth = 10; ctx.strokeRect(200, 10, 60, 80); ctx.fillStyle = "blue"; ctx.fillRect(150, 110, 200, 80);
Wichtig: Das canvas
-Element kann als eine Art Koordinatensystem angesehen werden. Der Nullpunkt
(X=0 und Y=0) befindet sich hier jedoch in der Ecke oben links. Haben wir z. B. eine Zeichenfläche mit der Größe von
300x200px, so hat die Ecke unten rechts die Position X=300 und Y=200.
Pfade
Um Linien oder andere Formen zu zeichnen, gibt es einige weitere Funktionen. Hier spricht man nun zumeist von Pfaden.
Ein Pfad (engl. path) kann dabei aus ein oder mehreren Linien (diese können gerade oder gekrümmt sein) bestehen.
Um das Zeichnen eines Pfades zu beginnen, müssen wir zuerst die Funktion beginPath()
aufrufen. Am Ende der
Pfad-Zeichnung rufen wir die Funktion fill()
oder stroke()
auf. fill()
füllt dabei
den „gewählten“ Bereich mit der Füllfarbe aus, stroke()
hingegen zeichnet lediglich die Kontur.
Zum Zeichnen von Linien gibt es die Funktionen moveTo()
und lineTo()
. Beiden Funktionen werden
als erster Parameter die X-Position und als zweiter Parameter die Y-Position übergeben. Die Funktion moveTo()
positioniert lediglich den „Cursor“, wohingegen die Funktion lineTo()
eine Linie von der letzten Position bis
zur angegebenen Position zeichnet. Eine nützliche Funktion ist closePath()
. Mit dieser ist es möglich, wieder
an den Ausgangspunkt des Pfades zu springen, um somit eine Form zu schließen.
Die Funktion arc()
erlaubt es, einen Kreis bzw. eine Ellipse oder einen Kreisteil bzw. Ellipsenteil zu zeichnen.
Der erste Parameter stellt die X-Position des Kreismittelpunkts dar, der zweite Parameter stellt die Y-Position des
Kreismittelpunkts dar und der dritte Parameter gibt den Kreisradius an. Des Weiteren benötigen wir noch zwei Parameter, welche
die Winkelpositionen angeben. Die Angabe der Winkelpositionen erfolgt mittels der Konstante PI
, welche im
Math
-Objekt hinterlegt ist. So entspricht z. B. 0.5 * Math.PI
dem Winkel 90° und 2 * Math.PI
360°. Der vierte Funktionsparameter gibt den Startwinkel an, dabei entspricht 0 der 3-Uhr-Position der Ellipse. Der fünfte
Parameter gibt den Endwinkel an.
Mit Hilfe der Funktion arcTo()
ist es möglich, eine Kurve bzw. einen Bogen (engl. arc) zu zeichnen.
Die Zeichnung erfolgt zwischen zwei Kontrollpunkten, welche als Funktionsparameter (Reihenfolge: x1, y1, x2, y2
)
übergeben werden müssen. Der fünfte Parameter gibt den Radius des Bogens an. Hier lohnt es sich, mit den Funktionsparametern
etwas zu experimentieren, bis man die Funktionsweise der Funktion besser verstanden hat.
Um ein Rechteck mittels Pfade zu zeichnen (z. B. um Transformationen darauf anzuwenden), gibt es die Funktion rect()
.
Die Funktion rect()
hat die gleichen Parameter wie strokeRect()
und fillRect()
.
var ctx = document.getElementById("zeichnung").getContext("2d"); // Zick-Zack-Linien zeichnen ctx.fillStyle = "red"; ctx.beginPath(); ctx.moveTo(0, 0); ctx.lineTo(50, 200); ctx.lineTo(100, 0); ctx.lineTo(150, 200); ctx.lineTo(200, 0); ctx.lineTo(250, 200); ctx.lineTo(300, 0); ctx.lineTo(350, 200); ctx.lineTo(400, 0); ctx.fill(); // Dreieck unten mitte ctx.strokeStyle = "blue"; ctx.beginPath(); ctx.moveTo(175, 200); ctx.lineTo(200, 100); ctx.lineTo(225, 200); ctx.closePath(); // entspricht => ctx.lineTo(175, 200); ctx.stroke(); // Ellipse unten links ctx.fillStyle = "lime"; ctx.beginPath(); ctx.arc(100, 150, 25, 0.6 * Math.PI, 1.8 * Math.PI); ctx.fill(); // kurvige Linie unten rechts ctx.strokeStyle = "lime"; ctx.beginPath(); ctx.moveTo(280, 200); ctx.lineTo(290, 150); ctx.arcTo(300, 130, 310, 150, 10); ctx.lineTo(320, 200); ctx.stroke(); // Rechteck oben mitte ctx.fillStyle = "yellow"; ctx.beginPath(); ctx.rect(150, 25, 100, 50); ctx.fill();
Texte
Zum Zeichnen eines Texts gibt es die Funktion strokeText()
und fillText()
. Als Parameter wird der Text
sowie die X- und Y-Position übergeben. Zum Einstellen der Schriftart, Schriftgröße etc. können wir die Eigenschaft font
nutzen. Dabei muss eine Zeichenkette gesetzt werden, welche mehrere Einstellungen umfassen kann. Hier muss vor allem auf die
Reihenfolge geachtet werden: font-style
, font-variant
, font-weight
, font-size
und font-family
. Die Eigenschafts-Namen und Werte sind dabei von CSS
abzuleiten.
var ctx = document.getElementById("zeichnung").getContext("2d"); ctx.fillStyle = "red"; ctx.font = "30px Times New Roman"; ctx.fillText("Herzlich Willkommen auf", 20, 75); ctx.strokeStyle = "blue"; ctx.font = "bold 35px Arial"; ctx.strokeText("Homepage-Webhilfe", 40, 150);
Bilder
Mit Hilfe der Funktion drawImage()
ist es möglich, ein Bild innerhalb des canvas
-Elements zu zeichnen.
Das Bild selbst muss dabei bereits auf der Seite verwendet werden (mittels img
-Element). Wollen wir das Bild ausschließlich
im canvas
-Element anzeigen, muss das Bild zwar ebenfalls mittels des img
-Elements eingebunden werden,
jedoch können wir das Bild selbst mit der CSS-Eigenschaft display
und dem Wert none
(siehe Beispiel)
ausblenden. Zusätzlich zu dem Bild (Angabe als Node
-Objekt) muss der Funktion noch die X- und Y-Position übergeben werden.
Bei Bedarf kann noch zusätzlich die Breite und Höhe angegeben werden.
<img src="/Bilder/Logo/Logo.jpg" alt="Logo" id="logo" style="display: none;" /> <canvas id="zeichnung" width="400" height="200"></canvas>
window.onload = function () { var ctx = document.getElementById("zeichnung").getContext("2d"); ctx.drawImage(document.getElementById("logo"), 100, 0, 200, 200); };
Wichtig: In dem obigen Beispiel ist es nochmals besonders wichtig, dass die Canvas-Funktion drawImage()
erst
aufgerufen wird, wenn die Seite (und somit auch das Bild) vollständig geladen ist.
Transformationen
Transformationen erlauben es, zu zeichnende Bestandteile, wie z. B. Rechtecke, Linien, Kreise, Texte oder Bilder, zu rotieren,
verschieben, skalieren und neigen. Dafür stehen uns die Funktionen rotate()
, translate()
, scale()
,
transform()
und setTransform()
zur Verfügung, welche wir Ihnen gleich noch genauer erklären werden.
Davor wollen wir uns jedoch mit der Funktionsweise aller Transformationsfunktionen sowie den Funktionen save()
und
resotre()
beschäftigen: Alle ausgeführten Transformationen werden gespeichert (und dies nicht nur für den
aktuellen Pfad). Dies hat zur Folge, dass wenn wir z. B. zwei Rechtecke zeichnen und beide mittels der rotate()
-Funktion
und einer Angabe von 1° rotieren, dass das erste Rechteck wie gewollt um 1° rotiert wird, das zweite jedoch schon um 2°. Dies kann u.
U. gewünscht sein oder eben auch nicht. Um diesem Szenario entgegenzuwirken, gibt es die Funktionen save()
und
restore()
. Beim Aufruf der Funktion save()
werden Transformations-Einstellungen sowie viele andere Parameter
(u. a. strokeStyle
, fillStyle
, lineWidth
und font
) gespeichert. Um diese
Einstellungen wieder zurückzuholen, gibt es die Funktion restore()
. Innerhalb eines Zeichenvorgangs kann natürlich
mehrmals die Funktion save()
und restore()
aufgerufen werden. save()
baut dabei einen Einstellungsstapel
von unten nach oben auf, welcher von restore()
von oben nach unten wieder abgebaut wird. Für alle Transformationsfunktionen
gilt zusätzlich noch, dass die Transformationen nur für Bestandteile gelten, welche nach dem Aufruf der Transformationsfunktion gezeichnet
wurden.
Die Funktion rotate()
kann zum Rotieren verwendet werden. Der Funktion muss dabei ein Wert in der Einheit Radiant übergeben
werden. Um Grad in Radiant umzuwandeln, gilt die Formel grad * (Math.PI / 180)
. Die Rotation selbst erfolgt immer vom Nullpunkt
der Zeichenfläche aus.
Die Funktion translate()
verschiebt den Nullpunkt der Zeichenfläche, wovon zu beachten ist, dass Überstände nicht gezeichnet
werden. Als Parameter werden der translate()
-Funktion die neue X- und Y-Position übergeben.
Mit Hilfe der Funktion scale()
lässt sich eine Zeichnung skalieren. Hierfür werden der Funktion zwei Parameter übergeben,
welche den Skalierungsfaktor (z. B. 1 = 100%, 1.5 = 150%) für die X- und Y-Achse festlegen. Bei Verwendung der scale()
-Funktion
wird neben der Größe (Breite und Höhe) auch die Positionsangabe (X- und Y-Position) skaliert, wovon sich ein Teil des Ergebnisses ähnlich
wie eine Verschiebung auswirkt.
Im folgenden Beispiel wird bewusst die Transformation innerhalb der Schleife nicht zurückgesetzt. Das Beispiel zeigt ein blaues Rechteck, welches immer mehr im Uhrzeigersinn rotiert wird, ein rotes Rechteck, welches immer weiter verschoben wird, und ein grünes Rechteck, welches immer größer wird (und somit auch immer weiter verschoben wird).
var ctx = document.getElementById("zeichnung").getContext("2d"); ctx.save(); ctx.fillStyle = "blue"; for (var i = 0; i < 30; i++) { ctx.beginPath(); ctx.rotate(1 * (Math.PI / 180)); ctx.rect(100, 50, 100, 50); ctx.fill(); } ctx.restore(); ctx.save(); ctx.fillStyle = "red"; for (var i = 0; i < 30; i++) { ctx.beginPath(); ctx.translate(1, 1); ctx.rect(225, 25, 100, 50); ctx.fill(); } ctx.restore(); ctx.save(); ctx.fillStyle = "lime"; for (var i = 0; i < 5; i++) { ctx.beginPath(); ctx.scale(1.1, 1.1); ctx.rect(195, 95, 25, 25); ctx.fill(); }
Neben den zwei bisher vorgestellten Funktionen zur Transformation gibt es noch die Funktionen transform()
und setTransform()
.
Beiden Funktionen werden 6 Parameter übergeben, mit welchen eine Skalierung, Neigung und Verschiebung angegeben werden kann. Dabei muss
vor allem auf die Reihenfolge geachtet werden: horizontale Skalierung, horizontale Neigung, vertikale Neigung, vertikale Skalierung, horizontale
Verschiebung und vertikale Verschiebung. transform()
verhält sich wie die bereits vorgestellten Transformationsfunktionen, d. h. die
Transformationseinstellungen werden „gespeichert“ und summieren sich u. U. bei mehrmaligem aufrufen. setTransform()
hingegen setzt
zuerst die Transformationseinstellungen zurück und führt dann die Transformation aus. Im Beispiel ist dieser Unterschied klar zu erkennen.
var ctx = document.getElementById("zeichnung").getContext("2d"); ctx.save(); ctx.fillStyle = "blue"; for (var i = 0; i < 15; i++) { ctx.beginPath(); ctx.transform(1, 0, 0, 1, i, i); ctx.rect(25, 25, 50, 50); ctx.fill(); } for (var i = 0; i < 15; i++) { ctx.beginPath(); ctx.setTransform(1, 0, 0, 1, i, i); ctx.rect(150, 25, 50, 50); ctx.fill(); } ctx.restore(); ctx.fillStyle = "red"; for (var i = 0; i < 15; i++) { ctx.beginPath(); ctx.transform(1, 0, (i / 100), 1, 0, 0); ctx.rect(250, 25, 50, 50); ctx.fill(); } for (var i = 0; i < 15; i++) { ctx.beginPath(); ctx.setTransform(1, 0, (i / 100), 1, 0, 0); ctx.rect(250, 125, 50, 50); ctx.fill(); }