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

Anteriorment en aquest tutorial hem après sobre la graella de canvas i l'espai de coordenades. Fins ara, només usàvem la graella per defecte i canviàvem la grandària del llenç per a les nostres necessitats. Amb les transformacions, hi ha formes més poderoses de traslladar l'origen a una posició diferent, girar la graella i fins i tot escalar-la.

Guardar i restaurar l'estat

Abans de veure els mètodes de transformació, vegem altres dos mètodes que són indispensables una vegada que comencem a generar dibuixos cada vegada més complexos.

save()
Guarda tot l'estat del llenç.
restore()
Restaura l'estat del llenç guardat més recent.

Els estats del llenç s'emmagatzemen en una pila. Cada vegada que es crida al mètode save() l'estat del dibuix actual es mou a la pila. L'estat d'un dibuix consisteix de

Es pot cridar al mètode save() tantes vegades com es vulgui. Cada vegada que es crida al mètode restore() l'últim estat desat s'elimina de la pila i es restauren tots les configuracions guardades.

Un exemple de save i restore de l'estat del llenç

Aquest exemple intenta il·lustrar com funciona la pila d'estats del dibuix en dibuixar un conjunt de rectangles consecutius.

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

  ctx.fillRect(0, 0, 150, 150);   // Dibuixa un rectangle amb la configuració per defecte
  ctx.save();                  // Guarda el estat predeterminat
 
  ctx.fillStyle = '#09F';      // Es fan canvis a la configuració
  ctx.fillRect(15, 15, 120, 120); // Es dibuixa un rectangle amb la nova configuració

  ctx.save();                  // Es guarda el estat actual
  ctx.fillStyle = '#FFF';      // Es fan canvis a la configuració
  ctx.globalAlpha = 0.5; 
  ctx.fillRect(30, 30, 90, 90);   // Es dibuixa un rectangle amb la nova configuració

  ctx.restore();               // Es restaura el estat anterior
  ctx.fillRect(45, 45, 60, 60);   // Es dibuixa un rectangle amb la configuració restaurada

  ctx.restore();               // Es restaura el estat original
  ctx.fillRect(60, 60, 30, 30);   // Es dibuixa un rectangle amb la configuració restaurada

El primer pas és dibuixar un gran rectangle amb la configuració predeterminada.  A continuació guardem aquest estat i fem canvis en el color de farcit. Després dibuixem el segon rectangle blau i més petit i guardem l'estat. De nou canviem algunes configuracions de dibuix i dibuixem el tercer rectangle blanc semitransparent.

Fins ara, això és bastant similar al que hem fet en les seccions anteriors. No obstant això, una vegada que cridem a la primera instrucció restore() l'estat del dibuix superior s'elimina de la pila i es restaura la configuració. Si no haguéssim guardat l'estat usant save(), hauríem de canviar el color de farcit i la transparència manualment, per tornar a l'estat anterior. Això seria fàcil per a dues propietats, però si tenim més que això, el nostre codi es faria molt llarg, molt ràpid.

Quan es crida a la segona instrucció restore(), es restaura l'estat original (el que hem configurat abans de la primera crida save) i l'últim rectangle es dibuixa de nou en negre.

ScreenshotLive sample

Traslladar

El primer dels mètodes de transformació que veurem és translate(). Aquest mètode s'utilitza per moure el llenç i el seu origen a un punt diferent de la graella.

translate(x, y)
Mou el llenç i el seu origen en la graella. x indica la distància horitzontal a moure, i y indica quanta distància s'ha de moure la graella verticalment.

És una bona idea guardar l'estat del llenç abans de fer qualsevol transformació. En la majoria dels casos, és més fàcil cridar al mètode restore, que haver de fer una traslació inversa per tornar a l'estat original. També, si estem trasladant dins d'un bucle i no guardem i restaurem l'estat del llenç, pot ser que acabem perdent part del dibuix, ja que es va dibuixar fora de la vora del llenç.

Un exemple de translate

Aquest exemple demostra alguns dels beneficis de traslladar l'origen del llenç. Sense el mètode translate(), tots els rectangles es dibuixarien en la mateixa posició (0,0). El mètode translate(), també ens dóna la llibertat de col·locar el rectangle en qualsevol lloc del llenç, sense haver d'ajustar manualment les coordenades en la funció fillRect(). Això fa que sigui una mica més fàcil d'entendre i usar.

En la funció draw(), cridem a la funció fillRect() nou vegades, usant dos  bucles for. En cada bucle, el llenç es trasllada, es dibuixa el rectangle i el llenç torna al seu estat original. Observem com la crida a fillRect() usa les mateixes coordenades cada vegada, confiant en translate() per ajustar la posició del dibuix.

function draw() {
  var ctx = document.getElementById('canvas').getContext('2d');
  for (var i = 0; i < 3; i++) {
    for (var j = 0; j < 3; j++) {
      ctx.save();
      ctx.fillStyle = 'rgb(' + (51 * i) + ', ' + (255 - 51 * i) + ', 255)';
      ctx.translate(10 + j * 50, 10 + i * 50);
      ctx.fillRect(0, 0, 25, 25);
      ctx.restore();
    }
  }
}

ScreenshotLive sample

Girar

El segon mètode de transformació és rotate(). Se usa per girar el llenç al voltant de l'origen actual.

rotate(angle)
Gira el llenç en sentit horari, al voltant de l'origen actual pel nombre d'angle de radiants.

El punt central de gir és sempre l'origen del llenç. Per canviar el punt central, necessitarem moure el llenç usant el mètode translate().

Un exemple de rotate

En aquest exemple, usarem el mètode rotate() para girar primer un rectangle des de l'origen del llenç, i després des del centre del rectangle mateix amb l'ajuda de translate().

Recordatori: Els angles estan en radiants, no en graus. Per fer la conversació, estem usant: radians = (Math.PI/180)*degrees.

function draw() {
  var ctx = document.getElementById('canvas').getContext('2d');
  
  // rectangles esquerra, giren a partir d'origen del llenç 
  ctx.save();
  // rect blau
  ctx.fillStyle = '#0095DD';
  ctx.fillRect(30, 30, 100, 100); 
  ctx.rotate((Math.PI / 180) * 25);
  // grey rect
  ctx.fillStyle = '#4D4E53';
  ctx.fillRect(30, 30, 100, 100);
  ctx.restore();

  // rectangles drets, giren des del centre del rectangle 
  // dibuixa rect blau
  ctx.fillStyle = '#0095DD';
  ctx.fillRect(150, 30, 100, 100);  
  
  ctx.translate(200, 80); // trasllada al rectangle central 
                          // x = x + 0.5 * width
                          // y = y + 0.5 * height
  ctx.rotate((Math.PI / 180) * 25); // gira
  ctx.translate(-200, -80); // traslladar de nou
  
  // dibuixa rect gris
  ctx.fillStyle = '#4D4E53';
  ctx.fillRect(150, 30, 100, 100);
}

Per girar el rectangle al voltant del seu propi centre, traslladem el llenç al centre del rectangle, després girem el llenç i tornem a traslladar el llenç a 0,0, i a continuació dibuixem el rectangle.

ScreenshotLive sample

Escalar

El següent mètode de transformació és l'escalat. Ho usem per augmentar o disminuir les unitats en la graella del llenç. Això es pot utilitzar per dibuixar formes i mapes de bits reduïts o ampliats.

scale(x, y)
Escala les unitats del llenç, per x horitzontalment i per y verticalment. Tots dos paràmetres són nombres reals. Els valors inferiors a 1.0 redueixen la grandària de la unitat i els valors superiors a 1.0 augmenten la grandària de la unitat. Els valors a 1.0 deixen les unitats de la mateixa grandària.

Usant nombres negatius es pot fer la replica d'eixos (per exemple, usant translate(0,canvas.height); scale(1,-1); tindrem el conegut sistema de coordenades cartesianes, amb l'origen en la cantonada inferior esquerra).

Per defecte, una unitat en el llenç és exactament un píxel. Si apliquem, per exemple, un factor d'escala de 0.5, la unitat resultant es convertirà en 0.5 píxels i per tant les formes es dibuixaran a meitat de la seva grandària. De manera similar, si s'ajusta el factor d'escala a 2.0, la grandària de la unitat augmentarà i una unitat ara es converteix en dos píxels. Això dona com a resultat que les formes es dibuixin dues vegades més grans.

Un exemple de scale

En aquest últim exemple, dibuixarem formes amb diferents factors d'escala.

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

  // dibuixar un rectangle senzill, però escalar-lo.
  ctx.save();
  ctx.scale(10, 3);
  ctx.fillRect(1, 10, 10, 10);
  ctx.restore();

  // mirror horizontally
  ctx.scale(-1, 1);
  ctx.font = '48px serif';
  ctx.fillText('MDN', -135, 120);
}

ScreenshotLive sample

Transformar

Finalment, els següents mètodes de transformació permeten modificar directament la matriu de transformació.

transform(a, b, c, d, e, f)
Multiplica la matriu de transformació actual amb la matriu descrita pels seus arguments. La matriu de transformació és descrita per: [acebdf001]\left[ \begin{array}{ccc} a & c & e \\ b & d & f \\ 0 & 0 & 1 \end{array} \right]
Si qualsevol dels arguments és Infinity, la matriu de transformació ha de ser marcada com a infinita en lloc que el mètode llanci una excepció.

Els paràmetres d'aquesta funció són:

a (m11)
Escalat horitzontal.
b (m12)
Desviació Horizontal.
c (m21)
Desviació Vertical.
d (m22)
Escalat Vertical.
e (dx)
Moviment Horizontal.
f (dy)
Moviment Vertical.
setTransform(a, b, c, d, e, f)
Reinicia la transformació actual a la matriu d'identitat i, a continuació, invoca el mètode transform() amb els mateixos arguments. Això, bàsicament, desfà la transformació actual, i després estableix la transformació especificada, tot en un sol pas.
resetTransform()
Reinicia la transformació actual a la matriu d'identitat. Això és el mateix que cridar: ctx.setTransform(1, 0, 0, 1, 0, 0);

Exemple de transform i setTransform

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

  var sin = Math.sin(Math.PI / 6);
  var cos = Math.cos(Math.PI / 6);
  ctx.translate(100, 100);
  var c = 0;
  for (var i = 0; i <= 12; i++) {
    c = Math.floor(255 / 12 * i);
    ctx.fillStyle = 'rgb(' + c + ', ' + c + ', ' + c + ')';
    ctx.fillRect(0, 0, 100, 10);
    ctx.transform(cos, sin, -sin, cos, 0, 0);
  }
  
  ctx.setTransform(-1, 0, 0, 1, 100, 100);
  ctx.fillStyle = 'rgba(255, 128, 255, 0.5)';
  ctx.fillRect(0, 50, 100, 100);
}

ScreenshotLive sample

 
 

Document Tags and Contributors

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