Tips & Tricks (Aktualisiert: 2.6.2026)

Canvas-Entwicklung mit Claude Code: HiDPI, RAF, Pointer und Tests

So baust du robuste Canvas-UIs mit Claude Code: HiDPI, requestAnimationFrame, Pointer Events, State und Playwright.

Canvas-Entwicklung mit Claude Code: HiDPI, RAF, Pointer und Tests

Canvas scheitert oft an den Details

Canvas ist stark, wenn normale DOM-Elemente nicht reichen: eigene Diagramme, Bildannotationen, Partikel, kleine Spiele, Lernsimulationen oder Produktkonfiguratoren. Die Freiheit hat aber einen Preis. Der Browser merkt sich keine gezeichneten Linien als DOM-Knoten, korrigiert nicht automatisch die Pixeldichte und beendet keine Animationsschleife für dich. Ein Demo kann auf dem Desktop sauber aussehen und auf dem Smartphone unscharf, unbedienbar oder zu breit sein.

Claude Code ist dann hilfreich, wenn du nicht nur einen Effekt, sondern einen Produktionspfad anforderst. Dazu gehören HiDPI, requestAnimationFrame, Pointer Events, State-Management, Resize-Verhalten, Playwright-Screenshots und die Frage, ob Text, Anzeigen und CTA weiter funktionieren. Gerade bei Content-Seiten ist Canvas selten allein auf der Seite.

Passende Vertiefungen sind Claude Code Animation, Claude Code Three.js 3D und Claude Code Datenvisualisierung. Offizielle Referenzen: Claude Code Docs, MDN Canvas API, requestAnimationFrame, Pointer Events und Playwright Screenshots.

Der richtige Prompt

HiDPI bedeutet hohe Pixeldichte. Ein CSS-Pixel kann auf dem Gerät mehrere physische Pixel belegen. Wenn du nur die CSS-Breite setzt, aber den internen Canvas-Puffer nicht anpasst, skaliert der Browser ein zu kleines Bild. Linien wirken dann weich.

Implementiere ein Canvas-2D-Demo.
Anforderungen:
- CSS-Pixel und interne Canvas-Pixel trennen und devicePixelRatio nutzen
- Mit requestAnimationFrame rendern und dt nach Tab-Pausen begrenzen
- Pointer Events für mouse, touch und pen verwenden
- Zeichenzustand in einem state halten, render(ctx) zeichnet nur
- ResizeObserver für Container-Änderungen nutzen
- Bei 375px Breite darf kein horizontaler Scroll entstehen
- Playwright prüft Sichtbarkeit, nicht-leere Pixel, Screenshot und Mobile-Breite
- Geänderte Dateien, Risiken, Fehlerfälle und manuelle Checks ausgeben

Dieser Prompt verhindert, dass Claude Code nur ein hübsches Desktop-Beispiel erzeugt. Er beschreibt den Vertrag, den der Canvas später erfüllen muss.

Architektur vor Code

Teile Eingabe, Zustand, Zeitupdate, Rendering und Prüfung. Diese Skizze kann direkt in den Kontext für Claude Code.

Pointer Events
      |
      v
  input handler  --->  state update  --->  update(dt)
                                         |
ResizeObserver ---> resize(dpr)          v
                                     render(ctx)
                                         |
                                         v
                                Playwright checks

render(ctx) sollte keine Events registrieren, keine DOM-Struktur ändern und keine weitere Schleife starten. Es liest Zustand und zeichnet. Diese Trennung macht Undo, Radierer, WebGL-Fallbacks und visuelle Tests deutlich einfacher.

Typische Anwendungsfälle

Erster Fall: redaktionelle Visualisierung und Dashboards. Live-Spuren, dichte Punktwolken, freie Formen, Audiowellen oder Kartenbewegungen lassen sich mit Canvas oft besser abbilden als mit Standarddiagrammen. Trotzdem müssen leere Daten, Ladezustände und mobile Breite geprüft werden.

Zweiter Fall: Bildannotation. Screenshots prüfen, Designs markieren, Kursaufgaben korrigieren: Linien, Rechtecke, Pfeile, Labels und Undo sind hier normal. Pointer Events sparen getrennte Implementierungen für Maus, Finger und Stift. Auf passenden Geräten kann pressure die Linienstärke steuern.

Dritter Fall: Lernen und kleine Spiele. Physikdemos, Vokabelkarten, Tipptrainer und Partikeleffekte passen gut zu Frame-basiertem State. Das Risiko liegt im Lifecycle: Läuft requestAnimationFrame nach einem Routenwechsel weiter, sieht der Nutzer nichts, aber CPU und Akku zahlen.

Vierter Fall: Produktseiten. Ein interaktiver Preview kann Farbe, Vergleich oder Bediengefühl erklären. Er muss aber die Kauf- oder Beratungs-CTA stützen, nicht verdrängen.

Ausführbares Beispiel

Speichere diesen Ausschnitt als HTML-Datei und öffne ihn im Browser. Wichtig ist ctx.setTransform, weil es die Skalierung nach jedem Resize ersetzt und nicht stapelt.

<style>
  body { margin: 0; display: grid; min-height: 100vh; place-items: center; background: #111827; }
  canvas { width: min(100%, 720px); aspect-ratio: 16 / 9; display: block; background: #020617; border: 1px solid #374151; border-radius: 8px; touch-action: none; }
</style>
<canvas id="demo" aria-label="Canvas particle demo"></canvas>
<script type="module">
  const canvas = document.querySelector("#demo");
  const ctx = canvas.getContext("2d");
  const state = { width: 1, height: 1, dpr: 1, last: 0, pointer: { x: 0, y: 0, down: false }, dots: [] };

  function resize() {
    const rect = canvas.getBoundingClientRect();
    state.width = Math.max(1, rect.width);
    state.height = Math.max(1, rect.height);
    state.dpr = Math.min(window.devicePixelRatio || 1, 2);
    canvas.width = Math.round(state.width * state.dpr);
    canvas.height = Math.round(state.height * state.dpr);
    ctx.setTransform(state.dpr, 0, 0, state.dpr, 0, 0);
  }

  function point(event) {
    const rect = canvas.getBoundingClientRect();
    return { x: event.clientX - rect.left, y: event.clientY - rect.top, pressure: event.pressure || 0.5 };
  }

  function emit(x, y, pressure = 0.5) {
    for (let i = 0; i < 8; i += 1) {
      const angle = Math.random() * Math.PI * 2;
      const speed = 90 + Math.random() * 180;
      state.dots.push({ x, y, vx: Math.cos(angle) * speed, vy: Math.sin(angle) * speed, life: 1, size: 4 + pressure * 8 });
    }
    state.dots = state.dots.slice(-360);
  }

  canvas.addEventListener("pointerdown", (event) => {
    canvas.setPointerCapture(event.pointerId);
    const p = point(event);
    state.pointer = { x: p.x, y: p.y, down: true };
    emit(p.x, p.y, p.pressure);
  });
  canvas.addEventListener("pointermove", (event) => {
    const events = event.getCoalescedEvents ? event.getCoalescedEvents() : [event];
    for (const item of events) {
      const p = point(item);
      state.pointer.x = p.x;
      state.pointer.y = p.y;
      if (state.pointer.down) emit(p.x, p.y, p.pressure);
    }
  });
  canvas.addEventListener("pointerup", () => (state.pointer.down = false));
  canvas.addEventListener("pointercancel", () => (state.pointer.down = false));

  function frame(now) {
    const dt = state.last ? Math.min((now - state.last) / 1000, 0.033) : 0;
    state.last = now;
    for (const dot of state.dots) {
      dot.vy += 220 * dt;
      dot.x += dot.vx * dt;
      dot.y += dot.vy * dt;
      dot.life -= dt;
    }
    state.dots = state.dots.filter((dot) => dot.life > 0);
    ctx.clearRect(0, 0, state.width, state.height);
    ctx.fillStyle = "#020617";
    ctx.fillRect(0, 0, state.width, state.height);
    for (const dot of state.dots) {
      ctx.fillStyle = `rgba(56,189,248,${dot.life})`;
      ctx.beginPath();
      ctx.arc(dot.x, dot.y, dot.size * dot.life, 0, Math.PI * 2);
      ctx.fill();
    }
    ctx.fillStyle = "#e5e7eb";
    ctx.fillText(`dpr ${state.dpr.toFixed(2)} / dots ${state.dots.length}`, 16, 24);
    requestAnimationFrame(frame);
  }

  new ResizeObserver(resize).observe(canvas);
  resize();
  requestAnimationFrame(frame);
</script>

State, Mobile und Review

Canvas speichert keine Striche als DOM-Elemente. Wenn du Undo, Redo, Werkzeugwechsel oder Replay brauchst, gehören Punkte, Farbe, Linienstärke und Tool in JavaScript-State. Bitte Claude Code darum, State-Änderungen in kleinen Funktionen zu halten und Rendering deterministisch zu machen.

Mobile Fehler entstehen oft durch feste Breiten und fehlende Höhe. width: 800px erzeugt auf 375px Breite horizontalen Scroll; width: 100% ohne aspect-ratio kann den Canvas zusammenquetschen. Prüfe ihn zusammen mit Text, Codeblöcken, Anzeigen, Related Cards und CTA.

Playwright-Prüfung

DOM-Assertions beweisen nicht, dass der Canvas gemalt hat. Prüfe Sichtbarkeit, mobile Breite, bemalte Pixel und Screenshot.

import { expect, test } from "@playwright/test";

test("canvas renders on mobile", async ({ page }) => {
  await page.setViewportSize({ width: 390, height: 844 });
  await page.goto("/canvas-demo");
  const canvas = page.locator("canvas").first();
  await expect(canvas).toBeVisible();

  const box = await canvas.boundingBox();
  expect(box?.width ?? 0).toBeLessThanOrEqual(390);

  const paintedPixels = await canvas.evaluate((node) => {
    const context = node.getContext("2d");
    if (!context) return 0;
    const data = context.getImageData(0, 0, node.width, node.height).data;
    let painted = 0;
    for (let i = 3; i < data.length; i += 4) if (data[i] > 0) painted += 1;
    return painted;
  });

  expect(paintedPixels).toBeGreaterThan(1000);
  await expect(canvas).toHaveScreenshot("canvas-mobile.png", { maxDiffPixelRatio: 0.03 });
});

Fehler, Monetarisierung und Ergebnis

Häufige Fehler sind: nur CSS-Größe ändern, ctx.scale stapeln, nur mousemove verwenden, RAF nicht aufräumen, Desktop-Breite fixieren und einen schwarzen Screenshot als Erfolg werten. Diese Punkte gehören explizit in die Claude-Code-Review.

Canvas unterstützt Monetarisierung, wenn es etwas erklärt, das ein statisches Bild nicht leisten kann: interaktives Tutorial, Annotationstool, Produktpreview oder Datenwerkzeug. Es schadet, wenn es Inhalt und CTA verdeckt. Für Teams kann Claude Code Training und Beratung helfen, Prompts, Implementierungsregeln und Playwright-Prüfungen als Workflow aufzusetzen.

Beim Ausprobieren dieses Ablaufs war der größte Gewinn eine zweite Claude-Code-Runde nur für Fehlerfälle. Sie fand feste Breiten, kumulatives ctx.scale, fehlende Touch-Unterstützung und mobilen Scroll vor der Veröffentlichung. Das Ergebnis war weniger Show-Demo, aber deutlich näher an einer stabilen Seite.

#Claude Code #Canvas #WebGL #Grafik #TypeScript
Kostenlos

Kostenloses PDF: Claude-Code-Cheatsheet

E-Mail eintragen und eine Seite mit Befehlen, Review-Gewohnheiten und sicheren Workflows herunterladen.

Wir schützen Ihre Daten und senden keinen Spam.

Masa

Über den Autor

Masa

Engineer für praktische Claude-Code-Workflows und Team-Einführung.