Apprendre comment créer un moteur d'affichage 2D en TypeScript

Pour réagir au contenu de ce tutoriel, un espace de dialogue vous est proposé sur le forum. 4 commentaires Donner une note  l'article (5)

Article lu   fois.

L'auteur

Profil Pro

Liens sociaux

Viadeo Twitter Facebook Share on Google+   

I. Barista - Les bases

On se retrouve aujourd’hui pour la grande première de Barista, le tout premier cycle du grand programme de formation. Cette série de tutoriels ne nécessite pas forcément de gros prérequis, elle demande cependant une certaine ténacité, car la difficulté peut augmenter très vite. C’est d’ailleurs pour cela que je lui donne la note de 3 cafés ! Pour celles et ceux qui dormaient au fond de la classe, voici un bref récapitulatif des faits :


Cliquez pour lire la vidéo


La première version de Barista, précédemment nommée Tomahawk, était écrite en JavaScript ES5, cela commence à dater ! Cette version apportera son lot de nouveautés, comme l’utilisation de Typescript, Typedoc, Jasmine et Webpack. Commençons déjà par l’installation de notre projet, j’ai détaillé toute la procédure en vidéo juste ici :


Cliquez pour lire la vidéo


I-A. Dessiner avec la balise canvas

La balise canvas est, comme son nom l’indique, une surface sur laquelle on peut dessiner, il s’agit d’un nouvel élément du DOM qui fait partie des nouveautés apportées par HTML5. Nous allons commencer par créer un fichier de base pour notre application sur la base du modèle suivant :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Barista</title>
    <script src="./dist/main.js"></script>
</head>
<body>
    <canvas id="barista"></canvas>
</body>
</html>
 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
/* Point d'entrée de l'application */
function init(){
    //code de notre Application
}
/* 
* Quand toutes les données sont chargées (DOM, images, sons, etc.)
* On démarre l'application par la fonction init
*/
window.addEventListener("load", init);

Le fichier index.html est un fichier HTML5 simple, les seuls éléments notables sont :

  • l’inclusion d’un fichier javascript ./dist.main.js ;
  • la présence d’une balise dont l’id est « barista ».

Nous allons pouvoir passer à la suite, c’est-à-dire créer un canvas et l’utiliser.

I-B. Créer un canvas

Créer un canvas peut se faire de deux façons :

  • soit de la manière la plus simple qui soit, c’est-à-dire, en ajoutant une balise au sein du DOM (dans le code HTML) ;
  • soit en la créant par programmation et en l’ajoutant manuellement au DOM de la façon suivante :
 
Sélectionnez
1.
2.
const canvas = document.createElement("canvas");
document.querySelector("body").appendChild(canvas);

Dans notre exemple, nous choisirons la première méthode, c’est-à-dire ajouter une balise au code HTML. Notre fichier index.html en contient déjà une, nous allons donc créer une fonction pour la retrouver.

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
/*
* Retourne une référence à l'objet canvas créé à l'aide de la balise 
* placée dans le code html
*/
function getCanvas(){
    return document.querySelector("canvas"); 
}

À l’aide de cette méthode, nous avons accès à l’objet canvas, maintenant il nous faut pouvoir dessiner à l’intérieur. Pour ce faire nous allons d’abord devoir récupérer le contexte de l’objet canvas.

L’élément canvas crée une surface pour dessiner à grandeur fixe. Cette surface expose un ou plusieurs contextes de rendu, qui sont utilisés pour créer et manipuler le contenu affiché. Ce tutoriel se concentrera sur le contexte de rendu 2D. D’autres contextes permettent d’autres types de rendu, tels que le contexte WebGL, qui utilise un contexte 3D (« experimental-webgl ») inspiré de OpenGL ES.

Initialement, le canvas est vide. Pour afficher quelque chose, un script doit commencer par accéder au contexte de rendu pour pouvoir dessiner dessus.

Pour de plus amples informations sur ce qu’est un contexte je vous invite à vous rendre sur le site du W3C. Le contexte de l’objet canvas se récupère à l’aide de la méthode getContext() la façon suivante :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
/*
* Retourne le contexte d'exécution 2d du canvas
*/
function getContext(){
    return getCanvas().getContext("2d");
}
/*
* Retourne une référence à l'objet canvas créé à l'aide de la balise 
* placée dans le code html
*/
function getCanvas(){
    return document.querySelector("canvas"); 
}

I-C. Dessiner des primitives


Cliquez pour lire la vidéo


L'objet canvas embarque toute une API dédiée au dessin, ce qui permet aux développeurs de créer du contenu 2D sans avoir systématiquement recours à des images. À l’aide de cette API, on peut dessiner n’importe quelle forme géométrique, voire des dessins plus complexes, la seule vraie limite est votre imagination.

I-C-1. Dessiner une ligne

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
/*
* Retourne le contexte d'exécution 2d du canvas
*/
function getContext(){
    return getCanvas().getContext("2d");
}
/*
* Retourne une référence à l'objet canvas créé à l'aide de la balise 
* placée dans le code html
*/
function getCanvas(){
    return document.querySelector("canvas"); 
}

/* Point d'entrée de l'application */
function init(){
    const canvas = getCanvas();
    const context  = getContext(); 

    // on détermine les dimensions du canvas
    canvas.width = 640;
    canvas.height = 480;

    // crée un nouveau path 
    context.beginPath(); 

    // on définit l'épaisseur de la ligne (unité en px)
    context.lineWidth = 5; 

    // on bouge vers les coordonnées x = 10 et y = 10 (sans rien dessiner)
    context.moveTo(10,10); 

    // on dessine une ligne depuis notre point de départ vers le point d'arrivée (x=10,y100)
    context.lineTo(10,100); 

    // on définit la couleur de la ligne, ici c'est du vert.
    context.strokeStyle = "#00ff00"

    // et enfin on appelle la méthode stroke, qui va se charger d'exécuter l'ensemble 
    // des commandes que nous avons utilisées depuis que l'on a invoqué beinPath()
    // si l'on commente l'appel à cette méthode, rien ne se produit. 
    // L'appel à stroke permet de fermer le chemin créé par beginPath().
    context.stroke();
}
/* 
* Quand toutes les données sont chargées (DOM, images, sons, etc.)
* On démarre l'application par la fonction init
*/
window.addEventListener("load", init);

Avant toute opération de dessin en HTML5, il nous faudra commencer par un beginPath() qui permet, comme son nom l’indique, de commencer un « chemin », comprendre par là que l’on initialise un nouveau cycle de dessin, un peu comme si l’on prenait une nouvelle feuille vierge pour dessiner. Faisons le point de ce que nous avons actuellement sur notre canvas, une ligne dont les propriétés sont :

  • une épaisseur de 5px définie par la propriété lineWidth = 5 ;
  • une couleur définie par la propriété strokeStyle = ‘#003300’ ;
  • un point de départ situé à x = 10px et y = 10px que nous avons défini avec l’appel à la fonction moveTo ;
  • un point d’arrivée situé à x = 10px et y = 100px que nous avons relié au point de départ en faisant appel à la fonction lineTo, qui relie le dernier point dessiné au point dont les coordonnées sont passées en paramètres.

Vous pouvez remarquer que la dernière ligne de notre code se termine par context.stroke(), cette méthode permet de dessiner l’ensemble du jeu d’instructions défini entre l’appel à context.beginPath() et context.stroke(), si vous commentez l’appel à cette méthode, rien ne sera dessiné.

Notez également que context.stroke() n’exécute que les jeux d’instructions relatifs aux lignes et pas aux formes pleines, ces dernières sont gérées de manière différente, ce qui nous amène à la suite, dessiner des primitives « pleines », en commençant par le cercle.

I-C-2. Dessiner un cercle

Si je souhaite dessiner un cercle, je peux utiliser la méthode suivante :

 
Sélectionnez
1.
context.arc(x, y, radius, startAngle, endAngle, counterClockwise);

où x et y représentent les coordonnées du centre de mon arc, radius le rayon (en pixels) de mon arc, startAngle et endAngle les angles de départ et d’arrivée (en radians) et counterClockwise un booléen qui sert à définir si l’arc est défini dans le sens antihoraire ou non. Étudions à présent le code suivant :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
/*
* Retourne le contexte d'exécution 2d du canvas
*/
function getContext() {
    return getCanvas().getContext("2d");
}
/*
* Retourne une référence à l'objet canvas créé à l'aide de la balise 
* placée dans le code html
*/
function getCanvas() {
    return document.querySelector("canvas");
}

function init() {
    const canvas = getCanvas();
    const context = getContext();

    // on détermine les dimensions du canvas
    canvas.width = 640;
    canvas.height = 480;

    // crée un nouveau path 
    context.beginPath();
    // on va donc définir un style de remplissage
    context.fillStyle = "#f00f00";
    // on bouge notre premier point de chemin vers le centre du cercle
    context.moveTo(320, 240);
    // on dessine le cercle
    context.arc(320, 240, 50, 0, 360 * Math.PI / 180, false);
    //remplir le cercle
    context.fill();
}


window.addEventListener("load", init);

Comme pour la ligne, faisons le point de ce que nous avons actuellement sur notre canvas.

Un cercle dont les propriétés sont :

  • une couleur de remplissage définie par la propriété fillStyle ;
  • le centre de départ situé à x = 320px et y = 240px ;
  • un rayon de 50px ;
  • un angle de départ situé à 0 degré et converti en radians ;
  • un angle d’arrivée situé à 360 degrés et converti en radians ;
  • une direction dans le sens horaire.

Comme vous pouvez le constater, nous utilisons toujours la méthode context.beginPath() pour créer un nouveau dessin. Afin de pouvoir exécuter le nouveau jeu d’instructions, relatif cette fois-ci à des formes pleines, nous utilisons la méthode context.fill() qui agit de la même façon que la méthode context.stroke().

En regardant un peu plus en avant l’API de dessin, on peut s’apercevoir qu’il existe pas mal de méthodes pour dessiner d’autres primitives, ou d’autres types de lignes, qu’elles soient droites ou dessinées à l’aide de courbes de Bézier, etc. On va maintenant apprendre à dessiner un rectangle.

I-C-3. Dessiner un rectangle

Si je souhaite dessiner un rectangle, je peux utiliser la méthode suivante :

 
Sélectionnez
1.
context.fillRect(x, y, width, height);

où x et y représentent les coordonnées du coin en haut à gauche de mon rectangle et width et height représentent la largeur et la hauteur du rectangle que je souhaite dessiner.

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
/*
* Retourne le contexte d'exécution 2d du canvas
*/
function getContext() {
    return getCanvas().getContext("2d");
}
/*
* Retourne une référence à l'objet canvas créé à l'aide de la balise 
* placée dans le code html
*/
function getCanvas() {
    return document.querySelector("canvas");
}

function init() {
    const canvas = getCanvas();
    const context = getContext();

    // on détermine les dimensions du canvas
    canvas.width = 640;
    canvas.height = 480;

    // crée un nouveau path 
    context.beginPath();
    // on va donc définir un style de remplissage
    context.fillStyle = "#ff0000";
    //dessiner un rectangle
    context.fillRect(320,240,100,30);
    //remplir le rectangle
    context.fill();
}


window.addEventListener("load", init);

Maintenant que nous savons comment dessiner des primitives, nous allons apprendre à les transformer, comprendre par là que nous allons leur appliquer un changement d’échelle, de rotation, d’alpha ou de translation.

I-D. Appliquer des transformations


Cliquez pour lire la vidéo


Appliquer une transformation en HTML5 est facile, en effet l’API met à notre disposition des méthodes simples, la seule difficulté réside dans le fait que ces méthodes sont cumulatives, mais nous reviendrons là-dessus plus tard, pour l’instant nous allons nous contenter d’appliquer des transformations à un rectangle.

I-D-1. L’alpha

Pour dessiner quelque chose en transparence, on modifie la propriété :

 
Sélectionnez
1.
2.
// pour définir la transparence à 50 %
context.globalAlpha = 0.5;

La valeur de la propriété globalAlpha du contexte se situe toujours entre 0 et 1, si, comme dans l’exemple ci-dessus, vous souhaitez obtenir un alpha de 50 %, il vous suffit de modifier la valeur de cette propriété à 0.5. Facile n’est-ce pas ? Par exemple si je veux dessiner un rectangle avec une transparence de 50 % mon code ressemblera à ceci :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
function init() {
    const canvas = getCanvas();
    const context = getContext();

    // on détermine les dimensions du canvas
    canvas.width = 640;
    canvas.height = 480;

    // on redéfinit l'opacité des dessins qui vont suivre
    context.globalAlpha = 0.5;
    // crée un nouveau path 
    context.beginPath();
    // on va donc définir un style de remplissage
    context.fillStyle = "#f00f00";
    //dessiner un rectangle
    context.fillRect(320,240,100,30);
    //remplir le rectangle
    context.fill();
}

On aperçoit bien le rectangle en transparence, essayez de jouer un peu avec les valeurs de l’alpha, c’est vraiment facile ! Vous avez sans doute remarqué que jusqu’ici nous avons défini les coordonnées de nos primitives à l’aide des paramètres fournis à cet effet, toutefois lors de vos futurs développements, vous verrez qu’il n’est pas forcément pratique de procéder de la sorte. Le mieux serait de pouvoir dessiner nos objets aux coordonnées 0, 0 et de les déplacer ensuite. Ça tombe plutôt bien, la prochaine transformation que je compte vous montrer est la translation.

I-D-2. La translation

Pour effectuer une translation, on utilise la méthode :

 
Sélectionnez
1.
context.translate( translateX, translateY );

translateX est le déplacement sur l’axe des x (en pixels) que vous souhaitez obtenir et translateY la même chose, mais sur l’axe des y. Ainsi, pour dessiner un carré rouge de 100 pixels de côté prenant son point d’origine aux coordonnées x = 47 et y = 72, j’aurai à écrire le code suivant :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
function init() {
    const canvas = getCanvas();
    const context = getContext();

    // on détermine les dimensions du canvas
    canvas.width = 640;
    canvas.height = 480;

    // on déplace le point de départ des futurs dessins
    context.translate( 47, 72 );
    // crée un nouveau path 
    context.beginPath();
    // on va donc définir un style de remplissage
    context.fillStyle = "red";
    //dessiner un rectangle
    context.fillRect(0,0,100,100);
    //remplir le rectangle
    context.fill();
}

Notez que nous aurions tout aussi bien pu utiliser les deux premiers paramètres de la méthode fillRect (ce que nous faisions jusqu’ici), toutefois comme expliqué plus haut, il vous sera plus utile d’utiliser les méthodes de transformations par la suite plutôt que d’utiliser ce type de paramètre. Passons maintenant au changement d’échelle.

I-D-3. Le scale

Pour effectuer un changement d’échelle, on utilise la méthode :

 
Sélectionnez
1.
context.scale( scaleX, scaleY);

scaleX est l’échelle sur l’axe des x que vous souhaitez obtenir et scaleY la même chose, mais sur l’axe des y. Ainsi, pour dessiner le même carré que dans l’exemple précédent, mais à une échelle deux fois plus grande, nous aurons le code suivant :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
function init() {
    const canvas = getCanvas();
    const context = getContext();

    // on détermine les dimensions du canvas
    canvas.width = 640;
    canvas.height = 480;

    // on déplace le point de départ des futurs dessins
    context.translate( 47, 72 );
    // échelle 2 fois plus grande sur l'axe des x et des y
    context.scale( 2, 2 );
    // crée un nouveau path 
    context.beginPath();
    // on va donc définir un style de remplissage
    context.fillStyle = "red";
    //dessiner un rectangle
    context.fillRect(0,0,100,100);
    //remplir le rectangle
    context.fill();
}

Ainsi, nous obtenons un carré de 100 pixels de côté, mais dont l’échelle est de 2, et visuellement, j’ai un carré de 200 pixels de côté. Quel est l’intérêt de cette méthode ? Pourquoi ne pas directement dessiner un carré de 200 pixels de côté ? En plus on code moins !

Et bien l’intérêt principal est de ne pas avoir à recalculer la largeur et la hauteur d’un objet d’affichage à chaque fois que l’on souhaite changer son échelle, de plus, ces calculs sont simples à réaliser lorsque l’objet en question n’est pas en rotation, mais dès qu’il s’agit de calculer une largeur et une hauteur avec une rotation par-dessus le marché, ça devient plus compliqué et plus coûteux en ressources. Passons maintenant à la dernière transformation, la rotation.

I-D-4. La rotation

Avant de commencer, il me faut éclaircir un point que nous avons omis de préciser jusque là : l’unité de mesure employée pour une rotation. En effet, alors que la plupart des gens calculent leurs angles en degrés, en programmation graphique il est de coutume d’employer le radian. La formule de conversion degrés/radians est la suivante :

angle_radians = angle_degré * ( Math.PI / 180 );

Nous l’avons déjà utilisé plus haut pour définir les angles de départ et de fin de notre arc. Maintenant, nous savons que lorsqu’on parlera d’un angle, on s’exprimera par défaut en radians et si l’on change d’unité de mesure, je vous le préciserai alors. Bien, maintenant que tout le monde parle le même langage, laissez-moi vous présenter la méthode qui vous permettra d’appliquer une rotation à vos objets :

context.rotate( angle_radian );

Assez simple n’est-ce pas ? Nous allons reprendre le code de tout à l’heure et ajouter une rotation de 37° à notre carré :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
function init() {
    const canvas = getCanvas();
    const context = getContext();

    // on détermine les dimensions du canvas
    canvas.width = 640;
    canvas.height = 480;

    // on déplace le point de départ des futurs dessins
    context.translate( 47, 72 );
    // échelle 2 fois plus grande sur l'axe des x et des y
    context.scale( 2, 2 );
    // on applique une rotation de 37 degrés convertis en radians
    context.rotate( 37 * Math.PI / 180 );
    // crée un nouveau path 
    context.beginPath();
    // on va donc définir un style de remplissage
    context.fillStyle = "red";
    //dessiner un rectangle
    context.fillRect(0,0,100,100);
    //remplir le rectangle
    context.fill();
}

Notez que toutes les rotations s’effectuent dans le sens horaire ! Nous avons vu les transformations que nous voulions voir, nous y reviendrons plus tard. Il nous reste à voir le cumul des transformations, la sauvegarde et restauration du contexte et nous aurons terminé ce premier chapitre.

I-E. Cumul , save et restore

L’objet context utilise une matrice pour représenter et stocker le résultat de toutes les transformations qu’on lui applique. Nous ne nous étendrons pas pour l’instant sur ce qu’est une matrice ni comment l’utiliser, en revanche, sachez qu’une des lois basiques des calculs matriciels est la non-commutativité. En clair, cela signifie que les transformations que l’on applique à une matrice se cumulent et que l’ordre dans lequel on les exécute influe sur le résultat obtenu.

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
function init() {
    const canvas = getCanvas();
    const context = getContext();

    //on détermine les dimensions du canvas
    canvas.width = 640;
    canvas.height = 480;

    //ici on applique une translation AVANT le scale
    context.translate(47, 72);
    context.scale(2, 2);

    context.beginPath();
    context.fillStyle = "red";
    context.fillRect(0, 0, 100, 100);
    context.fill();

    //ici on applique une translation APRÈS le scale
    context.scale(2, 2);
    context.translate(47, 72);

    context.beginPath();
    context.fillStyle = "red";
    context.fillRect(0,0,100,100);
    context.fill();
}

On peut voir que le résultat obtenu à l’écran est différent suivant que l’on applique scale avant ou après la translation, tout cela est normal. Mais alors, dans quel ordre appliquer mes transformations ? Et bien ça, c’est à vous de le décider, bien qu’en règle générale le résultat attendu nécessite que l’on applique dans l’ordre une translation, une rotation et enfin l’échelle.

Et vous pensiez que c’était fini ? Eh bien non, en effet les transformations en html5 c’est pas de la tarte (enfin uniquement quand on n'y est pas habitué, après je vous rassure ça roule tout seul). Si je veux par exemple définir une échelle à 0,5 après avoir l’avoir définie à 2, le code suivant ne fonctionne pas :

 
Sélectionnez
1.
2.
3.
4.
5.
// l'échelle est à 2
context.scale(2,2);

// l'échelle vaut 1 car 2 * 0.5 = 1
context.scale(0.5, 0.5);

Le problème, c’est que je ne suis pas forcément au courant de l’état actuel de ma matrice au moment où je l’utilise, donc cela peut me poser pas mal de problèmes pour obtenir l’état désiré. Heureusement, il existe une parade à cela : la sauvegarde du contexte ! En effet, il est possible de stocker en mémoire l’état du contexte et de le restaurer par la suite. Cela fonctionne avec la paire de méthodes suivantes :

 
Sélectionnez
1.
2.
context.save()
context.restore()

La méthode, context.save() permet de sauvegarder l’état actuel du contexte, la méthode context.restore() permet quant à elle de restituer l’état du dernier contexte sauvegardé, c’est-à-dire que les données de transformations de la matrice ainsi que les valeurs des attributs du contexte (comme strokeStyle, fillStyle, globalAlpha, etc.).

C’est-à-dire que les données de transformations de la matrice ainsi que les données de dessins, etc. seront exactement les mêmes que lors du dernier appel à context.save(). Ces méthodes fonctionnent un peu à la manière d’une pile d’assiettes, c’est-à-dire que le dernier contexte sauvegardé ira « au-dessus » de la pile et donc, lors du prochain appel à context.restore() ce sera cette dernière « assiette » qui sera restituée.

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
function init(){
    const canvas = getCanvas();
    const context  = getContext(); 

    // on détermine les dimensions du canvas
    canvas.width = 640;
    canvas.height = 480;

    // sauvegarder le contexte = t1
    context.save();

    context.scale(0.5,0.5); 
    context.translate(10,10);
    // on applique une rotation de 45° au repère
    context.rotate(45 * Math.PI / 180);

    // sauvegarde le contexte tel qu'il est en t2
    context.save();

    // crée un nouveau "path"
    context.beginPath(); 
    // on va donc définir un style de remplissage
    context.fillStyle = "blue";
    // dessiner un carré
    context.fillRect(300,220, 100, 100);
    // remplir le carré 
    context.fill();

    // on restaure le dernier contexte sauvegardé avec save();
    context.restore();

    // on redéfinit l'opacité des dessins qui vont suivre 
    context.globalAlpha = 0.5;

    // crée un nouveau "path"
    context.beginPath(); 
    // on va donc définir un style de remplissage
    context.fillStyle = "#ff0000";
    // dessiner un rectangle
    context.fillRect(320,240, 100, 30);
    // remplir le rectangle 
    context.fill();
}

function getContext(){
    return getCanvas().getContext("2d");
}

function getCanvas(){
    return document.querySelector("canvas"); 
}

window.addEventListener("load", init);

Voilà, les bases des transformations et du dessin avec canvas sont posées, nous venons de clôturer ce premier chapitre.

Voir le code : Github

Afin de vous entraîner, je vous ai concocté deux exercices à l’adresse suivante : https://github.com/thetinyspark/barista/tree/barista/exercices-kirby-and-curves. Au programme, de la visualisation de données et Kirby, have fun!

Exercice 1 : dessiner un kirby avec l’API Canvas

Cet exercice est plutôt simple, tu as juste à dessiner un « Kirby » en utilisant les appels à l'API canvas. Plus précisément, ceux que tu as appris dans les épisodes 2 et 3 sur la chaine YouTube moocaccino. Pour ceux qui ne le savent pas, Kirby est un alien rose très mignon créé par Nintendo, ton résultat doit ressembler à cela :

Image non disponible

Exercice 2 : Tracer une courbe qui représente la variation au cours du temps

Cet exercice est plutôt orienté data visualization, tu dois dessiner une courbe qui représente les données suivantes à travers le temps.

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
const data = [
    {amount: 0, time: 0},
    {amount: 10, time: 5},
    {amount: 12, time: 10},
    {amount: 5, time: 15},
    {amount: 7, time: 20},
    {amount: 20, time: 25},
    {amount: 10, time: 30},
    {amount: 0, time: 35}
];

La solution pour l'exercice de la courbe est dans le fichier index.ts, mais essaie d'abord par toi-même !

 
Cacher/Afficher le codeSélectionnez

II. Remerciements Developpez.com

Nous tenons à remercier Nicolas Legrand qui nous a aimablement autorisés à publier son tutoriel. Nos remerciements également à Yahiko pour la relecture technique, Malick pour la mise au gabarit et Claude Leloup pour la relecture orthographique.

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

En complément sur Developpez.com

  

Copyright © 2020 Nicolas Legrand. Aucune reproduction, même partielle, ne peut être faite de ce site ni de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.