This translation is incomplete. Please help translate this article from English.

Atès que estem usant Javascript per controlar els elements <canvas>, també és molt fàcil fer animacions (interactives). En aquest capítol veurem com fer algunes animacions bàsiques.

Probablement, la major limitació és que, una vegada que es dibuixa una forma, aquesta es manté així. Si necessitem moure-la, hem de tornar a dibuixar-la i tot el que s'ha dibuixat abans. Es necessita molt temps per tornar a dibuixar quadres complexos i el rendiment depèn en gran manera de la velocitat de l'equip en el qual s'està executant.

Passos bàsics d'animació

Aquests són els passos que s'han de seguir per dibuixar un marc:

  1. Esborrar el llenç
    A menys que les formes que es dibuixin omplin el llenç complet (per exemple, una imatge de fons), és necessari esborrar qualsevol forma que s'hi hagi dibuixat prèviament. La manera més fàcil de fer-ho, és usant el mètode clearRect().
  2. Guardar l'estat del llenç
    Si es canvia qualsevol configuració (com ara estils, transformacions, etc.) que afectin a l'estat del llenç i ens volem assegurar que l'estat original s'utilitza cada vegada que es dibuixa un marc, hem de guardar aquest estat original.
  3. Dibuixar formes animades
    El pas on es fa la representació del marc real.
  4. Restaurar l'estat del llenç
    Si s'ha guardat l'estat, ho hem de restaurar abans de dibuixar un nou marc.

Controlar una animació

Les formes es dibuixen al llenç usant els mètodes de canvas directament o cridant a les funcions personalitzades. En circumstàncies normals, només veiem que aquests resultats apareixen en el llenç quan el script acaba d'executar-se. Per exemple, no és possible fer una animació des d'un bucle for.

Això significa que necessitem una forma d'executar les nostres funcions de dibuix durant un període de temps. Hi ha dues maneres de controlar una animació com aquesta.

Actualitzacions programades

Primer estan les funcions window.setInterval(), window.setTimeout() i window.requestAnimationFrame(), que es poden utilitzar per cridar a una funció específica durant un període de temps determinat.

setInterval(function, delay)
Inicia repetidament l'execució de la funció especificada per la funció, cada mil·lisegons de retard.
setTimeout(function, delay)
Executa la funció especificada per la function en mil·lisegons de delay.
requestAnimationFrame(callback)
Li diu al navegador que desitja realitzar una animació i sol·licita al navegador que cridi a una funció especifica per actualitzar una animació abans del proper repintat.

Si no es vol cap interacció amb l'usuari, es pot utilitzar la funció setInterval() que executa repetidament el codi proporcionat. Si volguéssim fer un joc, podríem usar esdeveniments de teclat o ratolí per controlar l'animació i usar setTimeout(). En establir EventListeners, capturem qualsevol interacció de l'usuari i s'executan les nostres funcions d'animació

En els exemples següents, utilitzarem el mètode window.requestAnimationFrame() per controlar l'animació. El mètode requestAnimationFrame proporciona una manera fluïda i eficient per a l'animació, cridant al marc d'animació quan el sistema estigui preparat per pintar el marc. El nombre de crides retornades és generalment 60 vegades per segon i pot reduir-se a una taxa més baixa quan s'executa en les pestanyes de fons. Per a més informació sobre el bucle d'animació, especialment per a jocs, veure l'article Anatomia d'un videojoc en la nostra Zona de desenvolupament de jocs.

Un sistema solar animat

Aquest exemple anima un petit model del nostre 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';
  window.requestAnimationFrame(draw);
}

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);

  window.requestAnimationFrame(draw);
}

init();

ScreenshotLive sample

Un rellotge animat

Aquest exemple dibuixa un rellotge animat que mostra l'hora actual.

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();

  window.requestAnimationFrame(clock);
}

window.requestAnimationFrame(clock);

ScreenshotLive sample

Un panorama en bucle

En aquest exemple, es desplaça una imatge panoràmica d'esquerra a dreta. Estem usant una imatge del Parc Nacional Yosemite, que hem pres de Wikipedia, però es pot usar qualsevol imatge que sigui més gran que el llenç.

var img = new Image();

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

img.src = 'https://mdn.mozillademos.org/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 width larger than canvas
    else { clearX = CanvasXSize; }
    if (imgH > CanvasYSize) { clearY = imgH; } // image height larger than canvas
    else { clearY = CanvasYSize; }
    
    // get canvas context
    ctx = document.getElementById('canvas').getContext('2d');
 
    // set refresh rate
    return setInterval(draw, speed);
}

function draw() {
    ctx.clearRect(0, 0, clearX, clearY); // clear the canvas
    
    // if image is <= Canvas Size
    if (imgW <= CanvasXSize) {
        // reset, start from beginning
        if (x > CanvasXSize) { x = -imgW + x; }
        // draw additional image1
        if (x > 0) { ctx.drawImage(img, -imgW + x, y, imgW, imgH); }
        // draw additional image2
        if (x - imgW > 0) { ctx.drawImage(img, -imgW * 2 + x, 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;
}

A continuació un <canvas> en què es desplaça la imatge. Hem de tenir en compte que l'amplada i l'alçada especificades aquí, han de coincidir amb els valors de les variables CanvasXZSize i CanvasYSize en el codi JavaScript.

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

Altres exemples

Una roda de raigs bàsica
Un bon exemple de com fer animacions usant els controls del teclat.
Animacions avançades
En el proper capítol veurem algunes tècniques avançades d'animació i física.

Document Tags and Contributors

 Contributors to this page: Legioinvicta
 Last updated by: Legioinvicta,