Vamos usar o canvas para gerar desenhos e animações 2D e 3D em uma página web, usando HTML e JavaScript.
Nessa aula vamos introduzir as ferramentas de programação que vamos utilizar para resolver os exercícios. Embora essas ferramentas sejam muito relevantes nessa disciplina, elas não constituem nosso foco principal. Por isso, nessa aula vamos apenas introduzi-las por meio de exemplos ilustrando alguns recursos básicos. Você pode copiar e depois estender esses exemplos para realizar os exercícios.
Vamos começar apresentando um esqueleto HTML que cria um canvas (área de desenho) para começar a desenhar em 2D, usando algumas primitivas gráficas comuns.
A sigla HTML significa HyperText Markup Language
, ou seja, é uma
linguagem que permite criar hipertexto como links URL e outros elementos
como o próprio canvas usando tags
.
O documento HTML abre e fecha usando as tags <html>
e </html>
.
A parte delimitada por <head>
e </head>
não é exibida pelo
navegador quando a página é carregada. Ela tipicamente contém:
<title> </title>
);.css
;script.js
. Observe que os arquivos em JavaScript tem a extensão .js
.O trecho delimitado por <body> </body>
é a parte que será exibida
pelo navegador. Para criar um canvas basta copiar o seguinte trecho de
código HTML para um arquivo script.html
. Observe que no corpo
(<body>
) precisamos incluir uma tag <canvas> </canvas>
,
para criar uma área de desenho.
Dentro de cada tag você pode definir algumas opções para configurar o
elemento HTML. No caso da tag <canvas>
,
o script.html
cria um elemento canvas com \(640 \times 480\) (width
\(\times\) height
) pixels em um sistema de coordenadas com o
eixo x
para a direita e y
para baixo como:
Fig. 4.1 Coordenadas no Canvas (reproduzido de Tutorial do Canvas)¶
Infelizmente, nem todos os navegadores são compatíveis com o canvas. Para esses navegadores, podemos incluir uma mensagem alternativa entre os tags
<canvas> </canvas>
, como por exemplo a mensagem “Opa! pode ser que seu navegador não suporte o canvas do HTML5.”. Assim, esses navegadores, ao invés do canvas, exibem a mensagem alternativa.
No exemplo incluímos também outros tags HTML para você se divertir ainda mais criando listas, sessões etc.
Os arquivos de estilo no formato CSS (Cascading Stylesheets) facilitam a formatação de sua página.
Salve o trecho abaixo em um arquivo estilo.css
na mesma pasta do seu
arquivo script.html
, e brinque um pouco, por exemplo, mudando algumas
cores de black
para red
ou green
. Para isso, abra o arquivo
com um editor de texto de sua preferência.
A sintaxe do CSS é relativamente simples. Basta, para cada elemento, criar um dicionário de nomes e valores. Assim, para o elemento “canvas”, esse arquivo estilo.css
define algumas de suas propriedades, nesse caso, “border” e “background-color”. Para o elemento “h3”, o arquivo define “color”.
Para saber mais, consulte materiais disponíveis na Internet, como esses tutoriais da Mozilla.
Para a maioria dos nossos exemplos e exercícios, o uso de CSS é facultativo e talvez seja suficiente definir as propriedades desejadas em tags de estilo (style).
O JavaScript é uma linguagem de script (scripting language
) criada
para a Web. Toda vez que uma página mostra algo “dinâmico” (como updates
de tempos em tempos) ou algo “interativo” (como um mapa), seu navegador deve
estar executando um script (ou programa) em
JavaScript.
O exemplo a seguir mostra o conteúdo do arquivo script.js
que
foi usado no arquivo HTML fornecido anteriormente.
Crie um arquivo com esse mesmo nome na mesma pasta dos arquivos
anteriores e salve o código abaixo nesse arquivo.
script.js
¶/*
* ==================================================================
*
* Esqueleto de um script JavaScript
*
* ==================================================================
*/
// A `main()` só deve ser executada quando tudo estiver carregado
window.onload = main();
// Lista de variáveis globais
var ctx; // contexto com a API para desenhar
/**
* função principal
*/
function main() {
// veja o canvas id definido no arquivo index.html
const canvas = document.getElementById('meucanvas');
// vamos definir um contexto para desenhar em 2D
ctx = canvas.getContext('2d');
if (!ctx) alert("Não consegui abrir o contexto 2d :-( ");
let cor = 'blue';
desenheRect( cor, 20, 40, 160, 80 );
cor = 'red';
desenheRect( cor, 100, 60, 340, 280 );
cor = 'green';
desenheRect( cor, 320, 240, 300, 220 );
};
// ==================================================================
// outras funções
/**
* recebe uma cor e os parâmetros de um retângulo e
* desenha a região interna do retângulo com cor.
* @param {string} cor
* @param {number} left - coluna esquerda
* @param {number} top - linha superior
* @param {number} width - largura do retângulo
* @param {number} height - altura
*/
function desenheRect( cor, left, top, width, height ) {
console.log("Desenhando retângulo ", cor);
ctx.fillStyle = cor;
ctx.fillRect( left, top, width, height );
};
Nesse e em futuros exemplos vamos adotar um esqueleto e sintaxe que se assemelham a programas em C e Python. Além de facilitar o entendimento desses exemplos por programadores que dominam outras linguagens, esperamos com isso facilitar também sua transição para o JavaScript. Dessa forma, nossos exemplos vão conter uma função main()
sempre definida no início do arquivo e que é chamada apenas quando o arquivo termina de ser carregado pelo navegador usando a chamada window.onload = main();
.
Essa é a única função a ser chamada durante o carregamento do arquivo pelo navegador. Todas as demais funções são chamadas a partir da main()
.
Para definir uma função em JavaScript devemos usar o comando function
. Suas demais funções podem ser definidas, em qualquer ordem, após a main()
.
Observe nesse exemplo que, ao programar em JavaScript, devemos também:
;
);Em JavaScript, as variáveis precisam ser declaradas como var
,
let
ou const
. As definidas como var
são variáveis globais e
as demais são consideradas locais, ou seja, seus nomes são válidos
apenas dentro da função onde foram definidas. As variáveis do tipo const
são
constantes, ou seja, seus valores não devem ser alterados depois de
criados.
Depois de criar os três arquivos (HTML, CSS e JavaScript) na mesma pasta, carregue o arquivo script.html
em um navegador de sua preferência. A Figura Fig. 4.2 exibe o resultado desse nosso primeiro programa em JavaScript em uma janela do navegador Chrome.
Fig. 4.2 Navegador Chrome exibindo o resultado do programa JavaScript. O canvas é exibido na metade esquerda e, à direita, está o console do JavaScript.¶
Aproveite também para abrir o console do JavaScript clicando, a partir da barra de menu do Chrome, em View
-> Developer
-> JavaScript Console
. O console está também disponível em outros navegadores modernos, como o Firefox, Edge e Safari, e pode lhe ser muito útil para depurar os seus programas, por exemplo, exibindo mensagens usando o comando console.log()
como mostrado na função desenheRect()
. Antes porém de explicar o desenho, vamos entender melhor o comportamento da main()
.
main()
?¶As páginas da Web são estruturadas como objetos DOM (Document Object Model – modelo de objeto de documento), permitindo que os programas em JavaScript (ou em outra linguagem) possam ter acesso aos elementos da página. Esse documento é carregado automaticamente e o canvas
pode então ser obtido pelo método document.getElementById()
, usando o id
definido no arquivo script.html
.
Associada ao canvas podemos fazer uso de APIs gráficas distintas para desenho, também chamadas de contextos. Nesse exemplo,
a variável global ctx
recebe um contexto para desenhos 2D. Em aulas futuras, vamos fazer uso do contexto webgl2
, a API gráfica para desenhar cenas 3D usando o WebGL. Observe que, por ter sido declarada como global, ctx
pode ser utilizada dentro da função desenheRect()
diretamente, ao invés de passá-la como argumento da função. Como é possível que o contexto que você deseje utilizar não esteja disponível no navegador utilizado, é recomendado testá-lo antes de prosseguir. O programa envia um alerta caso o contexto não esteja disponível.
A função main()
chama a função desenheRect()
3 vezes. Cada vez que a função é chamada, uma mensagem é impressa no console.log
indicando a cor do retângulo a ser pintado.
Antes de desenhar algum elemento gráfico, como uma linha ou um
retângulo, é necessário definir todas as propriedades desejadas do
elemento. Nesse exemplo, apenas a cor usada para preenchimento foi
definida no atributo fillStyle
do contexto, antes de desenhar
cada retângulo usando o método fillRect()
. Métodos identificados como fill
incluem o interior da região (no caso um retângulo), enquanto métodos identificados como stroke
apenas desenham o contorno da região.
Modelo de plotter com caneta
Esse comportamento, de definir as condições (ou estado) das ferramentas de desenho, como cor do pincel ou lápis, grossura do pincel, tipo da linha (se contínua ou tracejada), etc. antes de desenhar é tipico de ferramentas de desenho 2D tradicionais conhecida como modelo de plotter com caneta (Pen Plotter). Os plotters são dispositivos ainda bastante utilizados para impressão de folhas grandes, usadas por exemplo para exibir a planta de uma casa ou edifício. Nesse modelo, devemos mover a caneta para uma determinada posição, baixar a caneta, e arrasta-la (move-la) para outras posições, desenhando as linhas que compoem o desenho. Note também a adequação desse modelo para desenhar imagens vetoriais.
Experimente desenhar apenas o contorno do retângulo, ao invés de preencher seu interior, usando
o método strokeRect()
(experimente, modificando o script.js
e recarregando o script.html
!).
O contexto 2D possui recursos simples para desenhar retângulos e outros figuras formadas por linhas. Permite também desenhar texto e imagens raster (carregadas de um arquivo, por exemplo).
Arrays no JavaScript são estruturas sequenciais dinâmicas, muito semelhante ao tipo list
do Python, ou seja, um mesmo array
pode conter elementos de tipos distintos, que podem ser inseridos e removidos dinamicamente usando os métodos push()
e pop()
. O seguinte exemplo ilustra o uso de array para a definição de pontos que definem um polígono.
/**
* Esse script mostra mais recursos do canvas e da JS
*/
// A `main()` só deve ser executada quando tudo estiver carregado
window.onload = main();
// variáveis globais
var ctx; // contexto de desenho
//==================================================================
/**
* função principal
*/
function main() {
// veja o canvas id definido no arquivo index.html
const canvas = document.getElementById('meucanvas');
// vamos definir um contexto para desenhar em 2D
ctx = canvas.getContext('2d');
if (!ctx) alert("Não consegui abrir o contexto 2d :-( ");
let pontos = [ [20, 40], [180, 120], [180, 40], [20, 120] ];
desenhePoligono( pontos );
pontos = [] // array vazio
pontos.push( [100, 60] ); // coloca um ponto
pontos.push( [440, 60] ); // experimente alterar
pontos.push( [440, 340] ); // a ordem para ver o que acontece!
desenhePoligono( pontos, 'red' );
pontos.push( [100, 340] ); // descomente essa linha
desenhePoligono( pontos, 'black', 2 );
cor = 'green';
pontos = [ [320, 240], [620, 240] ] // array não começa vazio
pontos.push( [320, 460] );
pontos.push( [620, 460] );
desenhePoligono( pontos, 'green', 0 );
desenheTexto("Exemplo de stroke e fill", 120, 420, 36, 'magenta' );
};
//==================================================================
// outras funções
// ------------------------------------------------------------------
/**
* desenha um poligono definido por pts e
* preenchido com uma cor sólida caso wid = 0.
* Caso contrário, desenha o contorno com lagura wid.
* @param {array} pts - array de pontos
* @param {string} cor - cor para pintar o poligono
* @param {number} wid - largura da borda se wid>0.
*/
function desenhePoligono( pts, cor='blue', wid = 10 ) {
let tam = pts.length;
console.log("Desenhando poligono", cor, pts, tam);
let poli = new Path2D();
poli.moveTo( pts[0][0], pts[0][1] );
for (let i = 1; i < pts.length; i++) {
poli.lineTo( pts[i][0], pts[i][1] );
console.log( pts[i][0], pts[i][1] );
}
poli.closePath(); // cria um contorno fechado.
if (wid > 0) {
ctx.strokeStyle = cor;
ctx.lineWidth = wid;
ctx.stroke( poli );
}
else { // wid <= 0 preenche o polígono
ctx.fillStyle=cor;
ctx.fill( poli );
}
}
// ------------------------------------------------------------------
/**
* recebe o texto msg e o desenha na posição (x,y) do canvas.
* @param {string} msg
* @param {number} x
* @param {number} y
* @param {number} tam - tamanho da fonte
* @param {string} cor - cor do texto
*/
function desenheTexto (msg, x, y, tam=24, cor = 'black') {
ctx.fillStyle = cor;
ctx.font = `${tam}px serif`;
ctx.fillText(msg, x, y);
}
A função main()
desse exemplo ilustra como criar e acessar os elementos de um array pontos
.
Você pode substituir o conteúdo do arquivo script.js
com esse novo programa, e recarregar o script.html
em seu navegador, para ver o resultado, que está ilustrado na Figura Fig. 4.3.
Um array pode ser criado com todos os valores entre colchetes ([ ]
).
O atributo length
contém o tamanho do array.
Um segundo polígono (triângulo em vermelho) é criado usando um array inicialmente vazio e cada elemento é inserido em seguida usando o método push()
. Observe que o triângulo se torna um retângulo (em preto) ao inserir mais um canto. Observe também que a ordem dos pontos no array é importante na definição das linhas que formam o objeto.
Fig. 4.3 Resultado do novo scrip2.js
usando arrays de pontos para desenhar polígonos.¶
Esse exemplo explora também o polimorfismo da função desenhePoligono()
, chamando a função com e sem alguns dos parâmetros opcionais. No caso, os valores default de cor
e wid
são blue
e 10
, respectivamente.
A função desenhePoligono()
recebe um array de pontos e define um path2D
, um objeto no contexto 2d representado por uma sequência de linhas, também chamado de polilinhas (polylines). O método closePath()
é usado para criar um contorno fechado, ou seja, o último ponto é conectado ao primeiro. Sem a chamada desse método, o contorno fica aberto. Experimente comentar a chamada desse método para ver o que acontece com o desenho.
Essas linhas (contorno do polígono) são desenhadas usando o método stroke()
com a cor e a largura de linha passados como argumentos da função. No entanto, quando o parâmetro wid
tem valor 0, a região interna é preenchida com a cor
usando o método fill()
.
Além do uso de arrays, há vários detalhes também ilustrados nesse exemplo, como o uso dos comandos if-else
e for
, o uso de parâmetros opcionais na função desenhePoligono
e o uso de stroke
no canvas.
Outro recurso interessante oferecido pelo canvas é a possibilidade de desenhar textos. A função desenheTexto()
mostra um exemplo desse recurso.
Uma polilinha (ou mais propriamente uma curva poligonal) é uma sequência finita de segmentos de linha unidos ponta a ponta. Esses segmentos de linha são chamados de arestas, e os pontos finais dos segmentos de linha são chamados de vértices. Um único segmento de linha é um caso especial.
Uma polilinha no plano pode ser representada simplesmente como uma sequência das coordenadas (x, y) de seus vértices. Isso é suficiente para codificar a geometria de uma polilinha. Em contraste, a maneira como a polilinha é renderizada é determinada por um conjunto de propriedades chamadas de atributos gráficos. Isso inclui elementos como cor, largura de linha e estilo de linha (sólida, pontilhada, tracejada) e como segmentos consecutivos são unidos.
A polilinha é uma forma “vetorizada” para representar formas (vector graphics). Além de permitir uma representação compacta e facilmente escalável, polilinhas são mais simples de serem desenhadas usando monitores baseados em tubos de raios catódicos.
Qualquer polilinha simples e fechada no plano define uma região que pode ser definida como interna e um externa. Este é um exemplo típico de um fato totalmente óbvio da topologia que é notoriamente difícil de provar. É chamado de teorema da curva de Jordan. Podemos preencher qualquer região com uma cor ou padrão repetitivo. Em alguns casos, a própria polilinha delimitadora também é desenhada (considerada interna).
Uma polilinha com “buracos” embutidos também define naturalmente uma região que pode ser preenchida. Na verdade, isso pode ser generalizado aninhando buracos dentro dos buracos (alternando a cor com a cor de fundo). Mesmo que uma polilinha não seja simples, é possível generalizar a noção de interior. Dado qualquer ponto, atire um raio ao infinito. Se ele cruzar a fronteira um número ímpar de vezes, será colorido. Se cruzar um número par de vezes, receberá a cor de fundo.
Começamos a introduzir as ferramentas de programação que vamos utilizar ao longo desse curso. Em particular, vamos adotar um esqueleto e sintaxe simples (ou semelhante a outras linguagens) para os programas em JavaScript para facilitar a leitura desses programas, e a escrita de futuros programas, por programadores e programadoras familiarizadas com outras linguagens.
Nessa aula vimos como criar desenhos usando o contexto 2d do canvas. Na próxima aula vamos apresentar recursos para criar desenhos interativos e introduzir o conceito de programação baseada em eventos.
Escreva um programa em JavaScript (+ HTML) que desenha, em um canvas de tamanho \(300 \times 300\), a aproximação de um círculo de raio 100 usando 8 segmentos de reta. Depois disso, altere esse número para 16, 32, 64 segmentos para ver o que acontece com a qualidade visual desse círculo.
Escreva um programa em JavaScript (+ HTML) que desenha, em um canvas de tamanho \(400 \times 400\), 50 elementos aleatórios de uma grade de dimensão \(10\times10\), com uma cor também aleatória.
Para sortear um inteiro no intervalo [min, max]
, e uma cor RGB, você pode utilizar as seguintes funções em JS:
function sorteie_inteiro (min, max) {
return Math.floor(Math.random() * (max - min) ) + min;
}
function sorteie_corRGB () {
let r = sorteie_inteiro(0, 255);
let g = sorteie_inteiro(0, 255);
let b = sorteie_inteiro(0, 255);
return `rgb( ${r}, ${g}, ${b} )`; // retorna uma string
}
A figura abaixo ilustra 3 exemplos de como deve aparecer seu canvas. Clique em reload várias vezes para gerar novos desenhos.
Fig. 4.4 Exemplos de grades \(10\times10\) com aproximadamente 50% de seus elementos pintados de forma aleatória.¶
Escreva um programa em JavaScript (+ HTML) que desenha, em um canvas de tamanho \(640 \times 480\), 10 quadrados de tamanhos e cores aleatórias, em posições também aleatórias.
Para aprender mais detalhes dessas ferramentas recomendamos as seguintes leituras: