EMZETT.
Login

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>
MethodeWirkung
fillRect, strokeRect, clearRectRechtecke füllen/umranden/löschen
beginPath, moveTo, lineTo, closePathPfade zeichnen
arc, quadraticCurveTo, bezierCurveToKreise und Kurven
fill, strokePfad füllen / umranden
fillText, strokeTextText
drawImageBild oder anderes Canvas
save, restore, translate, rotate, scaleZustand 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

  1. Eingabe lesen (Tasten, Maus, Touch)
  2. Zustand aktualisieren (Positionen, Punkte, Kollisionen)
  3. Zeichnen (Bildfläche leeren, alles neu malen)
  4. 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
false

Scharf 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: requestAnimationFrame und 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

Weiter im Kurs

Zurück: Nützliche Browser-APIs

Weiter: Node.js Grundlagen

Alle Kapitel: JavaScript im Überblick