Canvas API: Desenhando Formas
Além de retângulos, o canvas permite desenhar formas mais complexas. Para construir essas formas complexas, utilizamos a concepção de caminhos geométricos, que consistem em um conjunto de linhas, círculos, retângulos e outros elementos menores, necessários para a construção da forma desejada.
Para criar um novo caminho, utilizamos o método beginPath()
:
const canvas = document.getElementById("canvas");
const context = canvas.getContext("2d");
context.beginPath(); // começando o desenho da forma
Após chamar o método beginPath()
, utilizamos outros métodos para criar as diferentes partes do caminho.
Métodos moveTo() e lineTo()
Para iniciar o desenho de um caminho, precisamos definir o ponto de partida. Isso pode ser feito com o método moveTo()
, que possui a seguinte definição:
moveTo(x, y);
Esse método move a posição atual para o ponto especificado pelas coordenadas x
e y
.
O método lineTo()
, por sua vez, desenha uma linha, e possui uma definição similar:
lineTo(x, y);
Esse método desenha uma linha da posição atual até o ponto com as coordenadas x
e y
.
Vamos desenhar algumas linhas:
const canvas = document.getElementById("canvas");
const context = canvas.getContext("2d");
context.beginPath();
context.moveTo(20, 100);
context.lineTo(140, 10);
context.lineTo(260, 100);
context.stroke();
Aqui, definimos o início do caminho no ponto (20, 100)
. A partir desse ponto, desenhamos uma linha até (140, 10)
(subindo) e, em seguida, outra linha até (260, 100)
. Por fim, chamamos o método stroke()
para desenhar as linhas.
Por padrão, o desenho será feito em preto, mas podemos alterar a cor utilizando a propriedade strokeStyle
:
const canvas = document.getElementById("canvas");
const context = canvas.getContext("2d");
context.beginPath();
context.moveTo(20, 100);
context.lineTo(140, 10);
context.lineTo(260, 100);
context.strokeStyle = "red"; // definindo a cor vermelha
context.stroke(); // exibindo o caminho
Fechando o Caminho
Desenhamos duas linhas e, supondo que queremos conectá-las para formar uma figura fechada — neste caso, um triângulo — podemos utilizar o método closePath()
. Esse método conecta automaticamente o primeiro e o último pontos do caminho, formando a figura:
const canvas = document.getElementById("canvas");
const context = canvas.getContext("2d");
context.beginPath();
context.moveTo(20, 100);
context.lineTo(140, 10);
context.lineTo(260, 100);
context.closePath(); // fechando o caminho
context.stroke();
Objetos Path2D
Ao trabalhar com vários caminhos, pode ser difícil gerenciá-los. Para separar diferentes caminhos, podemos utilizar o objeto Path2D
, que fornece métodos semelhantes aos do objeto contexto para criar caminhos. Por exemplo:
const canvas = document.getElementById("canvas");
const context = canvas.getContext("2d");
const path1 = new Path2D(); // primeiro caminho
path1.moveTo(20, 100);
path1.lineTo(140, 10);
path1.lineTo(260, 100);
path1.closePath(); // fechando o caminho
context.strokeStyle = "blue";
context.stroke(path1);
const path2 = new Path2D(); // segundo caminho
path2.moveTo(20, 110);
path2.lineTo(140, 200);
path2.lineTo(260, 110);
path2.closePath(); // fechando o caminho
context.strokeStyle = "red";
context.stroke(path2);
Aqui, criamos dois caminhos, cada um representando um triângulo. Para desenhar cada caminho, chamamos o método context.stroke()
passando o caminho como argumento.
Método rect
O método rect()
cria um retângulo e tem a seguinte definição:
rect(x, y, width, height);
Onde x
e y
são as coordenadas do canto superior esquerdo do retângulo em relação ao canvas, e width e height são a largura e a altura do retângulo, respectivamente. Vamos desenhar um retângulo:
const canvas = document.getElementById("canvas");
const context = canvas.getContext("2d");
context.beginPath();
context.rect(30, 20, 100, 90);
context.closePath();
context.stroke();
Vale notar que poderíamos criar o mesmo retângulo utilizando linhas:
const canvas = document.getElementById("canvas");
const context = canvas.getContext("2d");
context.beginPath();
context.moveTo(30, 20);
context.lineTo(130, 20);
context.lineTo(130, 110);
context.lineTo(30, 110);
context.closePath();
context.stroke();
Método fill()
O método fill()
preenche todo o espaço interno da forma desenhada com uma cor:
const canvas = document.getElementById("canvas");
const context = canvas.getContext("2d");
context.beginPath();
context.moveTo(20, 100);
context.lineTo(140, 10);
context.lineTo(260, 100);
context.closePath();
context.strokeStyle = "#2e86de";
context.fillStyle = "#4bcffa";
context.fill();
context.stroke();
A cor de preenchimento pode ser definida usando a propriedade fillStyle
. Neste exemplo, a cor escolhida foi #4bcffa
.
Método clip()
O método clip()
permite recortar uma área específica do canvas, ignorando tudo que estiver fora dessa área durante a renderização subsequente.
Para entender melhor como esse método funciona, primeiro desenharemos dois retângulos:
const canvas = document.getElementById("canvas");
const context = canvas.getContext("2d");
// desenhando o primeiro retângulo vermelho
context.beginPath();
context.moveTo(10, 20);
context.lineTo(130, 20);
context.lineTo(130, 110);
context.lineTo(10, 110);
context.closePath();
context.strokeStyle = "red";
context.stroke();
// desenhando o segundo retângulo verde
context.beginPath();
context.rect(30, 50, 180, 70);
context.closePath();
context.strokeStyle = "green";
context.stroke();
Agora, vamos aplicar o método clip()
para limitar a área de desenho apenas ao primeiro retângulo:
const canvas = document.getElementById("canvas");
const context = canvas.getContext("2d");
// desenhando o primeiro retângulo vermelho
context.beginPath();
context.moveTo(10, 20);
context.lineTo(130, 20);
context.lineTo(130, 110);
context.lineTo(10, 110);
context.closePath();
context.strokeStyle = "red";
context.stroke();
context.clip(); // recortando a área de desenho conforme o primeiro caminho
// desenhando o segundo retângulo verde
context.beginPath();
context.rect(30, 50, 180, 70);
context.closePath();
context.strokeStyle = "green";
context.stroke();
Como o método clip()
é chamado após o desenho do primeiro retângulo, apenas a parte do segundo retângulo que se sobrepõe ao primeiro será exibida.
Método arc()
O método arc()
adiciona um segmento de arco ou círculo ao caminho. Ele possui a seguinte definição:
arc(x, y, radius, startAngle, endAngle, anticlockwise);
Aqui estão os parâmetros utilizados:
x
ey
: coordenadas do ponto central do arco.radius
: raio do círculo no qual o arco é desenhado.startAngle
eendAngle
: ângulos inicial e final que definem a porção do círculo que será desenhada. Esses ângulos são medidos em radianos. Por exemplo, uma circunferência completa equivale a 2π radianos. Em JavaScript, podemos obter esse valor com a expressãoMath.PI * 2
.anticlockwise
: define a direção do traçado. Se tr``ue, o arco é traçado no sentido anti-horário; sefalse
, no sentido horário.
Exemplo de desenho de arcos e círculos:
const canvas = document.getElementById("canvas");
const context = canvas.getContext("2d");
context.strokeStyle = "red";
context.beginPath();
context.moveTo(20, 90);
context.arc(20, 90, 50, 0, Math.PI / 2, false);
context.closePath();
context.stroke();
context.beginPath();
context.moveTo(130, 90);
context.arc(130, 90, 50, 0, Math.PI, false);
context.closePath();
context.stroke();
context.beginPath();
context.moveTo(240, 90);
context.arc(240, 90, 50, 0, (Math.PI * 3) / 2, false);
context.closePath();
context.stroke();
context.beginPath();
context.arc(350, 90, 50, 0, Math.PI * 2, false);
context.closePath();
context.stroke();
O último parâmetro, anticlockwise
, desempenha um papel importante, pois define a direção do traçado. Alterando o valor de true
para false
ou vice-versa, podemos obter formas diferentes:
const canvas = document.getElementById("canvas");
const context = canvas.getContext("2d");
context.strokeStyle = "red";
context.beginPath();
context.moveTo(80, 90);
context.arc(80, 90, 50, 0, Math.PI / 2, false);
context.closePath();
context.stroke();
context.beginPath();
context.moveTo(240, 90);
context.arc(240, 90, 50, 0, Math.PI / 2, true);
context.closePath();
context.stroke();
Método arcTo()
O método arcTo()
também é utilizado para desenhar arcos, mas com uma abordagem diferente. Ele possui a seguinte definição:
arcTo(x1, y1, x2, y2, radius);
Onde x1
e y1
são as coordenadas do primeiro ponto de controle, x2
e y2
são as coordenadas do segundo ponto de controle, e radius
é o raio do arco.
Exemplo de desenho de arcos com arcTo()
:
const canvas = document.getElementById("canvas");
const canvas = document.getElementById("canvas");
const context = canvas.getContext("2d");
context.strokeStyle = "red";
context.beginPath();
context.moveTo(0, 150);
context.arcTo(0, 0, 150, 0, 140);
context.closePath();
context.stroke();
Neste exemplo, começamos no ponto (0, 150)
e desenhamos uma tangente até a primeira coordenada de controle (0, 0)
. A partir daí, uma segunda tangente é desenhada até a segunda coordenada de controle (150, 0)
. Essas duas tangentes definem o arco, com um raio de 140 pixels.
Método quadraticCurveTo()
O método quadraticCurveTo()
cria uma curva quadrática de Bézier. A definição é a seguinte:
quadraticCurveTo(x1, y1, x2, y2);
Aqui, x1
e y1
são as coordenadas do primeiro ponto de controle, e x2
e y2
são as coordenadas do segundo ponto de controle.
Exemplo de uma curva quadrática de bézier:
const canvas = document.getElementById("canvas");
const context = canvas.getContext("2d");
context.strokeStyle = "red";
context.beginPath();
context.moveTo(20, 90);
context.quadraticCurveTo(130, 0, 280, 90);
context.closePath();
context.stroke();
Neste exemplo, a curva começa no ponto (20, 90)
, passa pelo ponto de controle (130, 0)
e termina no ponto (280, 90)
.
Método bezierCurveTo()
O método bezierCurveTo()
desenha uma curva cúbica de Bézier, definida por três pontos de controle. A sua definição é:
bezierCurveTo(x1, y1, x2, y2, x3, y3);
Onde x1
e y1
são as coordenadas do primeiro ponto de controle, x2
e y2
as do segundo ponto de controle, e x3
e y3
são as coordenadas do ponto final.
Exemplo de uma curva bézier:
const canvas = document.getElementById("canvas");
const context = canvas.getContext("2d");
context.strokeStyle = "red";
context.beginPath();
context.moveTo(30, 100);
context.bezierCurveTo(110, 0, 190, 200, 270, 100);
context.closePath();
context.stroke();
Neste exemplo, a curva começa no ponto (30, 100)
, passa pelos pontos de controle (110, 0)
e (190, 200)
, e termina no ponto (270, 100)
.
Figuras Complexas
Agora, vamos combinar várias formas para desenhar uma cena bidimensional mais complexa:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Desenho Complexo</title>
</head>
<body>
<canvas id="canvas" width="400" height="250"></canvas>
<script>
const canvas = document.getElementById("canvas");
const context = canvas.getContext("2d");
// Desenhando o fundo amarelo
context.fillStyle = "yellow";
context.beginPath();
context.arc(160, 130, 100, 0, 2 * Math.PI);
context.fill();
// Desenhando a boca
context.beginPath();
context.moveTo(100, 160);
context.quadraticCurveTo(160, 250, 220, 160);
context.closePath();
context.fillStyle = "red";
context.fill();
context.lineWidth = 2;
context.strokeStyle = "black";
context.stroke();
// Desenhando os dentes
context.fillStyle = "#FFFFFF";
context.fillRect(140, 160, 15, 15);
context.fillRect(170, 160, 15, 15);
// Desenhando os olhos
context.beginPath();
context.arc(130, 90, 20, 0, 2 * Math.PI);
context.fillStyle = "#333333";
context.fill();
context.closePath();
context.beginPath();
context.arc(190, 90, 20, 0, 2 * Math.PI);
context.fillStyle = "#333333";
context.fill();
context.closePath();
</script>
</body>
</html>