Kurz: Das HTML-Element <canvas> ist eine Zeichenfläche, auf der du per JavaScript Formen, Bilder und Animationen erzeugst. Auf Canvas basieren viele Browserspiele und Diagramme.
Teil des Kurses JavaScript
Kapitel 33 von 40 im Kurs JavaScript. Mit Fortschritt, Quiz und Zertifikat auf der Lernseite.
Das HTML-Element <canvas> ist eine Zeichenfläche, auf der du per JavaScript Formen, Bilder und Animationen erzeugst. Auf Canvas basieren viele Browserspiele und Diagramme.
Erste Zeichnung
canvas1.html
<canvas id="c" width="300" height="160" style="border:1px solid #888"></canvas>
<script>
const ctx = document.getElementById("c").getContext("2d");
ctx.fillStyle = "steelblue";
ctx.fillRect(20, 20, 100, 60); // x, y, Breite, Höhe
ctx.strokeStyle = "crimson";
ctx.lineWidth = 4;
ctx.strokeRect(140, 20, 100, 60);
ctx.beginPath();
ctx.arc(70, 120, 25, 0, Math.PI * 2); // Kreis: x, y, Radius, Start, Ende
ctx.fillStyle = "gold";
ctx.fill();
ctx.stroke();
ctx.font = "20px sans-serif";
ctx.fillStyle = "black";
ctx.fillText("Hallo Canvas", 130, 130);
</script>Das Koordinatensystem beginnt oben links (0, 0); x wächst nach rechts, y nach unten.
Linien und Pfade
canvas2.html
<canvas id="c" width="300" height="140" style="border:1px solid #888"></canvas>
<script>
const ctx = document.getElementById("c").getContext("2d");
ctx.beginPath();
ctx.moveTo(30, 110);
ctx.lineTo(80, 30);
ctx.lineTo(130, 110);
ctx.closePath();
ctx.fillStyle = "#4caf50";
ctx.fill();
ctx.beginPath();
ctx.moveTo(160, 110);
ctx.quadraticCurveTo(210, 10, 270, 110);
ctx.strokeStyle = "purple";
ctx.lineWidth = 3;
ctx.stroke();
</script>| Methode | Wirkung |
|---|---|
fillRect, strokeRect, clearRect | Rechtecke füllen/umranden/löschen |
beginPath, moveTo, lineTo, closePath | Pfade zeichnen |
arc, quadraticCurveTo, bezierCurveTo | Kreise und Kurven |
fill, stroke | Pfad füllen / umranden |
fillText, strokeText | Text |
drawImage | Bild oder anderes Canvas |
save, restore, translate, rotate, scale | Zustand und Transformationen |
Animation mit requestAnimationFrame
Eine Animation zeichnet immer wieder ein neues Bild. requestAnimationFrame ruft deine Funktion passend zur Bildwiederholrate (meist 60-mal pro Sekunde) auf:
animation.html
<canvas id="c" width="320" height="160" style="border:1px solid #888;background:#111"></canvas>
<script>
const c = document.getElementById("c"), ctx = c.getContext("2d");
const ball = { x: 40, y: 40, vx: 120, vy: 90, r: 14 }; // Geschwindigkeit in Pixeln pro Sekunde
let letzte = performance.now();
function schritt(jetzt) {
const dt = (jetzt - letzte) / 1000; // Sekunden seit dem letzten Bild
letzte = jetzt;
ball.x += ball.vx * dt;
ball.y += ball.vy * dt;
if (ball.x < ball.r || ball.x > c.width - ball.r) ball.vx *= -1;
if (ball.y < ball.r || ball.y > c.height - ball.r) ball.vy *= -1;
ctx.clearRect(0, 0, c.width, c.height);
ctx.beginPath();
ctx.arc(ball.x, ball.y, ball.r, 0, Math.PI * 2);
ctx.fillStyle = "orange";
ctx.fill();
requestAnimationFrame(schritt);
}
requestAnimationFrame(schritt);
</script>Der Trick: Bewegung hängt von der vergangenen Zeit (dt) ab, nicht von der Anzahl der Bilder. So läuft die Animation auf schnellen und langsamen Geräten gleich schnell.
Steuerung per Tastatur
steuerung.html
<canvas id="c" width="320" height="140" style="border:1px solid #888" tabindex="0"></canvas>
<p>Klicke aufs Feld und steuere mit den Pfeiltasten.</p>
<script>
const c = document.getElementById("c"), ctx = c.getContext("2d");
const spieler = { x: 150, y: 60, groesse: 24 };
const gedrueckt = new Set();
window.addEventListener("keydown", e => { if (e.key.startsWith("Arrow")) { e.preventDefault(); gedrueckt.add(e.key); } });
window.addEventListener("keyup", e => gedrueckt.delete(e.key));
function schleife() {
if (gedrueckt.has("ArrowLeft")) spieler.x -= 3;
if (gedrueckt.has("ArrowRight")) spieler.x += 3;
if (gedrueckt.has("ArrowUp")) spieler.y -= 3;
if (gedrueckt.has("ArrowDown")) spieler.y += 3;
spieler.x = Math.max(0, Math.min(c.width - spieler.groesse, spieler.x));
spieler.y = Math.max(0, Math.min(c.height - spieler.groesse, spieler.y));
ctx.clearRect(0, 0, c.width, c.height);
ctx.fillStyle = "tomato";
ctx.fillRect(spieler.x, spieler.y, spieler.groesse, spieler.groesse);
requestAnimationFrame(schleife);
}
schleife();
</script>Typische Spielschleife
- Eingabe lesen (Tasten, Maus, Touch)
- Zustand aktualisieren (Positionen, Punkte, Kollisionen)
- Zeichnen (Bildfläche leeren, alles neu malen)
- Nächsten Frame anfordern
Kollisionen zwischen Rechtecken prüfst du so:
const treffer = (a, b) =>
a.x < b.x + b.w && a.x + a.w > b.x && a.y < b.y + b.h && a.y + a.h > b.y;
console.log(treffer({ x: 0, y: 0, w: 10, h: 10 }, { x: 5, y: 5, w: 10, h: 10 }));
console.log(treffer({ x: 0, y: 0, w: 10, h: 10 }, { x: 20, y: 0, w: 10, h: 10 }));Ausgabe:
true
falseScharf auf Retina-Bildschirmen
Auf hochauflösenden Displays wirkt Canvas unscharf. Skaliere die Zeichenfläche mit devicePixelRatio:
const dpr = window.devicePixelRatio || 1;
canvas.width = 300 * dpr; canvas.height = 150 * dpr;
canvas.style.width = "300px"; canvas.style.height = "150px";
ctx.scale(dpr, dpr);Merke
canvas.getContext("2d")liefert den Zeichenkontext; Ursprung oben links- Rechtecke, Pfade, Kreise, Text und Bilder lassen sich zeichnen
- Animation:
requestAnimationFrameund Bewegung nach vergangener Zeit - Spielschleife: Eingabe, Zustand ändern, zeichnen
- Kollision zweier Rechtecke durch vier Vergleiche
Übungsaufgabe
Lass einen Ball über die Zeichenfläche springen und färbe ihn bei jedem Wandkontakt anders.
Quiz zur Selbstkontrolle
Wo liegt der Ursprung (0, 0) im Canvas?
- Oben links (richtig)
- Unten links
- In der Mitte
- Oben rechts
Wofür ist requestAnimationFrame?
- Für Animationen im Takt der Bildwiederholrate (richtig)
- Zum Speichern von Bildern
- Zum Laden von Bildern
- Für Timer in Sekunden
Warum rechnet man Bewegung mit der vergangenen Zeit (dt)?
- Damit die Geschwindigkeit auf allen Geräten gleich bleibt (richtig)
- Damit der Code kürzer ist
- Weil Canvas es verlangt
- Damit weniger gezeichnet wird
Weiter im Kurs
Zurück: Nützliche Browser-APIs
Weiter: Node.js Grundlagen
Alle Kapitel: JavaScript im Überblick