Tips & Tricks (Actualizado: 2/6/2026)

Desarrollo Canvas con Claude Code: HiDPI, RAF, eventos y pruebas

Aprende a crear interfaces Canvas con Claude Code: HiDPI, requestAnimationFrame, Pointer Events, estado y Playwright.

Desarrollo Canvas con Claude Code: HiDPI, RAF, eventos y pruebas

El problema no es dibujar, es publicar sin romper

Canvas permite dibujar gráficos, trazos, partículas, juegos ligeros, anotaciones sobre imágenes y visualizaciones que no encajan bien en HTML normal. Esa libertad tiene un coste: el navegador no guarda una estructura de elementos dibujados, no corrige automáticamente la densidad de píxeles y no sabe cuándo debe parar un bucle de animación. Por eso un demo que se ve bien en escritorio puede salir borroso en un móvil, ignorar el tacto o crear scroll horizontal dentro de un artículo.

Claude Code es útil cuando le pides una solución completa, no solo un efecto bonito. Debe leer el componente existente, las restricciones de CSS, los tamaños objetivo, el flujo de CTA y el comando de pruebas. En un sitio de contenidos, Canvas suele convivir con texto, anuncios, bloques de código y enlaces de conversión; si empuja el botón principal fuera de la pantalla, el efecto ya no ayuda.

Para complementar esta guía, revisa animaciones con Claude Code, Three.js con Claude Code y visualización de datos con Claude Code. Las referencias oficiales son Claude Code Docs, MDN Canvas API, requestAnimationFrame, Pointer Events y capturas en Playwright.

Prompt recomendado para Claude Code

HiDPI significa alta densidad de píxeles: un píxel CSS puede ocupar varios píxeles físicos. Si solo pones width: 100% en CSS pero dejas el buffer interno del Canvas en 300x150, el navegador estira una imagen pequeña y el resultado queda borroso.

Implementa un demo Canvas 2D.
Requisitos:
- Separar píxeles CSS y píxeles internos usando devicePixelRatio
- Renderizar con requestAnimationFrame y limitar dt tras pausas de pestaña
- Usar Pointer Events para mouse, touch y pen
- Mantener el estado en un objeto state y hacer que render(ctx) solo dibuje
- Usar ResizeObserver para seguir el tamaño del contenedor
- Evitar scroll horizontal a 375px de ancho
- Añadir pruebas Playwright: visibilidad, píxeles no vacíos, screenshot y ancho móvil
- Devolver archivos cambiados, riesgos, casos de fallo y checklist manual

El objetivo es que Claude Code genere una base de renderizado mantenible. La estética viene después; primero hay que evitar los fallos que cuestan tiempo en revisión.

Modelo de arquitectura

Antes de escribir código, separa entrada, estado, actualización, renderizado y validación.

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

render(ctx) no debería registrar eventos ni iniciar otro bucle. Si solo lee estado y dibuja, luego es más fácil añadir undo, herramienta de borrado, fallback WebGL o pruebas visuales sin reescribir todo.

Casos de uso reales

Primer caso: visualizaciones editoriales y dashboards. Canvas encaja cuando necesitas trayectorias en tiempo real, nubes de puntos densas, formas personalizadas, ondas de audio o mapas con movimiento. Claude Code puede crear el bucle, pero debe contemplar datos vacíos, carga lenta y móviles.

Segundo caso: herramientas de anotación. Revisar capturas, marcar diseños o corregir tareas exige líneas, flechas, etiquetas, rectángulos y undo. Pointer Events evita mantener tres implementaciones para ratón, dedo y lápiz. En dispositivos compatibles, pressure puede cambiar el grosor del trazo.

Tercer caso: educación y juegos ligeros. Simulaciones físicas, entrenadores de escritura, tarjetas interactivas o efectos de partículas funcionan bien con actualización por frames. El riesgo es dejar requestAnimationFrame corriendo tras cambiar de ruta.

Cuarto caso: páginas de producto. Un Canvas puede servir para comparar colores, arrastrar un preview o explicar un flujo antes de una compra. Debe apoyar el CTA, no competir con él.

Ejemplo ejecutable

Guarda este fragmento como HTML y ábrelo en el navegador. Usa ctx.setTransform para reemplazar la escala tras cada resize, evitando que ctx.scale se acumule.

<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>

Estado, móvil y revisión

Canvas no conserva trazos como nodos DOM. Si quieres undo, redo, borrador o replay, guarda puntos, color, herramienta y grosor en estado JavaScript. Pide a Claude Code que actualice estado en funciones pequeñas y que render solo dibuje desde ese estado.

En móvil, los errores más comunes son ancho fijo y altura implícita. width: 800px rompe el artículo en pantallas de 375px; un Canvas con width: 100% pero sin aspect-ratio puede quedar aplastado. Revisa junto a texto, código, anuncios, tarjetas relacionadas y CTA.

Verificación con Playwright

Una prueba DOM no demuestra que el Canvas esté pintado. Combina visibilidad, tamaño móvil, conteo de píxeles y 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 });
});

Errores habituales y monetización

Los fallos más frecuentes son cambiar solo el tamaño CSS, acumular ctx.scale, depender de mousemove, olvidar limpiar el bucle de animación, fijar el Canvas en ancho de escritorio y confiar en un screenshot vacío. Incluye esos puntos en el prompt de revisión de Claude Code.

Canvas puede mejorar la monetización cuando explica algo que una imagen estática no logra: tutorial interactivo, herramienta de anotación, preview de producto o visualización de datos que conduce a una plantilla, compra o consulta. Si el equipo quiere convertirlo en proceso repetible, la página de formación y consultoría de Claude Code puede servir para diseñar prompts, reglas de implementación y pruebas Playwright.

Al probar este flujo, lo que más redujo retrabajo fue pedir a Claude Code una segunda pasada dedicada solo a fallos. Esa revisión detectó ancho fijo, escalado acumulado, entrada táctil incompleta y scroll móvil antes de publicar. El resultado fue menos llamativo que un demo improvisado, pero mucho más apto para una página real.

#Claude Code #Canvas #WebGL #gráficos #TypeScript
Gratis

PDF gratis: cheatsheet de Claude Code

Introduce tu email y descarga una hoja con comandos, hábitos de revisión y flujos seguros.

Cuidamos tus datos y no enviamos spam.

Masa

Sobre el autor

Masa

Ingeniero enfocado en workflows prácticos con Claude Code.