W3docs

Desenhando no Canvas

Aprenda a desenhar no canvas HTML com JavaScript passo a passo: retângulos, contornos, caminhos, linhas, círculos e arcos, com exemplos de código executáveis.

O elemento <canvas> é apenas uma superfície de desenho em branco e transparente. Por si só, ele não exibe nada — não é possível desenhar nele com HTML ou CSS. Todo desenho em um canvas é feito com JavaScript, por meio de um objeto chamado contexto de renderização. Esta página percorre as operações de desenho 2D essenciais passo a passo: preenchimento de retângulos, traçado de contornos, desenho de caminhos livres e desenho de círculos e arcos.

Se você ainda não criou um elemento <canvas>, comece pela tag HTML <canvas> e pela introdução ao Canvas.

O sistema de coordenadas do canvas

Todo método de desenho utiliza coordenadas em pixels. A grade do canvas tem sua origem (0, 0) no canto superior esquerdo. O eixo x cresce para a direita e o eixo y cresce para baixo (observe que y aumenta ao se mover para baixo, ao contrário de um gráfico matemático). Portanto, o ponto (0, 0) é o pixel superior esquerdo, e (width, height) é o canto inferior direito.

Para uma análise mais detalhada da grade, veja Coordenadas do Canvas.

1. Encontre o elemento canvas

Primeiro, obtenha uma referência ao elemento <canvas> no DOM com getElementById():

const canvas = document.getElementById("canvas");

2. Obtenha o contexto de renderização 2D

Chame o método getContext() para obter um contexto de desenho. Passar "2d" retorna um objeto CanvasRenderingContext2D, que contém todas as propriedades e métodos usados para desenhar:

const ctx = canvas.getContext("2d");

A variável ctx (abreviação de "context") é onde você chama todos os comandos de desenho.

getContext() retorna null se o navegador não puder fornecer o contexto solicitado (por exemplo, se "2d" estiver escrito incorretamente, ou se o elemento não for um <canvas> de fato). É uma boa prática se proteger contra isso antes de desenhar, para que um contexto ausente falhe silenciosamente em vez de lançar um erro na linha seguinte:

const ctx = canvas.getContext("2d");
if (!ctx) return; // bail out if the 2D context is unavailable; only valid inside a function

Em um <script> inline simples (que não é o corpo de uma função), envolva o desenho em if (ctx) { ... }, como fazem os exemplos abaixo.

3. Desenhe um retângulo preenchido

A propriedade fillStyle define a cor usada para preencher formas. Pode ser qualquer cor CSS, um padrão ou um gradiente:

ctx.fillStyle = "#1c87c9";

Em seguida, fillRect(x, y, width, height) desenha um retângulo preenchido. Os dois primeiros parâmetros são as coordenadas x e y do canto superior esquerdo, seguidas pela largura e altura em pixels:

ctx.fillRect(0, 0, 230, 130);

Exemplo de desenho de um retângulo preenchido:

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

Torne o canvas acessível

Tudo o que você desenha em um canvas são apenas pixels — ele não possui estrutura, portanto leitores de tela não conseguem vê-lo. Não há nada a anunciar, e usuários de teclado não podem acessar nada dentro dele. Forneça à tecnologia assistiva um equivalente textual de duas formas:

  • Conteúdo de fallback fica entre as tags <canvas>. Navegadores que renderizam o canvas o ignoram; a tecnologia assistiva (e navegadores muito antigos) o utiliza em seu lugar. Coloque uma descrição significativa aqui, não "seu navegador não suporta canvas."
  • role="img" mais aria-label descreve o desenho finalizado como uma única imagem, da mesma forma que o texto alt descreve um <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 qualquer elemento interativo (regiões clicáveis, controles), forneça também elementos DOM reais e focalizáveis — canvas sozinho não é acessível pelo teclado.

Contorno, sem preenchimento

Às vezes você quer apenas o contorno de uma forma, em vez de um preenchimento sólido. Use a propriedade strokeStyle para definir a cor do contorno e strokeRect(x, y, width, height) para desenhar um retângulo sem preenchimento. A propriedade lineWidth controla a espessura do 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>
Result

Limpando parte do canvas

clearRect(x, y, width, height) apaga o retângulo especificado, tornando-o totalmente transparente novamente. É comumente usado para limpar todo o canvas antes de redesenhar (por exemplo, a cada quadro de uma animação):

// Erase everything on a 250 × 150 canvas
ctx.clearRect(0, 0, 250, 150);

No próximo exemplo, dois retângulos azuis são desenhados e, em seguida, clearRect() abre um buraco transparente no meio do canvas — observe que o retângulo da direita está parcialmente apagado:

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

Desenhando caminhos (linhas e formas personalizadas)

Retângulos são convenientes, mas a maioria dos desenhos usa caminhos — sequências de pontos conectados por linhas ou curvas. Um caminho é construído com um pequeno conjunto de métodos e só fica visível quando você chama stroke() (para desenhar o contorno) ou fill() (para preencher a área delimitada).

MétodoO que faz
beginPath()Inicia um novo caminho vazio.
moveTo(x, y)Move a "caneta" para (x, y) sem desenhar.
lineTo(x, y)Adiciona uma linha reta do ponto atual até (x, y).
closePath()Desenha uma linha de volta ao ponto inicial do caminho.
stroke()Renderiza o caminho como um contorno.
fill()Preenche a área delimitada pelo caminho.

Sempre comece com beginPath()

beginPath() limpa a lista de pontos que o contexto está rastreando atualmente e inicia um caminho novo. Isso é importante porque o canvas lembra todos os sub-caminhos até que você o reinicie. Se você desenhar uma forma e depois começar a adicionar pontos para uma segunda forma sem chamar beginPath(), os pontos antigos ainda estão no caminho — portanto, o próximo stroke() ou fill() redesenha a primeira forma também, muitas vezes com a nova cor e espessura de linha. O resultado são formas "vazando" umas nas outras.

Regra de ouro: chame beginPath() antes de cada nova forma. Cada exemplo abaixo começa dessa forma.

Exemplo: uma linha simples

Para desenhar uma linha, inicie um caminho, mova-se para o ponto de início, desenhe uma linha até o ponto final e então trace-a:

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

Exemplo: um triângulo

Encadear várias chamadas lineTo() cria uma forma com múltiplos lados. closePath() conecta o último ponto ao primeiro, e fill() preenche a área delimitada:

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

Desenhando círculos e arcos

O método arc() desenha círculos e segmentos curvos:

ctx.arc(x, y, radius, startAngle, endAngle, counterclockwise);
  • x, y — as coordenadas do centro do arco.
  • radius — o raio em pixels.
  • startAngle, endAngle — os ângulos de início e fim em radianos (não graus). O ângulo 0 aponta ao longo do eixo x positivo — diretamente para a direita, a direção das 3 horas — e por padrão os ângulos aumentam no sentido horário (porque o eixo y do canvas cresce para baixo). Um círculo completo equivale a 2 * Math.PI radianos.
  • counterclockwise — um boolean opcional. Passe true para percorrer no sentido oposto (padrão é false).

Como os ângulos estão em radianos, é conveniente converter a partir de graus com um pequeno auxiliar:

function toRadians(degrees) {
  return degrees * Math.PI / 180;
}

// e.g. a quarter turn:
ctx.arc(125, 75, 50, 0, toRadians(90));

Assim, 90 graus equivale a Math.PI / 2, 180 graus a Math.PI, e 360 graus a 2 * Math.PI.

Um círculo completo vai de 0 a 2 * Math.PI. Como em qualquer caminho, você deve chamar stroke() ou fill() para que ele apareça:

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

Para desenhar meio círculo (uma semicírculo), encerre o arco em Math.PI radianos em vez de 2 * Math.PI.

O que explorar em seguida

Quando você estiver confortável com formas e caminhos, explore o restante do tutorial de Canvas:

Prática

Prática
O que retorna chamar getContext('2d') em um elemento canvas?
O que retorna chamar getContext('2d') em um elemento canvas?
Prática
Por que você deve chamar beginPath() antes de desenhar cada nova forma?
Por que você deve chamar beginPath() antes de desenhar cada nova forma?
Was this page helpful?