Exercitando: Canvas + Objetos + Animação!

Esse post requer o conhecimento dos seguintes posts:


Vamos unir os conhecimentos adquiridos até aqui para criar alguma coisa visualmente interessante. Já aprendemos a desenhar formas no canvas e a criar objetos com Javascript. Ainda não é possível criar um jogo, pois nos falta o conceito de colisão, mas isso não quer dizer que não podemos fazer nada.

Fazendo acontecer

Vamos criar um classe para desenhar uma bola de tamanho, cor e velocidade variada. Essa bola vai se mover pelo canvas.

A classe Bola

Bola será a classe que usaremos como base. O código é o seguinte:

/* Determino o canvas que receberá o desenho */
var canvas = document.getElementById('output').getContext('2d');

/* Classe Bola. Desenha uma bola no canvas */
/* Parâmetros:
   cor: a cor da bola
   raio: o tamanho da bola
   x: posição da bola no eixo X
   y: posição da bola no eixo Y
   canvas: onde a bola será desenhada */
var Bola = function(cor, raio, x, y, canvas) {
this.cor = cor;
this.raio = raio;
this.posicaoX = x;
this.posicaoY = y;
this.canvas = canvas;
}

Bola.prototype = {
desenha: function() {
this.canvas.fillStyle = this.cor;
this.canvas.beginPath();
this.canvas.arc(this.posicaoX, this.posicaoY, this.raio, 0, Math.PI * 2);
this.canvas.fill();
}

}

Ao instanciarmos um objeto da classe bola, passamos a cor, o raio, a posição no canvas e o próprio canvas, como explicado no código (aí está a importância dos comentários).

Vamos testar o funcionamento da classe pelo console do navegador. Abra lá o console e instancie uma bola.

red = new Bola('red', 20, 40, 40, canvas);

Ao pressionar ENTER uma mágica invisível irá acontecer: um objeto chamado red será criado com as características que você determinou. Mas a bola não foi desenha, certo? Pois bem. Desenhe a bola usando o comando:

red.desenha();

Pronto. Você pode instanciar a quantidade de bolas que quiser. Faça isso e altere cor, raio e posição delas no canvas. A classe bola está funcionando adequadamente.

Animando

As bolas estão estáticas no canvas, mas podemos escrever um pouco mais de código e animá-las. Vamos criar uma classe que receba os objetos criados e os movimente pela tela, mas os impeça de vazar pelas bordas do canvas. Vamos precisar alterar um pouco nossa classe Bola e criar uma nova classe chamada Animacao.

A nova classe Bola

Algumas coisas precisam acontecer para que a animação seja bem sucedida. A primeira delas é atualizar nossa classe Bola para que receba uma nova propriedade: velocidade. Lembre-se que estamos trabalhando com o contexto 2D, então temos duas grandezas: X e Y, como apresentado na imagem:



Para que a animação funcione, a bola precisa se deslocar nos eixos X e Y numa determinada velocidade. Essa velocidade será determinada em número de pixels por segundo ("por segundo" em teoria. Na prática o tempo varia bastante). Sendo assim, duas novas propriedades serão adicionadas à classe Bola e uma nova função também: atualiza, que atualizará a posição da bola no canvas conforme a posição e a velocidade.

/* Classe Bola. Desenha uma bola no canvas */
/* Parâmetros:
   cor: a cor da bola
   raio: o tamanho da bola
   x: posição da bola no eixo X
   y: posição da bola no eixo Y
   canvas: onde a bola será desenhada */
var Bola = function(cor, raio, x, y, canvas) {
this.cor = cor;
this.raio = raio;
this.posicaoX = x;
this.posicaoY = y;
this.velocidadeX = 5; //Velocidade no eixo X
this.velocidadeY = 5; //Velocidade no eixo Y
this.contexto = canvas;
}

Bola.prototype = {
desenha: function() {
this.contexto.fillStyle = this.cor;
this.contexto.beginPath();
this.contexto.arc(this.posicaoX, this.posicaoY, this.raio, 0, Math.PI * 2);
this.contexto.fill();
},

atualiza: function() {
/* Identifica a posição X da bola no canvas. Se a posição
           da bola for menor que o raio da bola (ou seja, se a bola
           estiver na extremidade esquerda do canvas) ou se for 
           maior que o canvas menos o raio da bola, então inverte 
           a velocidade X */
if(this.posicaoX < this.raio || (this.posicaoX > this.contexto.canvas.width - this.raio)) {
this.velocidadeX *= -1;
}

/* Agora trata a posição Y da bola */
if(this.posicaoY < this.raio || (this.posicaoY > this.contexto.canvas.height - this.raio)) {
this.velocidadeY *= -1;
}

//Atualiza a posição da bola no canvas
this.posicaoX += this.velocidadeX;
this.posicaoY += this.velocidadeY;
}

}

A classe Bola está atualizada. Agora é possível determinar a velocidade dela nos dois eixos possíveis, e também é possível atualizar a posição dela na tela.

A classe Animacao

Agora uma outra classe vai ativar a animação e rodá-la por um tempo. Essa classe deve receber a bola a ser animada, deve ter o método .liga() e o .desliga() para ligar e desligar a animação, e deve ativar nas bolas o método .atualiza(), para que a posição de cada uma das bolinhas criadas seja atualizada.

/* Classe Animacao */
/* Possibilita que objetos se reposicionem no canvas */
var Animacao = function(canvas) {
this.sprites = []; //Lista de objetos
this.ligada = false; //Estado inicial da animação é "desligada"
this.contexto = canvas; //O canvas que receberá o desenho
}

Animacao.prototype = {
/* Adiciona sprites */
adiciona: function(item) {
this.sprites.push(item);
return 'Sprite adicionado';
},

/* Liga a animação */
liga: function() {
this.ligada = true;
this.proximo();
return this.ligada;
},

/* Desliga a animação */
desliga: function() {
this.ligada = false;
this.limpaTela();
return this.ligada;
},

/* Limpa a tela para o próximo quadro da animação */
limpaTela: function() {
var canvas = this.contexto.canvas;
this.contexto.clearRect(0, 0, canvas.width, canvas.height);
},

/* Chama o próximo quadro da animação */
proximo: function() {
/* Se a animação estiver ligada */
if(this.ligada) {
this.limpaTela(); //Limpa a tela
for(var i in this.sprites) { //Percorre os sprites
this.sprites[i].atualiza(); //Chama o método .atualiza() do sprite
}

for(var i in this.sprites) { //Percorre os sprites
this.sprites[i].desenha(); //Chama o método .desenha() do sprite
}

var animacao = this; //Faz referência a si mesmo
requestAnimationFrame(function() { //Método novo!
animacao.proximo(); //Chama o próximo quadro da animação
});
}
}

}

Ok. Essa é a classe animação. Ela precisa de sprites para funcionar. Sprites são elementos da tela e esse termo é muito usado no desenvolvimento de jogos. Vamos ver como tudo vai funcionar usando o console do navegador. Vamos criar três bolas.

var green = new Bola('green', 17, 44, 21, canvas);
var dkred = new Bola('red', 14, 89, 102, canvas);
var purpl = new Bola('purple', 10, 103, 71, canvas);

Queremos animar 3 bolas na tela. Agora precisamos criar o objeto que controlará a animação:

var anima = new Animacao(canvas);

E, depois, precisamos adicionar todas as bolas nessa animação:

anima.adiciona(green);
anima.adiciona(dkred);
anima.adiciona(purpl);

Agora é hora de animar tudo isso:

anima.liga();

Se tudo correu bem, 3 bolinhas estarão passeando pela tela. Tente alterar a velocidade de cada uma delas usando:

green.velocidadeX = 8;

Ou a cor...

dkred.cor = '#45f869';

As alterações acontecem em tempo real pois alteramos o valor das propriedades do objeto no console. No futuro vamos alterar as propriedades dos objetos usando o teclado ou o mouse. Podemos mudar a direção de um sprite pressionando a seta para esquerda ou para a direita, acelerar um carrinho pressionando espaço ou atirar.

Para desligar a animação e limpar a tela, use:

anima.desliga();

Por hoje é só.

Comentários