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

Ara que hem configurat el nostre entorn canvas, podem entrar en els detalls de com dibuixar sobre el llenç. Al final d'aquest article, haureu après a dibuixar rectangles, triangles, línies, arcs i corbes, proporcionant-vos familiaritat amb algunes de les formes bàsiques. Treballar amb camins és essencial a l'hora de dibuixar objectes sobre el llenç i veurem com fer-ho.

La graella

Abans de poder començar a dibuixar, hem de parlar sobre la graella canvas o coordinar l'espai. El nostre esquelet HTML de la pàgina anterior tenia un element canvas amb 150 píxels d'amplada i 150 píxels d'alçada. A la dreta, veureu aquest llenç amb la graella predeterminada superposada. Normalment, una unitat de la graella correspon a 1 píxel sobre el llenç. L'origen d'aquesta graella es posiciona a la cantonada superior esquerra a la coordenada (0,0). Tots els elements es col·loquen en relació amb aquest origen. Per tant, la posició de la cantonada superior esquerra del quadrat blau es converteix en x pixels des de l'esquerra i y pixels des de la part superior, en la coordenada (x,y). Més endavant, en aquest tutorial veurem com podem traduir l'origen a una posició diferent, girar la graella i fins i tot escalar-la, però de moment ens quedarem amb el valor predeterminat.

Dibuixar rectangles

A diferència de SVG, <canvas> només suporta una forma primitiva: rectangles. Totes les altres formes han de crear-se combinant una o més trajectòries, llistes de punts connectats per línies. Afortunadament, tenim una gran varietat de funcions de dibuix de trajectòria que permeten compondre formes molt complexes.

Primer vegem el rectangle. Hi ha tres funcions que dibuixen rectangles en canvas:

fillRect(x, y, width, height)
Dibuixa un rectangle ple.
strokeRect(x, y, width, height)
Dibuixa un contorn rectangular.
clearRect(x, y, width, height)
Esborra l'àrea rectangular especificada, fent-la completament transparent

Cadascuna d'aquestes tres funcions pren els mateixos paràmetres. x i y indiquen la posició en el llenç (relativa a l'origen) de la cantonada superior esquerra del rectangle. width i height proporcionen la grandària del rectangle

A sota hi ha la funció draw() de la pàgina anterior, però ara fa ús d'aquestes tres funcions.

Exemple de forma rectangular

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

    ctx.fillRect(25, 25, 100, 100);
    ctx.clearRect(45, 45, 60, 60);
    ctx.strokeRect(50, 50, 50, 50);
  }
}

La sortida d'aquest exemple es mostra a continuació.

ScreenshotLive sample

La funció fillRect() dibuixa un quadrat negre de 100 píxels a cada costat. La funció clearRect() esborra un quadrat de 60x60 píxels del centre, i a continuació es crida a strokeRect() per crear un contorn rectangular de 50x50 píxels dins del quadrat buidat.

A les properes pàgines veurem dos mètodes alternatius per clearRect(), i també veurem com canviar l'estil de color i traç de les formes representades.

A diferència de les funcions de trajectòria, que veurem en la següent secció, les tres funcions rectangulars dibuixen immediatament al llenç.

Dibuixar trajectories

Les úniques altres formes primitives són les trajectòries. Una trajectòria és una llista de punts, connectats per segments de línies que poden ser de diferents formes, corbes o no, de diferent amplària i de diferent color. Un trajectòria, o fins i tot una subtrajecte, pot ser tancada. Per fer formes utilitzant trajectòries, es prenen alguns passos addicionals:

  1. Primer, crea la trajectòria.
  2. A continuació, utilitzar ordres de dibuix per dibuixar en la trajectòria.
  3. Després, tancar la trajectòria.
  4. Una vegada que s'ha creat la trajectòria, podeu traçar o omplir la trajectòria per representar-la

A continuació, es detallen les funcions que s'utilitzen per realitzar aquests passos:

beginPath()
Crea una trajectòria nova. Una vegada creada, les futures ordres de dibuix s'aplican a la trajectòria i són utilitzades per construir-la.
Mètodes de trajectòria
Mètodes per establir diferentes trajectòries d'objectes.
closePath()
Tanca la trajectòria perquè les ordres de dibuix futures tornin a dirigir-se al context.
stroke()
Dibuixa la forma traçant el seu contorn.
fill()
Dibuixa una forma sòlida emplenant l'àrea de contingut de la trajectòria.

El primer pas per crear una trajectòria és cridar a beginPath(). Internament, les trajectòries s'emmagatzemen com una llista de subtrayectes (línies, arcs, etc.) que formen una forma. Cada vegada que es crida a aquest mètode, la llista es reinicia i podem començar a dibuixar noves formes.

Nota: Quan la trajectòria actual està buida, com immediatament després de cridar beginPath(), o en un llenç recentment creat, la primera ordre de construcció de la trajectòria sempre es tractarà com moveTo(), independentment del que realment sigui. Per aquesta raó, gairebé sempre voldreu establir específicament la seva posició inicial després de restablir una trajectòria.

El segon pas és cridar als mètodes que realment especifiquen les trajectòries que s'han de dibuixar. Ho veurem en breu.

El tercer, i com a pas opcional, és cridar closePath(). Aquest mètode intenta tancar la forma, dibuixant una recta des del punt actual fins al començament. Si la forma ja s'ha tancat o només hi ha un punt a la llista, aquesta funció no fa res.

Nota: Quan crideu fill(), totes les formes obertes es tanquen automàticament, de manera que no heu de cridar closePath(). Aquest no és el cas quan es crida stroke().

Dibuixar un triangle

Per exemple, el codi per dibuixar un triangle es veuria així:

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

    ctx.beginPath();
    ctx.moveTo(75, 50);
    ctx.lineTo(100, 75);
    ctx.lineTo(100, 25);
    ctx.fill();
  }
}

El resultat és així:

ScreenshotLive sample

Moure el llapis

Una funció molt útil, que en realitat no dibuixa res però es converteix en part de la llista de trajectòries descrita anteriorment, és la funció moveTo(). Probablement el millor sigui pensar en això com aixecar un bolígraf o un llapis d'un lloc en un tros de paper i col·locar-ho en el següent.

moveTo(x, y)
Mou el llapis a les coordenades especificades per x i y.

Quan s'inicialitza el llenç o es crida beginPath() normalment voldreu utilitzar la funció moveTo() per col·locar el punt de partida en un altre lloc. També podrieu utilitzar moveTo() per dibuixar trajectòries no connectades. Feu un cop d'ull a la cara somrient d'a baix

Proveu-ho vosaltres mateixos, podeu utilitzar el fragment de codi que es mostra a continuació. Simplement enganxeu-lo a la funció draw() que vam veure anteriorment.

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

    ctx.beginPath();
    ctx.arc(75, 75, 50, 0, Math.PI * 2, true); // Outer circle
    ctx.moveTo(110, 75);
    ctx.arc(75, 75, 35, 0, Math.PI, false);  // Mouth (clockwise)
    ctx.moveTo(65, 65);
    ctx.arc(60, 65, 5, 0, Math.PI * 2, true);  // Left eye
    ctx.moveTo(95, 65);
    ctx.arc(90, 65, 5, 0, Math.PI * 2, true);  // Right eye
    ctx.stroke();
  }
}

El resultat és així:

ScreenshotLive sample

Si voleu veure les línies de connexió, podeu eliminar les línies que criden moveTo().

Nota: Per obtenir més informació sobre la funció arc(), vegeu Arcs a continuació.

Línies

Per dibuixar línies rectes, utilitzeu el mètode lineTo().

lineTo(x, y)
Dibuixa una línia des de la posició del dibuix actual fins a la posició especificada per x i y.

Aquest mètode pren dos arguments, x i y, que són les coordenades del punt final de la línia. El punt de partida depèn de les trajectòries prèviament traçades, on el punt final de la trajectòria anterior és el punt de partida de la següent, etc. El punt de partida també es pot canviar utilitzant el mètode moveTo().

L'exemple següent dibuixa dos triangles, un omplert i un altre delineat.

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

    // Filled triangle
    ctx.beginPath();
    ctx.moveTo(25, 25);
    ctx.lineTo(105, 25);
    ctx.lineTo(25, 105);
    ctx.fill();

    // Stroked triangle
    ctx.beginPath();
    ctx.moveTo(125, 125);
    ctx.lineTo(125, 45);
    ctx.lineTo(45, 125);
    ctx.closePath();
    ctx.stroke();
  }
}

Això comença cridant beginPath() per iniciar una nova trajectòria de forma. Seguidament, utilitzem el mètode moveTo() per moure el punt de partida a la posició desitjada. A continuació, es dibuixen dues línies que formen dues cares del triangle.

ScreenshotLive sample

Notareu la diferència entre el triangle emplenat i traçat. Això és, com es va esmentar anteriorment, perquè les formes es tanquen automàticament quan s'omple una trajectòria, però no quan es tracen. Si deixem de costat closePath() per al triangle traçat, només s'haurien dibuixat dues línies, no un triangle complet.

Arcs

Per dibuixar arcs o cercles, utilitzem els mètodes arc() o arcTo().

arc(x, y, radius, startAngle, endAngle, anticlockwise)
Dibuixa un arc que està centrat en la posició (x, y) amb un radi r que comença en startAngle i acaba en endAngle que va en la direcció donada, indicada per el sentit antihorari (predeterminat en sentit horari)
arcTo(x1, y1, x2, y2, radius)
Dibuixa un arc amb els punts de control i radi donats, connectats al punt anterior per una línia recta.

Vegem amb més detall el mètode arc, que pren sis paràmetres: x i y són les coordenades del centre del cercle en el qual s'ha de dibuixar l'arc. radius és autoexplicatiu. Els paràmetres startAngle i endAngle defineixen els punts inicial i final de l'arc en radiants, al llarg de la corba del cercle. Aquests es mesuren des de l'eix x. El paràmetre anticlockwise és un valor booleà, que quan és true, dibuixa l'arc en sentit contrari a les agulles del rellotge; en cas contrari, l'arc es dibuixa en sentit horari.

Nota: Els angles en la funció arc es mesuren en radiants, no en graus. Per convertir graus a radiants es pot utilitzar la següent expressió Javascript: radians = (Math.PI/180)*graus.

El següent exemple és una mica més complex que el que hem vist anteriorment. Dibuixa 12 arcs diferents, tots amb diferents angles i omplerts.

Els dos bucles (loops) for són per recórrer les files i columnes dels arcs. Per a cada arc, iniciem una nova trajectòria cridant beginPath(). En el codi, cadascun dels paràmetres de l'arc es troba en una variable per a major claredat, però no necessàriament ho fareu en la vida real.

Les coordenades x i y han de ser suficientment clares. radius i startAngle són fixes. endAngle comença a 180 graus (mig cercle) en la primera columna i s'incrementa per passos de 90 graus, culminant en un cercle complet en l'última columna

La instrucció per al paràmetre clockwise (sentit horari), resulti en la primera i la tercera fila que es dibuixin com a arcs en sentit horari i la segona i quarta fila com a arcs en sentit antihorari. Finalment, la sentència if fa que la meitat superior traci els arcs i la meitat inferior ompli els arcs.

Nota: Aquest exemple requereix un llenç una mica més gran que els altres, en aquesta pàgina: 150 x 200 píxels.

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

    for (var i = 0; i < 4; i++) {
      for (var j = 0; j < 3; j++) {
        ctx.beginPath();
        var x = 25 + j * 50; // x coordinate
        var y = 25 + i * 50; // y coordinate
        var radius = 20; // Arc radius
        var startAngle = 0; // Starting point on circle
        var endAngle = Math.PI + (Math.PI * j) / 2; // End point on circle
        var anticlockwise = i % 2 !== 0; // clockwise or anticlockwise

        ctx.arc(x, y, radius, startAngle, endAngle, anticlockwise);

        if (i > 1) {
          ctx.fill();
        } else {
          ctx.stroke();
        }
      }
    }
  }
}

ScreenshotLive sample

Corbes bézier i quadràtiques

El següent tipus de trajectòries disponibles són les corbes de Bézier, disponibles en varietats cúbiques i quadràtiques. Aquests s'utilitzen generalment per dibuixar formes orgàniques complexes.

quadraticCurveTo(cp1x, cp1y, x, y)
Dibuixa una corba Bézier quadràtica des de la posició actual del llapis fins al punt final especificat per x i y, utilitzant els punts de control especificats per cp1x i cp1y.
bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)
Dibuixa una corba Bézier cúbica des de la posició actual del llapis fins al punt final especificat per x i y, utilitzant els punts de control especificats per (cp1x, cp1y) i (cp2x, cp2y).

La millor manera de descriure la diferència entre aquestes dues opcions és usant la imatge de la dreta. Una corba Bézier quadràtica té un punt inicial i un punt final (punts blaus) i només un punt de control (indicat pel punt vermell), mentre que una corba Bézier cúbica utilitza dos punts de control.

Els paràmetres x i y de tots dos mètodes són les coordenades del punt final. cp1x i cp1y són les coordenades del primer punt de control, i cp2x i cp2y són les coordenades del segon punt de control.

L'ús de corbes de Bézier cúbiques i quadràtiques pot ser tot un repte, perquè a diferència del programari de dibuix vectorial com Adobe Illustrator, no tenim respostes visuals directes sobre el que estem fent. Això fa que sigui bastant difícil dibuixar formes complexes. En el següent exemple, dibuixarem algunes formes orgàniques senzilles, però si teniu el temps i, sobretot paciència, es poden crear formes molt més complexes.

No hi ha res molt difícil en aquests exemples. En tots dos casos veiem una successió de corbes dibuixades que finalment donen com a resultat una forma completa.

Corbes de Bezier quadràtiques

Aquest exemple usa múltiples corbes de Bézier quadràtiques per representar un globus de diàleg .

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

    // Quadratric curves example
    ctx.beginPath();
    ctx.moveTo(75, 25);
    ctx.quadraticCurveTo(25, 25, 25, 62.5);
    ctx.quadraticCurveTo(25, 100, 50, 100);
    ctx.quadraticCurveTo(50, 120, 30, 125);
    ctx.quadraticCurveTo(60, 120, 65, 100);
    ctx.quadraticCurveTo(125, 100, 125, 62.5);
    ctx.quadraticCurveTo(125, 25, 75, 25);
    ctx.stroke();
  }
}

ScreenshotLive sample

Corbes de Bezier cúbiques

Aquest exemple dibuixa un cor utilitzant corbes Bézier cúbiques.

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

    // Cubic curves example
    ctx.beginPath();
    ctx.moveTo(75, 40);
    ctx.bezierCurveTo(75, 37, 70, 25, 50, 25);
    ctx.bezierCurveTo(20, 25, 20, 62.5, 20, 62.5);
    ctx.bezierCurveTo(20, 80, 40, 102, 75, 120);
    ctx.bezierCurveTo(110, 102, 130, 80, 130, 62.5);
    ctx.bezierCurveTo(130, 62.5, 130, 25, 100, 25);
    ctx.bezierCurveTo(85, 25, 75, 37, 75, 40);
    ctx.fill();
  }
}

ScreenshotLive sample

Rectangles

A més dels tres mètodes que vam veure en Drawing rectangles, que dibuixen formes rectangulars directament al llenç, també està el mètode rect(), que afegeix una trajectòria rectangular a una trajectòria actualment oberta.

rect(x, y, width, height)
Dibuixa un rectangle a la cantonada superior esquerra especificada per (x, y) amb width i height especificats.

Quan s'executa aquest mètode, el mètode moveTo() es crida automàticament amb els paràmetres (0,0). En altres paraules, la posició actual del llapis es restableix automàticament a les coordenades predeterminades.

Fer combinacions

Fins ara, cada exemple d'aquesta pàgina només ha utilitzat un tipus de funció de trajectòria per forma. No obstant això, no hi ha limitació en el nombre o tipus de trajectòries que podeu utilitzar per crear una forma. Així, en aquest últim exemple, combinem totes les funcions de trajectòria per crear un conjunt de personatges de joc molt famosos.

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

    roundedRect(ctx, 12, 12, 150, 150, 15);
    roundedRect(ctx, 19, 19, 150, 150, 9);
    roundedRect(ctx, 53, 53, 49, 33, 10);
    roundedRect(ctx, 53, 119, 49, 16, 6);
    roundedRect(ctx, 135, 53, 49, 33, 10);
    roundedRect(ctx, 135, 119, 25, 49, 10);

    ctx.beginPath();
    ctx.arc(37, 37, 13, Math.PI / 7, -Math.PI / 7, false);
    ctx.lineTo(31, 37);
    ctx.fill();

    for (var i = 0; i < 8; i++) {
      ctx.fillRect(51 + i * 16, 35, 4, 4);
    }

    for (i = 0; i < 6; i++) {
      ctx.fillRect(115, 51 + i * 16, 4, 4);
    }

    for (i = 0; i < 8; i++) {
      ctx.fillRect(51 + i * 16, 99, 4, 4);
    }

    ctx.beginPath();
    ctx.moveTo(83, 116);
    ctx.lineTo(83, 102);
    ctx.bezierCurveTo(83, 94, 89, 88, 97, 88);
    ctx.bezierCurveTo(105, 88, 111, 94, 111, 102);
    ctx.lineTo(111, 116);
    ctx.lineTo(106.333, 111.333);
    ctx.lineTo(101.666, 116);
    ctx.lineTo(97, 111.333);
    ctx.lineTo(92.333, 116);
    ctx.lineTo(87.666, 111.333);
    ctx.lineTo(83, 116);
    ctx.fill();

    ctx.fillStyle = 'white';
    ctx.beginPath();
    ctx.moveTo(91, 96);
    ctx.bezierCurveTo(88, 96, 87, 99, 87, 101);
    ctx.bezierCurveTo(87, 103, 88, 106, 91, 106);
    ctx.bezierCurveTo(94, 106, 95, 103, 95, 101);
    ctx.bezierCurveTo(95, 99, 94, 96, 91, 96);
    ctx.moveTo(103, 96);
    ctx.bezierCurveTo(100, 96, 99, 99, 99, 101);
    ctx.bezierCurveTo(99, 103, 100, 106, 103, 106);
    ctx.bezierCurveTo(106, 106, 107, 103, 107, 101);
    ctx.bezierCurveTo(107, 99, 106, 96, 103, 96);
    ctx.fill();

    ctx.fillStyle = 'black';
    ctx.beginPath();
    ctx.arc(101, 102, 2, 0, Math.PI * 2, true);
    ctx.fill();

    ctx.beginPath();
    ctx.arc(89, 102, 2, 0, Math.PI * 2, true);
    ctx.fill();
  }
}

// A utility function to draw a rectangle with rounded corners.

function roundedRect(ctx, x, y, width, height, radius) {
  ctx.beginPath();
  ctx.moveTo(x, y + radius);
  ctx.lineTo(x, y + height - radius);
  ctx.arcTo(x, y + height, x + radius, y + height, radius);
  ctx.lineTo(x + width - radius, y + height);
  ctx.arcTo(x + width, y + height, x + width, y + height-radius, radius);
  ctx.lineTo(x + width, y + radius);
  ctx.arcTo(x + width, y, x + width - radius, y, radius);
  ctx.lineTo(x + radius, y);
  ctx.arcTo(x, y, x, y + radius, radius);
  ctx.stroke();
}

La imatge resultant és així:

ScreenshotLive sample

No anem a repassar això detalladament, ja que en realitat és sorprenentment senzill. La cosa més important a destacar és l'ús de la propietat fillStyle en el context del dibuix, i l'ús d'una funció d'utilitat (en aquest cas roundedRect()). L'ús de funcions d'utilitat per als bits del dibuix sovint pot ser molt útil i reduir la quantitat de codi que necessiteu, així com la seva complexitat.

Tornarem a fer un cop d'ull a fillStyle, en més detall, més endavant en aquest tutorial. Aquí, tot el que fem és usar-ho per canviar el color d'emplenament de les trajectòries del color predeterminat de negre a blanc, i viceversa .

Objectes Path2D

Com hem vist en l'últim exemple, pot haver-hi una sèrie de trajectories i ordres de dibuix per dibuixar objectes sobre el llenç. Per simplificar el codi i millorar el rendiment, l'objecte Path2D disponible en versions recents dels navegadors, permet emmagatzemar en la memòria cau o gravar aquestes ordres de dibuix. Podeu reproduir les vostres trajectòries ràpidament.
Vegem com podem construir un objecte Path2D:

Path2D()
El constructor Path2D() retorna un objecte Path2D recentment instanciat, opcionalment amb una altra trajectòria com a argument (crea una còpia), o opcionalment amb una cadena consistent en dades de trajectòria SVG.
new Path2D();     // empty path object
new Path2D(path); // copy from another Path2D object
new Path2D(d);    // path from SVG path data

Tots els mètodes de trajectòria com moveTo, rect, arc o quadraticCurveTo, etc., que hem conegut anteriorment, estan disponibles en els objectes Path2D.

L'API de Path2D també afegeix una forma de combinar trajectòries utilitzant el mètode addPath. Això pot ser útil quan es desitja construir objectes a partir de diversos components, per exemple.

Path2D.addPath(path [, transform])
Afegeix una trajectòria a la trajectòria actual amb una matriu de transformació opcional.

Exemple de Path2D

En aquest exemple, estem creant un rectangle i un cercle. Tots dos s'emmagatzemen com un objecte Path2D, de manera que estan disponibles per al seu ús posterior. Amb la nova API Path2D, es van actualitzar diversos mètodes per acceptar opcionalment un objecte Path2D que s'utilitzarà en comptes de la trajectòria actual. Aquí, stroke i fill s'utilitzen amb un argument de trajectòria per dibuixar tots dos objectes sobre el llenç, per exemple.

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

    var rectangle = new Path2D();
    rectangle.rect(10, 10, 50, 50);

    var circle = new Path2D();
    circle.moveTo(125, 35);
    circle.arc(100, 35, 25, 0, 2 * Math.PI);

    ctx.stroke(rectangle);
    ctx.fill(circle);
  }
}

ScreenshotLive sample

Ús de trajectòries SVG

Una altra característica poderosa de la nova API Path2D de canvas és l'ús de dades de trajectòria SVG per inicialitzar trajectòries en el seu llenç. Això podria permetre-li passar les dades de trajectòria i reutilitzar-les tant en SVG com en canvas.

La trajectòria es mourà al punt (M10 10) i després es mourà horitzontalment 80 punts a la dreta (h 80), després 80 punts cap avall (v 80), després 80 punts a l'esquerra (h -80), i després de tornada al principi (z). Podeu veure aquest exemple a la pàgina constructor Path2D.

var p = new Path2D('M10 10 h 80 v 80 h -80 Z');

Document Tags and Contributors

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