Tutorial de Canvas – Parte 7 – Animações básicas

Este é o sétimo e último artigo de uma série que irá apresentar os recursos disponibilizados pela nova tag <canvas> do HTML5. Nesse artigo, veremos como criar animações básicas no Canvas. Você pode acessar uma lista de todos os artigos da série nesse link.

Índice do conteúdo

  1. Passos básicos para animação
  2. Controlando uma animação
    1. Atualizações agendadas
    2. Atualizando com base na interação do usuário
      1. Um sistema solar animado
      2. Um relógio animado
      3. Uma imagem panorâmica em loop
  3. Outros exemplos
  4. Veja também

Como estamos usando JavaScript para controlar os elementos do <canvas>, é bastante fácil criar animações interativas. Criar animações mais complexas pode demandar um pouco mais de trabalho; espero que esse artigo ajude-o a começar.

Provavelmente a maior limitação é que uma vez que uma forma seja desenhada, ela permanece da mesma forma. Se precisarmos nos mover, temos que redesenha-la e tudo o que foi desenhado antes dela. Quando se tem quadros muito complexos, isso toma bastante tempo e a performance depende muito do computador onde ela está sendo executada.

Passos básicos para animação

Esses são os passos básicos que precisa seguir para desenhar um quadaro:

  1. Limpar o canvasA menos que as formas que irá desenhar completem o canvas (por exemplo, uma imagem de fundo), precisa limpar qualquer forma que foi desenhada anteriormente. A maneira mais fácil de fazer isso é usando o método clearRect().
  2. Salvar o estado do canvas Se estiver alterando algum ajuste (como estilo, transformações, etc) que afetem o estado do canvas e quiser garantir que o estado original seja usado a cada vez que o quadro é desenhado, precisa salvar o estado original.
  3. Desenhe as formas animadas Esse é o passo onde se faz a renderização propriamente dita do quadro.
  4. Restaure o estado do canvas Se você salvou o estado do canvas, restaure-o antes de desenhar um novo quadro.

Controlando uma animação

Formas são desenhadas no canvas usando os métodos dele diretamente ou chamando-se funções personalizadas. Em circunstâncias normais, apenas visualizamos esses resultados aparecerem no canvas quando o script termina de ser executado. Por exemplo, não é possível criar uma animação dentro de um loopfor.

Isso significa que precisamos de uma forma de executar nossos desenhos em um período de tempo. Existem duas formas de controlar uma animação dessa forma.

Atualizações agendadas

Em primeiro lugar, temos as funções window.setInterval() e window.setTimeout(), que podem ser usadas para chamar uma função especifica dentro de uma certo período de tempo.

Nota: O método window.requestAnimationFrame() é atualmente a forma recomendada de agendar animações.

setInterval(function, delay)
Inicia a execução repetida da função especificada por function a cada delay milissegundos.
setTimeout(function, delay)
Executa a função especificada por function em delay milissegundos.

Se não quiser nenhuma interação do usuário é melhor usar a função setInterval() que irá executar repetidamente o código fornecido.

Atualizando com base na interação do usuário

O segundo método que podemos usar para controlar uma animação é através da entrada do usuário. Se quisermos criar um jogo. poderíamos usar eventos do teclado ou mouse para controlar a animação. Através da configuração de EventListeners, podemos capturar qualquer interação e executar nossas próprias funções e animação.

Nos exemplos abaixo, usaremos o método window.setInterval() para controlar a animação. No final desse artigo estão alguns links para exemplos que usam window.setTimeout().

Um sistema solar animado

Esse exemplo anima um pequeno modelo de nosso sistema solar.

var sun = new Image();
var moon = new Image();
var earth = new Image();
function init(){
  sun.src = 'https://mdn.mozillademos.org/files/1456/Canvas_sun.png';
  moon.src = 'https://mdn.mozillademos.org/files/1443/Canvas_moon.png';
  earth.src = 'https://mdn.mozillademos.org/files/1429/Canvas_earth.png';
  setInterval(draw,100);
}

function draw() {
  var ctx = document.getElementById('canvas').getContext('2d');

  ctx.globalCompositeOperation = 'destination-over';
  ctx.clearRect(0,0,300,300); // clear canvas

  ctx.fillStyle = 'rgba(0,0,0,0.4)';
  ctx.strokeStyle = 'rgba(0,153,255,0.4)';
  ctx.save();
  ctx.translate(150,150);

  // Earth
  var time = new Date();
  ctx.rotate( ((2*Math.PI)/60)*time.getSeconds() + ((2*Math.PI)/60000)*time.getMilliseconds() );
  ctx.translate(105,0);
  ctx.fillRect(0,-12,50,24); // Shadow
  ctx.drawImage(earth,-12,-12);

  // Moon
  ctx.save();
  ctx.rotate( ((2*Math.PI)/6)*time.getSeconds() + ((2*Math.PI)/6000)*time.getMilliseconds() );
  ctx.translate(0,28.5);
  ctx.drawImage(moon,-3.5,-3.5);
  ctx.restore();

  ctx.restore();

  ctx.beginPath();
  ctx.arc(150,150,105,0,Math.PI*2,false); // Earth orbit
  ctx.stroke();

  ctx.drawImage(sun,0,0,300,300);
}
Screenshot Live sample

Um relógio animado

Esse exemplo desenha um relógio animado, exibindo sua hora atual.

function init(){
  clock();
  setInterval(clock,1000);
}

function clock(){
  var now = new Date();
  var ctx = document.getElementById('canvas').getContext('2d');
  ctx.save();
  ctx.clearRect(0,0,150,150);
  ctx.translate(75,75);
  ctx.scale(0.4,0.4);
  ctx.rotate(-Math.PI/2);
  ctx.strokeStyle = "black";
  ctx.fillStyle = "white";
  ctx.lineWidth = 8;
  ctx.lineCap = "round";

  // Hour marks
  ctx.save();
  for (var i=0;i<12;i++){
    ctx.beginPath();
    ctx.rotate(Math.PI/6);
    ctx.moveTo(100,0);
    ctx.lineTo(120,0);
    ctx.stroke();
  }
  ctx.restore();

  // Minute marks
  ctx.save();
  ctx.lineWidth = 5;
  for (i=0;i<60;i++){
    if (i%5!=0) {
      ctx.beginPath();
      ctx.moveTo(117,0);
      ctx.lineTo(120,0);
      ctx.stroke();
    }
    ctx.rotate(Math.PI/30);
  }
  ctx.restore();

  var sec = now.getSeconds();
  var min = now.getMinutes();
  var hr  = now.getHours();
  hr = hr>=12 ? hr-12 : hr;

  ctx.fillStyle = "black";

  // write Hours
  ctx.save();
  ctx.rotate( hr*(Math.PI/6) + (Math.PI/360)*min + (Math.PI/21600)*sec )
  ctx.lineWidth = 14;
  ctx.beginPath();
  ctx.moveTo(-20,0);
  ctx.lineTo(80,0);
  ctx.stroke();
  ctx.restore();

  // write Minutes
  ctx.save();
  ctx.rotate( (Math.PI/30)*min + (Math.PI/1800)*sec )
  ctx.lineWidth = 10;
  ctx.beginPath();
  ctx.moveTo(-28,0);
  ctx.lineTo(112,0);
  ctx.stroke();
  ctx.restore();

  // Write seconds
  ctx.save();
  ctx.rotate(sec * Math.PI/30);
  ctx.strokeStyle = "#D40000";
  ctx.fillStyle = "#D40000";
  ctx.lineWidth = 6;
  ctx.beginPath();
  ctx.moveTo(-30,0);
  ctx.lineTo(83,0);
  ctx.stroke();
  ctx.beginPath();
  ctx.arc(0,0,10,0,Math.PI*2,true);
  ctx.fill();
  ctx.beginPath();
  ctx.arc(95,0,10,0,Math.PI*2,true);
  ctx.stroke();
  ctx.fillStyle = "rgba(0,0,0,0)";
  ctx.arc(0,0,3,0,Math.PI*2,true);
  ctx.fill();
  ctx.restore();

  ctx.beginPath();
  ctx.lineWidth = 14;
  ctx.strokeStyle = '#325FA2';
  ctx.arc(0,0,142,0,Math.PI*2,true);
  ctx.stroke();

  ctx.restore();
}
Screenshot Live sample

Uma imagem panorâmica em loop

Nesse exemplo, uma imagem panorâmica é rolada da esquerda para a direita. Foi usada uma imagem do Parque Nacional Yosemetie retirada da Wikipedia, mas você pode usar qualquer imagem que seja maior que o canvas.

var img = new Image();

// User Variables - customize these to change the image being scrolled, its
// direction, and the speed.

img.src = '/files/4553/Capitan_Meadows,_Yosemite_National_Park.jpg';
var CanvasXSize = 800;
var CanvasYSize = 200;
var speed = 30; //lower is faster
var scale = 1.05;
var y = -4.5; //vertical offset

// Main program

var dx = 0.75;
var imgW;
var imgH;
var x = 0;
var clearX;
var clearY;
var ctx;

img.onload = function() {
    imgW = img.width*scale;
    imgH = img.height*scale;
    if (imgW > CanvasXSize) { x = CanvasXSize-imgW; } // image larger than canvas
    if (imgW > CanvasXSize) { clearX = imgW; } // image larger than canvas
    else { clearX = CanvasXSize; }
    if (imgH > CanvasYSize) { clearY = imgH; } // image larger than canvas
    else { clearY = CanvasYSize; }
    //Get Canvas Element
    ctx = document.getElementById('canvas').getContext('2d');
    //Set Refresh Rate
    return setInterval(draw, speed);
}

function draw() {
    //Clear Canvas
    ctx.clearRect(0,0,clearX,clearY);
    //If image is <= Canvas Size
    if (imgW <= CanvasXSize) {
        //reset, start from beginning
        if (x > (CanvasXSize)) { x = 0; }
        //draw aditional image
        if (x > (CanvasXSize-imgW)) { ctx.drawImage(img,x-CanvasXSize+1,y,imgW,imgH); }
    }
    //If image is > Canvas Size
    else {
        //reset, start from beginning
        if (x > (CanvasXSize)) { x = CanvasXSize-imgW; }
        //draw aditional image
        if (x > (CanvasXSize-imgW)) { ctx.drawImage(img,x-imgW+1,y,imgW,imgH); }
    }
    //draw image
    ctx.drawImage(img,x,y,imgW,imgH);
    //amount to move
    x += dx;
}

Below is the <canvas> in which the image is scrolled. Note that the width and height specified here must match the values of the CanvasXZSize and CanvasYSize variables in the JavaScript code.

<canvas id="canvas" width="800" height="200"></canvas>

Live sample

Outros exemplos

Gartic
Multi-player drawing game.
Canvascape
A 3D adventure game (first-person shooter).
A basic ray-caster
A good example of how to do animations using keyboard controls.
canvas adventure
Another nice example that uses keyboard controls.
An interactive Blob
Have fun with the blob.
Flying through a starfield
Fly through stars, circles, or squares.
iGrapher
An example that charts stock market data.

Veja também