JavaScript Canvas API
Aprenda a desenhar gráficos com o elemento canvas do HTML e JavaScript: obtenha um contexto 2D, desenhe formas e linhas, aplique transformações e anime com requestAnimationFrame.
Introdução ao HTML Canvas com JavaScript
O elemento HTML <canvas> é uma superfície de desenho que você controla com JavaScript. Ao contrário de imagens ou SVG, o canvas opera em modo imediato: não há objetos de forma para acompanhar. Você emite comandos de desenho e os pixels são pintados imediatamente. Uma vez pintada, uma forma é apenas pixels coloridos — o canvas não tem memória dela, por isso o redesenho é a ideia central por trás de toda animação.
Este capítulo percorre a obtenção de um contexto 2D, o desenho de formas e linhas, a transformação do sistema de coordenadas e a animação com requestAnimationFrame.
Esta página abrange o contexto de renderização 2D (getContext('2d')). O canvas também expõe um contexto webgl para gráficos 3D com aceleração de hardware, que é uma API separada.
Entendendo o Elemento 'canvas'
O elemento <canvas> cria uma superfície de desenho de tamanho fixo que renderiza gráficos dinamicamente. Ele se destaca em trabalhos com uso intensivo de gráficos — jogos, gráficos, manipulação de imagens, efeitos de partículas — onde você precisa de controle em nível de pixel e altas taxas de quadros. Por si só, a tag não desenha nada; é um contêiner vazio, e o JavaScript faz toda a pintura.
Um detalhe importante a aprender desde cedo: os atributos width e height definem o tamanho do buffer de desenho, enquanto width/height do CSS definem o tamanho exibido. Se eles divergirem, o canvas será esticado e seus desenhos parecerão borrados ou distorcidos. Defina o tamanho com os atributos (ou em JS via canvas.width/canvas.height), não com CSS, a menos que você queira escalar intencionalmente.
Configuração Básica do Canvas
Para começar a desenhar, coloque uma tag <canvas> no seu HTML e, em seguida, obtenha seu contexto de desenho 2D em JavaScript com getContext('2d'). O objeto de contexto é onde todos os métodos de desenho residem.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Canvas Example</title>
<style>
canvas {
border: 1px solid #000;
}
</style>
</head>
<body>
<canvas id="myCanvas" width="200" height="200"></canvas>
<script>
const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');
</script>
</body>
</html>Aqui definimos um canvas de 200×200, adicionamos uma borda para que você possa ver seus limites e obtemos o contexto com getContext('2d'). A borda é puramente visual; a própria superfície do canvas começa completamente transparente.
Observe o sistema de coordenadas: a origem (0, 0) fica no canto superior esquerdo. O eixo x cresce para a direita e o eixo y cresce para baixo, então (0, 0) é a borda superior e valores maiores de y movem-se em direção à parte inferior. Isso confunde os iniciantes que esperam eixos no estilo matemático.
Desenhando Formas
A maneira mais rápida de colocar algo na tela é fillRect(x, y, width, height), que desenha um retângulo preenchido em uma única chamada. Defina fillStyle primeiro para escolher a cor (qualquer string de cor CSS funciona). Também existe strokeRect para um retângulo apenas com contorno e clearRect para apagar uma região de volta ao transparente.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Rectangle Example</title>
<style>
canvas {
border: 1px solid black;
}
</style>
</head>
<body>
<canvas id="rectangleCanvas" width="200" height="200"></canvas>
<script>
const canvas = document.getElementById('rectangleCanvas');
const ctx = canvas.getContext('2d');
ctx.fillStyle = '#FF0000'; // Set the fill color to red
ctx.fillRect(20, 20, 150, 100); // Draw the rectangle
</script>
</body>
</html>Criando Linhas e Caminhos
Linhas, curvas e formas personalizadas são construídas a partir de caminhos. O padrão é sempre o mesmo:
beginPath()— inicia um novo caminho (pule isso e sua nova linha continuará a anterior).moveTo(x, y)— levanta a caneta e a coloca sem desenhar.lineTo(x, y)— desenha um segmento de linha até um novo ponto (chame repetidamente para encadear segmentos).stroke()para desenhar o contorno, oufill()para preencher a área delimitada.
Controle a aparência da linha com lineWidth, strokeStyle e lineCap antes de chamar stroke().
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Line Drawing Example</title>
<style>
canvas {
border: 1px solid black;
}
</style>
</head>
<body>
<canvas id="lineCanvas" width="200" height="200"></canvas>
<script>
const canvas = document.getElementById('lineCanvas');
const ctx = canvas.getContext('2d');
ctx.beginPath(); // Start the path
ctx.moveTo(50, 50); // Move the pen to (50, 50)
ctx.lineTo(150, 50); // Draw a line to (150, 50)
ctx.lineTo(150, 150); // Continue to (150, 150)
ctx.lineWidth = 4; // Thicker line
ctx.strokeStyle = 'navy'; // Line color
ctx.stroke(); // Render the path visible
</script>
</body>
</html>Desenhando Círculos e Arcos
Círculos são desenhados com arc(x, y, radius, startAngle, endAngle), onde os ângulos estão em radianos (um círculo completo é 2 * Math.PI). Envolva-o em um caminho e então use fill() ou stroke():
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Circle Example</title>
<style>
canvas {
border: 1px solid black;
}
</style>
</head>
<body>
<canvas id="circleCanvas" width="200" height="200"></canvas>
<script>
const canvas = document.getElementById('circleCanvas');
const ctx = canvas.getContext('2d');
ctx.beginPath();
// Center (100, 100), radius 60, full circle
ctx.arc(100, 100, 60, 0, 2 * Math.PI);
ctx.fillStyle = 'orange';
ctx.fill();
ctx.stroke(); // Add an outline on top of the fill
</script>
</body>
</html>Operações Avançadas com Canvas
Transformações no Canvas
translate, rotate e scale modificam o sistema de coordenadas do canvas em vez de formas individuais. Crucialmente, elas são cumulativas — cada uma é construída sobre a anterior, e uma rotação gira em torno da origem atual, não do centro da forma. Para girar em torno de um ponto, primeiro aplique translate para aquele ponto e então rotate.
Como as transformações se acumulam, envolva-as em ctx.save() / ctx.restore() para que você possa retornar ao estado anterior em vez de desfazer manualmente cada transformação:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Transformation Example</title>
<style>
canvas {
border: 1px solid black;
}
</style>
</head>
<body>
<canvas id="transformCanvas" width="200" height="200"></canvas>
<script>
const canvas = document.getElementById('transformCanvas');
const ctx = canvas.getContext('2d');
ctx.save(); // Remember the untransformed state
ctx.translate(50, 50); // Move the canvas origin to (50, 50)
ctx.rotate((Math.PI / 180) * 25); // Rotate 25 degrees (converted to radians)
ctx.fillStyle = 'blue'; // Set fill color to blue
ctx.fillRect(0, 0, 100, 50); // Draw at the new, rotated origin
ctx.restore(); // Back to the original coordinate system
</script>
</body>
</html>Animações com Canvas
O canvas não possui animação integrada; você cria movimento limpando e redesenhando repetidamente toda a superfície. O requestAnimationFrame do navegador agenda sua função de desenho para ser executada antes do próximo repaint — geralmente 60 vezes por segundo — e pausa automaticamente quando a aba está oculta, razão pela qual é preferível ao setInterval.
O loop sempre segue três etapas: limpar o canvas (clearRect), atualizar o estado (aqui, a posição x) e então desenhar o novo quadro. Esqueça o clearRect e você verá uma mancha de todos os quadros anteriores em vez de uma forma em movimento.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Animation Example</title>
<style>
canvas {
border: 1px solid black;
}
</style>
</head>
<body>
<canvas id="animationCanvas" width="200" height="200"></canvas>
<script>
const canvas = document.getElementById('animationCanvas');
const ctx = canvas.getContext('2d');
let x = 0; // Starting position
function drawFrame() {
ctx.clearRect(0, 0, canvas.width, canvas.height); // Clear the canvas for the new frame
ctx.fillStyle = 'green'; // Set the fill color to green
ctx.fillRect(x, 20, 50, 50); // Draw a moving rectangle
x++; // Increment the horizontal position
if (x > canvas.width) {
x = 0;
}
requestAnimationFrame(drawFrame); // Continue the animation
}
drawFrame();
</script>
</body>
</html>Armadilhas Comuns
- Saída borrada: tamanho do buffer e tamanho CSS incompatíveis. Defina as dimensões com os atributos
width/height, não com CSS, a menos que esteja escalando intencionalmente. - Formas unidas: esquecer
beginPath()antes de um novo caminho faz com que ele continue o anterior. - Linhas desaparecem: você chamou
stroke()oufill()mas nunca construiu um caminho, ou desenhou fora dos limites do canvas. - A animação deixa rastros: falta de
clearRectno início de cada quadro. - Nada é renderizado: o script foi executado antes de o
<canvas>existir no DOM. Coloque o script após o elemento, ou execute-o noDOMContentLoaded.
Conclusão
O elemento <canvas> fornece uma superfície de baixo nível com precisão de pixel para gráficos que HTML e CSS não conseguem produzir: gráficos, edição de imagens, sistemas de partículas e jogos completos. A API é pequena — obtenha um contexto, defina um estilo, emita um comando de desenho — e as animações são simplesmente esse loop executado muitas vezes por segundo.
Para ir mais fundo, consulte a tag HTML canvas e fundamentos de desenho em canvas para o lado da marcação, e animações em JavaScript e a API de animações baseada em requestAnimationFrame para movimentos mais suaves. Para selecionar seu canvas de forma confiável, consulte getElement e querySelector.