.. _canvas: Desenhando no Canvas HTML com JavaScript ======================================== 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. .. _introducao: Esqueleto HTML para usar o canvas --------------------------------- 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 ```` e ````. A parte delimitada por ```` e ```` não é exibida pelo navegador quando a página é carregada. Ela tipicamente contém: - um título delimitado pelas tags `` ``); - links para os `arquivos CSS `__ usados para configurar os estilos usados pelos elementos da página, com extensão ``.css``; - e outros meta-dados. No nosso caso, deve conter também o arquivo que contém os programas em JavaScript, como o ``script.js``. Observe que os arquivos em JavaScript tem a extensão ``.js``. O trecho delimitado por `` `` é 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 (````) precisamos incluir uma tag `` ``, 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 ````, o ``script.html`` cria um elemento canvas com :math:`640 \times 480` (``width`` :math:`\times` ``height``) pixels em um sistema de coordenadas com o eixo ``x`` para a direita e ``y`` para baixo como: .. figure:: figuras/a04/Canvas_default_grid.png :alt: Coordenadas no Canvas :width: 50% :name: fig-CoordenadasCanvas :align: center 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 `` ``, 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. Exemplo de um esqueleto HTML usando o canvas ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. literalinclude:: ./labs/l04/canvas01/script.html :language: html :name: arquivo 01 :caption: arquivo ``script.html`` .. :linenos: Usando arquivos CSS para formatar elementos do HTML --------------------------------------------------- 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) `__. Exemplo de um arquivo CSS ~~~~~~~~~~~~~~~~~~~~~~~~~ .. literalinclude:: ./labs/l04/canvas01/estilo.css :language: css :caption: arquivo ``estilo.css`` .. :linenos: Desenhando no canvas usando JavaSCript -------------------------------------- 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. Exemplo: arquivo ``script.js`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. literalinclude:: ./labs/l04/canvas01/script.js :language: javascript :caption: arquivo ``script.js`` .. :linenos: Discussão ~~~~~~~~~ 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 :code:`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: - terminar cada comando com um ponto-e-vírgula (``;``); - indicar o início e fim de cada bloco usando chaves, a menos quando o bloco conter apenas um comando. 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 :numref:`fig-Canvas01` exibe o resultado desse nosso primeiro programa em JavaScript em uma janela do navegador Chrome. .. figure:: figuras/a04/canvas01.png :alt: Navegador Chrome exibindo o resultado do programa JavaScript :width: 90% :name: fig-Canvas01 :align: center 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()``. O que faz a função ``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. .. admonition:: 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 -------------------- `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. .. literalinclude:: ./labs/l04/canvas02/script.js :language: javascript :caption: arquivo ``scrip2.js`` Discussão ~~~~~~~~~ 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 :numref:`fig-Canvas02`. 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. .. figure:: figuras/a04/canvas02.png :alt: Resultado desenhado no canvas usando stroke :width: 60% :name: fig-Canvas02 :align: center 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. Mais sobre polilinhas --------------------- 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 linha infinita, que se estende até o infinito em ambos os lados, geralmente não é considerada uma polilinha. * Uma polilinha é **fechada** se terminar onde começa. * É **simples** se não se auto-cruzar. Auto-cruzar inclui coisas como duas arestas se cruzando, um vértice se cruzando no interior de uma aresta ou mais de duas arestas compartilhando um vértice comum. * Uma polilinha fechada simples também é chamada de **polígono simples**. Se todos os seus ângulos internos forem no máximo :math:`180^o`, então é um **polígono convexo**. 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. Região interna e externa ~~~~~~~~~~~~~~~~~~~~~~~~ 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. .. ################################################################# Onde estamos e para onde vamos ------------------------------ 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. Exercícios ---------- 1. Escreva um programa em JavaScript (+ HTML) que desenha, em um canvas de tamanho :math:`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. 2. Escreva um programa em JavaScript (+ HTML) que desenha, em um canvas de tamanho :math:`400 \times 400`, 50 elementos aleatórios de uma grade de dimensão :math:`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: .. code:: javascript 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. .. figure:: figuras/a04/exemplos-ex02.png :alt: Exemplos de uma grade pintada aleatoriamente :width: 70% :name: fig-exemplos-exCanvas02 :align: center Exemplos de grades :math:`10\times10` com aproximadamente 50% de seus elementos pintados de forma aleatória. 3. Escreva um programa em JavaScript (+ HTML) que desenha, em um canvas de tamanho :math:`640 \times 480`, 10 quadrados de tamanhos e cores aleatórias, em posições também aleatórias. Para saber mais --------------- Para aprender mais detalhes dessas ferramentas recomendamos as seguintes leituras: - `Introdução a Web `__; - `Tutorial de HTML `__; - `Uma reintrodução ao JavaScript `__; - `Tutorial do canvas `__.