Dibujo en Canvas
Aprende a dibujar en el canvas de HTML con JavaScript: rectángulos, trazos, rutas, líneas, círculos y arcos, con ejemplos de código ejecutables.
El elemento <canvas> es simplemente una superficie de dibujo en blanco y transparente. Por sí solo no muestra nada — no se puede dibujar en él con HTML o CSS. Todo el dibujo en un canvas se realiza con JavaScript, a través de un object llamado el contexto de renderizado. Esta página recorre las operaciones de dibujo 2D principales paso a paso: rellenar rectángulos, trazar contornos, dibujar rutas libres y dibujar círculos y arcos.
Si aún no has creado un elemento <canvas>, comienza con la etiqueta HTML <canvas> y la introducción a Canvas.
El sistema de coordenadas del canvas
Cada método de dibujo usa coordenadas en píxeles. La cuadrícula del canvas tiene su origen (0, 0) en la esquina superior izquierda. El eje x crece hacia la derecha y el eje y crece hacia abajo (ten en cuenta que y aumenta al moverse hacia abajo, a diferencia de una gráfica matemática). Por lo tanto, el punto (0, 0) es el píxel superior izquierdo y (width, height) es la esquina inferior derecha.
Para un análisis más detallado de la cuadrícula, consulta Coordenadas de Canvas.
1. Encontrar el elemento canvas
Primero, obtén una referencia al elemento <canvas> en el DOM con getElementById():
const canvas = document.getElementById("canvas");2. Obtener el contexto de renderizado 2D
Llama al método getContext() para obtener un contexto de dibujo. Al pasar "2d" se devuelve un object CanvasRenderingContext2D, que contiene todas las propiedades y métodos que usas para dibujar:
const ctx = canvas.getContext("2d");La variable ctx (abreviatura de "context") es sobre la que llamas todos los comandos de dibujo.
getContext() devuelve null si el navegador no puede proporcionar el contexto solicitado (por ejemplo, si "2d" está mal escrito, o el elemento no es realmente un <canvas>). Es una buena práctica protegerse contra esto antes de dibujar, para que un contexto faltante falle silenciosamente en lugar de lanzar un error en la siguiente línea:
const ctx = canvas.getContext("2d");
if (!ctx) return; // bail out if the 2D context is unavailable; only valid inside a functionEn un <script> en línea simple (que no es el cuerpo de una función), envuelve el dibujo en if (ctx) { ... } en su lugar, como hacen los ejemplos a continuación.
3. Dibujar un rectángulo relleno
La propiedad fillStyle establece el color que se usa para rellenar las formas. Puede ser cualquier color CSS, un patrón o un degradado:
ctx.fillStyle = "#1c87c9";Luego fillRect(x, y, width, height) dibuja un rectángulo relleno. Los primeros dos parámetros son las coordenadas x e y de la esquina superior izquierda, seguidas del ancho y alto en píxeles:
ctx.fillRect(0, 0, 230, 130);Ejemplo de cómo dibujar un rectángulo relleno:
<!DOCTYPE html>
<html>
<head>
<title>Title of the document</title>
</head>
<body>
<canvas id="canvas" width="250" height="150" role="img"
aria-label="A blue filled rectangle" style="border:1px solid #dddddd;">
Your browser does not support the canvas element.
</canvas>
<script>
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
if (ctx) {
// only draw when the 2D context is available
ctx.fillStyle = "#1c87c9";
ctx.fillRect(0, 0, 230, 130);
}
</script>
</body>
</html>Hacer el canvas accesible
Todo lo que dibujes en un canvas son solo píxeles — no tiene estructura, por lo que los lectores de pantalla no pueden verlo. No hay nada que anunciar y los usuarios de teclado no pueden acceder a nada dentro de él. Proporciona a la tecnología de asistencia un equivalente de texto de dos formas:
- El contenido alternativo va entre las etiquetas
<canvas>. Los navegadores que renderizan el canvas lo ignoran; la tecnología de asistencia (y los navegadores muy antiguos) lo usan en su lugar. Pon aquí una descripción significativa, no "tu navegador no soporta canvas." role="img"másaria-labeldescribe el dibujo terminado como una sola imagen, de la misma manera que el textoaltdescribe una<img>.
<canvas id="chart" width="250" height="150" role="img"
aria-label="Bar chart: sales doubled from Q1 to Q2.">
A bar chart showing sales doubling from Q1 to Q2.
</canvas>Para cualquier elemento interactivo (regiones clicables, controles), proporciona también elementos DOM reales y enfocables — el canvas solo no es accesible por teclado.
Trazar, no rellenar
A veces quieres el contorno de una forma en lugar de un relleno sólido. Usa la propiedad strokeStyle para establecer el color del contorno y strokeRect(x, y, width, height) para dibujar un rectángulo sin relleno. La propiedad lineWidth controla el grosor del contorno:
<!DOCTYPE html>
<html>
<head>
<title>Title of the document</title>
</head>
<body>
<canvas id="canvas" width="250" height="150" role="img"
aria-label="A blue rectangular outline" style="border:1px solid #dddddd;">
A rectangle drawn as an outline.
</canvas>
<script>
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
if (ctx) {
ctx.strokeStyle = "#1c87c9";
ctx.lineWidth = 4;
ctx.strokeRect(20, 20, 200, 100);
}
</script>
</body>
</html>Limpiar parte del canvas
clearRect(x, y, width, height) borra el rectángulo indicado, dejándolo completamente transparente de nuevo. Se usa habitualmente para limpiar todo el canvas antes de volver a dibujar (por ejemplo, en cada fotograma de una animación):
// Erase everything on a 250 × 150 canvas
ctx.clearRect(0, 0, 250, 150);En el siguiente ejemplo se dibujan dos rectángulos azules y luego clearRect() abre un agujero transparente en el centro del canvas — observa que el rectángulo de la derecha queda parcialmente borrado:
<!DOCTYPE html>
<html>
<head>
<title>Title of the document</title>
</head>
<body>
<canvas id="canvas" width="250" height="150" role="img"
aria-label="Two blue squares with a cleared rectangle cut out of the middle"
style="border:1px solid #dddddd;">
Two filled squares with a cleared region.
</canvas>
<script>
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
if (ctx) {
ctx.fillStyle = "#1c87c9";
ctx.fillRect(20, 20, 100, 100);
ctx.fillRect(130, 20, 100, 100);
ctx.clearRect(90, 50, 70, 50); // erase a rectangle across both
}
</script>
</body>
</html>Dibujar rutas (líneas y formas personalizadas)
Los rectángulos son convenientes, pero la mayoría de los dibujos usan rutas — secuencias de puntos conectados por líneas o curvas. Una ruta se construye con un pequeño conjunto de métodos y no es visible hasta que llamas a stroke() (para dibujar el contorno) o fill() (para rellenar el área encerrada).
| Método | Qué hace |
|---|---|
beginPath() | Inicia una nueva ruta vacía. |
moveTo(x, y) | Mueve el "lápiz" a (x, y) sin dibujar. |
lineTo(x, y) | Añade una línea recta desde el punto actual hasta (x, y). |
closePath() | Dibuja una línea de regreso al punto de inicio de la ruta. |
stroke() | Renderiza la ruta como un contorno. |
fill() | Rellena el área encerrada por la ruta. |
Siempre comienza con beginPath()
beginPath() borra la lista de puntos que el contexto está rastreando actualmente e inicia una ruta nueva. Esto importa porque el canvas recuerda cada subruta hasta que lo reinicies. Si dibujas una forma y luego empiezas a añadir puntos para una segunda forma sin llamar a beginPath(), los puntos anteriores siguen en la ruta — por lo que el siguiente stroke() o fill() redibuja también la primera forma, a menudo con el nuevo color y el nuevo ancho de línea. El resultado es que las formas se "mezclan" entre sí.
Regla general: llama a beginPath() antes de cada nueva forma. Cada ejemplo a continuación comienza así.
Ejemplo: una sola línea
Para dibujar una línea, inicia una ruta, muévete al punto de inicio, dibuja una línea hasta el punto final y luego trázala:
<!DOCTYPE html>
<html>
<head>
<title>Title of the document</title>
</head>
<body>
<canvas id="canvas" width="250" height="150" role="img"
aria-label="A diagonal blue line" style="border:1px solid #dddddd;">
A diagonal line drawn across the canvas.
</canvas>
<script>
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
if (ctx) {
ctx.beginPath();
ctx.moveTo(20, 20); // start point (x, y)
ctx.lineTo(220, 120); // end point (x, y)
ctx.lineWidth = 3;
ctx.strokeStyle = "#1c87c9";
ctx.stroke(); // make the line visible
}
</script>
</body>
</html>Ejemplo: un triángulo
Encadenar varias llamadas a lineTo() crea una forma de múltiples lados. closePath() conecta el último punto con el primero, y fill() rellena el área encerrada:
<!DOCTYPE html>
<html>
<head>
<title>Title of the document</title>
</head>
<body>
<canvas id="canvas" width="250" height="150" role="img"
aria-label="A filled green triangle" style="border:1px solid #dddddd;">
A filled green triangle.
</canvas>
<script>
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
if (ctx) {
ctx.beginPath();
ctx.moveTo(125, 20); // top vertex
ctx.lineTo(220, 130); // bottom-right vertex
ctx.lineTo(30, 130); // bottom-left vertex
ctx.closePath(); // back to the top vertex
ctx.fillStyle = "#8ebf42";
ctx.fill();
}
</script>
</body>
</html>Dibujar círculos y arcos
El método arc() dibuja círculos y segmentos curvos:
ctx.arc(x, y, radius, startAngle, endAngle, counterclockwise);x,y— las coordenadas del centro del arco.radius— el radio en píxeles.startAngle,endAngle— los ángulos de inicio y fin en radianes (no en grados). El ángulo0apunta a lo largo del eje x positivo — directamente hacia la derecha, la dirección de las 3 en punto — y por defecto los ángulos aumentan en sentido horario (porque el eje y del canvas crece hacia abajo). Un círculo completo equivale a2 * Math.PIradianes.counterclockwise— un boolean opcional. Pasatruepara recorrer en el sentido contrario (el valor predeterminado esfalse).
Dado que los ángulos están en radianes, es conveniente convertir desde grados con una pequeña función auxiliar:
function toRadians(degrees) {
return degrees * Math.PI / 180;
}
// e.g. a quarter turn:
ctx.arc(125, 75, 50, 0, toRadians(90));Así, 90 grados equivale a Math.PI / 2, 180 grados a Math.PI y 360 grados a 2 * Math.PI.
Un círculo completo va de 0 a 2 * Math.PI. Como con cualquier ruta, debes llamar a stroke() o fill() para que aparezca:
<!DOCTYPE html>
<html>
<head>
<title>Title of the document</title>
</head>
<body>
<canvas id="canvas" width="250" height="150" role="img"
aria-label="A blue filled circle" style="border:1px solid #dddddd;">
A filled blue circle.
</canvas>
<script>
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
if (ctx) {
ctx.beginPath();
ctx.arc(125, 75, 50, 0, 2 * Math.PI); // center (125,75), radius 50, full circle
ctx.fillStyle = "#1c87c9";
ctx.fill();
}
</script>
</body>
</html>Para dibujar un semicírculo en su lugar, termina el arco en Math.PI radianes en lugar de 2 * Math.PI.
Qué hacer a continuación
Una vez que te sientas cómodo con las formas y las rutas, explora el resto del tutorial de Canvas:
- Coordenadas de Canvas — la cuadrícula con más detalle.
- Texto en Canvas — escribir texto con
fillText()ystrokeText(). - Degradados en Canvas — degradados de color lineales y radiales.
- Imágenes en Canvas — dibujar imágenes con
drawImage(). - Referencia de Canvas — la lista completa de propiedades y métodos.