Eventos
Interagindo com a Tela Gráfica
Para interagir com a tela gráfica precisaremos usar algum dispositivo de entrada. Por enquanto usaremos o mouse e o teclado, mas é possível usar outros dispositivos, como por exemplo o microfone ou a câmera, para enviar informações ao computador. Uma interação deve possuir ação e reação, não bastando passarmos informações ao programa. É essencial que tenhamos feedback, ou seja, que vejamos os efeitos de nossas ações no comportamento do programa, de preferência instantaneamente. Esse feedback pode ser sonoro, textual e/ou na forma de imagem (eventualmente também na forma de movimentos físicos, como no caso de um robô ou de outros equipamentos controlados pelo computador). Usaremos a função draw() para dar retorno visual, ao atualizar o conteúdo da tela gráfica em função das ações do usuário. Como a draw() é executada 60 vezes por segundo, as respostas a essas ações serão visualizadas instantaneamente. Para obter as ações executadas pelo usuário, via mouse e/ou teclado, usaremos as funções que tratam os eventos (movimentos, cliques, acionamentos de tecla) desses dispositivos.
Funções de tratamento de eventos: Teclado e Mouse
Uma função que trata eventos é disparada sempre que ocorre uma situação específica à qual esteja vinculada. Dois exemplos desse tipo de função são as já conhecidas setup() e draw(). A primeira, está vinculada ao evento de início de execução do programa. Assim, sempre que um programa é executado a setup() é acionada. Como esse evento (início de execução) só ocorre uma vez a cada execução do programa, a setup() é executada uma única vez também. Já a função draw() está associada a um contador de tempo, que na configuração default dispara um evento a cada 1/60 de segundo. Dessa forma a draw() é executada ininterruptamente 60 vezes por segundo.
As funções de tratamento de eventos inicialmente não possuem conteúdo. Cabe ao programador escrever a sequência de comandos que deseja sejam executados a cada vez que determinado evento de seu interesse ocorra. E esse conteúdo deve ser escrito num bloco que especifica a função correspondente àquele evento, conforme já visto para as funções setup() e draw():
function nomeDaFuncao() {
// comandos que serão executados quando ocorrer o evento associado
// a essa função
}
O p5.js inclui algumas outras funções que são executadas automaticamente, além da setup() e draw(). Entre elas estão funções para o tratamento de comandos de teclado e mouse, conforme resumido na lista a seguir:
| Função | executada automaticamente quando... |
|---|---|
| keyPressed() | Uma tecla do teclado é pressionada |
| keyReleased() | Uma tecla do teclado para de ser pressionada |
| keyTyped() | Uma tecla do teclado é pressionada e liberada |
| mouseClicked() | Um botão do mouse é pressionado e liberado |
| mouseDragged() | O mouse é movimentado com um botão pressionado |
| mouseMoved() | O mouse é movimentado sem nenhum botão pressionado |
| mousePressed() | Um botão do mouse é pressionado |
| mouseReleased() | Um botão do mouse deixa de ser pressionado |
| mouseWheel() | A roda do mouse é rotacionada |
Todas essas funções têm exatamente o mesmo nome em p5.js.
Exemplo: Tratamento de evento de mouse
Suponha que se deseja desenhar uma bolinha na tela, na posição do mouse, toda vez que esse for clicado. Uma forma simples de fazer isso é programar a função de tratamento de evento mouseClicked() para que faça o desenho de um círculo na posição do mouse. Toda vez que o mouse for clicado será gerado o evento "mouseClicked" e a função correspondente mouseClicked() será executada. Como a programamos para desenhar um círculo na posição do mouse, obteremos o efeito desejado. Execute o código a seguir e confira (clique dentro do canvas).
function setup() { // executada automaticamente no início de execução do programa
createCanvas(500, 500);
background(0); // fundo preto
}
function draw() { // mesmo vazia é necessária para que a janela seja redesenhada
}
function mouseClicked() { // executada automaticamente sempre que o mouse for clicado
// desenha círculo de 20 pixels na posição do mouse
circle(mouseX, mouseY, 20);
}
Como você pode observar, não há nada dentro da função draw() e mesmo assim você consegue gerar uma janela interativa. Entretanto, é importante ressaltar que, de qualquer forma, é necessário que ela seja escrita mesmo vazia, pois só assim a janela será redesenhada e refletirá as eventuais ações executadas pelas outras funções de tratamento de eventos.
Atividade 5
Escreva um programa que desenha bolinhas brancas enquanto o mouse for movido com botão pressionado (mouseDragged) e limpa a tela quando o botão do mouse for solto (mouseReleased).
Exemplo: Tratamento de evento de teclado
Vamos testar o funcionamento da função keyTyped(). Toda vez que uma tecla é pressionada essa função é disparada. Mas para que algo aconteça, como por exemplo, mostrar na tela o que foi digitado, é necessário escrever o programa a ser executado pela keyTyped(). O caractere da última tecla pressionada pode ser obtido na variável automática de sistema key. Diferente do Processing, em p5.js a variável key já é diretamente um texto (String), então não é preciso nenhuma conversão para usá-la em text(). Execute o programa a seguir para testar a função que trata o evento de digitar no teclado.
function setup() {
createCanvas(500, 300);
background(0);
}
function draw() { // mesmo vazia é necessária para que a janela seja redesenhada
}
// função executada quando uma tecla é pressionada e liberada
function keyTyped() {
// imprime a tecla digitada na posição do mouse
text(key, mouseX, mouseY);
}
Atividade 6
Elabore um programa que escreve na tela os caracteres que são digitados, iniciando na posição (0,50) e adicionando 10 pixels na posição x a cada caractere digitado.
Variáveis automáticas de entrada
Para muitas situações, podemos simplesmente utilizar as variáveis automáticas do p5.js, tais como mouseX, mouseY, mouseIsPressed, mouseButton, keyIsPressed, key e keyCode dentro da função draw() para responder a comandos do usuário. Essas variáveis são úteis quando precisamos responder de forma mais imediata ou, ainda, armazenar sequências de comandos, pois elas só armazenam a última tecla ou botão pressionados.
Atenção — diferença importante em relação ao Processing
No Processing, a variável booleana que indica se uma tecla está pressionada se chama keyPressed, mesmo nome da função de evento keyPressed() — o que é possível porque Java trata variáveis e funções em espaços de nomes diferentes. Em JavaScript isso causaria um conflito, já que uma função é armazenada numa variável comum. Por isso, o p5.js renomeou essa variável booleana para keyIsPressed. O mesmo vale para o mouse: a variável booleana chama-se mouseIsPressed, em vez de mousePressed, que continua sendo o nome da função de evento.
Para ilustrar esses conceitos, vamos comparar dois programas. No primeiro, vamos escrever na janela a tecla que o usuário está pressionando, usando as variáveis automáticas.
let x = 0;
function setup() {
createCanvas(500, 300);
background(0);
}
function draw() {
// existe tecla apertada?
if (keyIsPressed) {
// escreve na tela
text(key, x, 50);
x = x + 10;
}
}
Se executarmos esse programa, veremos que cada tecla pressionada é repetida várias vezes na tela. A razão para isso é que a função draw() é executada 60 vezes por segundo, portanto mais rapidamente que o tempo que um humano leva para pressionar e soltar uma tecla. O que queremos, na verdade, é detectar cada tecla que foi pressionada e atualizar a tela somente uma vez. Isso pode ser feito com o programa a seguir.
let x = 0;
let tecla = "";
function setup() {
createCanvas(500, 300);
background(0);
}
// função executada quando uma tecla é pressionada e liberada
function keyTyped() {
// memoriza a tecla que foi digitada
tecla = key;
}
function draw() {
// existe tecla apertada?
if (tecla !== "") {
// escreve na tela
text(tecla, x, 50);
// "esquece" a tecla que já foi desenhada na tela
tecla = "";
x = x + 10;
}
}
Saiba mais sobre escrever textos e números na janela gráfica
Você pode alterar o tamanho da fonte usando o comando textSize, a cor através do comando fill e o alinhamento do texto através do comando textAlign.
Na seção anterior, escrevemos o texto a ser desenhado na tela diretamente no comando
text. No entanto, também é possível armazenar texto em variáveis. O programa a seguir mostra o uso de uma variável desse tipo, além de uma fonte carregada de um arquivo. Diferente do Processing (que usa o formato proprietário .vlw, gerado a partir do menu Tools → Create Font), em p5.js as fontes são carregadas diretamente de arquivos .ttf ou .otf, dentro da função preload(), que garante que a fonte termine de carregar antes dosetup()ser executado.
let f;
function preload() {
f = loadFont('minhafonte.ttf');
}
function setup() {
createCanvas(500, 200);
background(0);
fill(255);
textFont(f);
let t;
t = "teste 1 2 3";
text(t, 50, 50);
}
Observe que o conteúdo a ser atribuído para a variável t é indicado entre aspas. Isso é necessário para identificar os limites do texto, caso contrário não haveria como determinar o que é texto e o que é programa.
Textos em JavaScript possuem alguns recursos adicionais em relação a outros tipos de variáveis. Por exemplo, se a variável t guarda um texto, podemos usar a propriedade t.length (sem parênteses, pois é uma propriedade, não uma função) para obter o comprimento do texto (em número de caracteres). Também podemos utilizar a função t.substring() para obter uma parte do texto original, exatamente como no Processing, como ilustrado nos exemplos abaixo:
let t;
t = "teste 1 2 3";
let c;
c = t.length; // c irá receber o valor 11 (propriedade, sem parênteses)
let w;
w = t.substring(0, 5); // w irá receber o valor "teste"
let k;
k = t.substring(6, 11); // k irá receber o valor "1 2 3"
A figura abaixo ajuda a entender os parâmetros do comando substring: "cortamos uma fatia" do texto original entre as duas posições passadas para o comando. É importante observar que a contagem começa do zero, que corresponde à posição do primeiro caractere do texto. Da mesma forma, a "fatia" não inclui o caractere indicado na posição final.
É possível utilizar a operação de adição (+) para agrupar dois ou mais textos, exatamente como no Processing. Veja o exemplo abaixo:
function setup() {
createCanvas(200, 200);
background(0);
fill(255);
let a;
a = "teste";
let b;
b = " 1 2 3";
let c;
c = a + b; // o sinal + indica concatenação de textos
text(c, 30, 30);
}
É possível também utilizar a operação de adição para agrupar texto (entre aspas) com conteúdos de variáveis numéricas, que são automaticamente convertidas para texto — assim como no Processing.
function setup() {
createCanvas(200, 200);
background(0);
fill(255);
let x, a, b;
a = 11;
b = 7;
x = a - b;
text("a subtração é " + x, 50, 50);
}
��������������������������������������������������������
