Accueil Nos publications Blog Mon image est un script ! Introduction aux Canvas de HTML5

Mon image est un script ! Introduction aux Canvas de HTML5

Gribouilli rouge

Tu salives de désir devant les animations de la page d’accueil Google ? Tu t’émerveilles du rendu des animations Flash ? Tu aimes gribouiller, programmer, et voudrais faire les deux à la fois ?

Ami lecteur je ne t’apprendrai pas à faire le prochain Google Doodlemais je te propose de te guider dans l’apprentissage des Canvas, la fonctionnalité de dessin en deux dimensions de HTML5 !

Canvas ? Qu’est-ce que c’est ?

Un canvas, une toile en français, est un nouvel élément HTML qui te permet de dessiner directement dans ton navigateur, à l’aide de Javascript. Les toiles sont dessinées pixel après pixel ou à l’aide de formes simples, et permettent la conception d’animations interactives et de jeux.

Le dessin va très vite et les API (les interfaces de programmation) offertes par Javascript sont très performantes. Canvas se place donc en concurrent sérieux de Flash ou de Java. Comme tu as pu le comprendre, Google se sert des Canvas pour réaliser ses animations d’accueil, et disons-le, le résultat est excellent !

Mini-page histoire, Canvas fut inventé par Apple pour Safari et Dashboard. C’est le WhatWG (Web Hypertext Application Technology Working Group) qui a décidé de l’intégrer à HTML5. Canvas n’est donc pas issu des réflexions des groupes de travail HTML.

Nous allons découvrir cette fonctionnalité par la pratique. Je te propose de dessiner un logo Soat plus beau encore que l’original, à l’aide de HTML5 ! (La beauté est une notion suggestive…)

Ma première toile

Une once de HTML

Le code HTML est bref. Chaque toile est représentée par un élément canvas.


<canvas id="logo_soat">
   Texte alternatif. Votre navigateur ne supporte pas le dessin 2D avec HTML5...
</canvas>

Avec le moteur de rendu WebKit (navigateurs Chrome et Safari) l’élément canvas fonctionne comme l’élément img. La balise pourrait donc être orpheline. Cependant si l’on souhaite renseigner un contenu alternatif comme ici, il faut une balise fermante. (Le contenu alternatif sera affiché sur les navigateurs ne supportant pas la fonctionnalité Canvas.)

Les dimensions de la toile sont des informations indispensables au navigateur. Si vous n’en mettez pas les dimensions par défaut seront adoptées. Elles peuvent être placées directement dans la balise canvas, ou dans le code Javascript (cf. partie suivante).

C’est tout pour le HTML ! Une toile, une balise.

Et beaucoup de Javascript

Tout le travail est réalisé en Javascript. Récupérons notre élément canvas :


var canvas = document.getElementById("logo_soat");

if(null == canvas)
   throw "Impossible de trouver la toile sur la page.";

if(null == canvas.getContext)
   throw "Votre navigateur ne permet pas le dessin avec HTML5";

De la même manière que nous assurons un contenu alternatif pour les anciens navigateurs, nous nous assurons de pouvoir travailler notre toile. Si ce n’est pas le cas nous terminons proprement le script sans faire de bruit.

Ensuite il est important de fixer les dimensions de la toile :


   canvas.width = 600;
   canvas.height = 300;

Les dimensions sont exprimées en pixels. Notez bien que si vous précisez des dimensions dans les styles (sur votre feuille CSS par exemple), elles n’affecteront que le rendu de votre toile. Tout le dessin se fera sur un repère de 600 pixels de larges, par 300 pixels de haut, quoi qu’il advienne.

Des propriétés CSS width et height valant respectivement 300 et 150 pixels ne feraient que réduire par quatre la taille de l’image affichée à l’écran.

Quelques variables pour mémoriser de superbes codes couleurs. On fait dans le sexy, oui monsieur.


   var sogray = "#C2C5C7"; //Soat's gray
   var sogreen = "#8AAB1E"; //Soat's green
   var soorange = "#E65C00"; //Soat's orange

Et c’est maintenant que tout commence ! Le dessin n’est pas fait directement dans la fenêtre du navigateur. Il est réalisé au moyen d’une interface Javascript, un contexte deux dimensions que nous obtenons par un simple appel à la méthode getContext() :


   var ctx = canvas.getContext('2d');

Le cadre vert

Traçons un premier élément, un cadre vert :


/* Des styles globaux */
ctx.strokeStyle = sogreen;
ctx.lineWidth = 5;

/* Un rectangle */
ctx.strokeRect(10, 10, 580, 280);

Le cadre est tracé par la méthode strokeRect() qui sert à dessiner des rectangles creux, où seules les bordures sont visibles. Seules les bordures sont visibles dans la mesure où vous avez donné des consignes pour qu’elles le soient. En effet strokeRect() n’attend aucun argument correspondant à la couleur du trait ni à son épaisseur. Ces propriétés sont globales et doivent être définies avant.

Jusqu’à ce que vous en décidiez autrement les traits seront donc de cinq pixels d’épaisseur, et seront vert.

La méthode strokeRect() attend quatre paramètres, les deux coordonnées de départ (abscisse puis ordonnée) puis les deux dimensions du rectangle (largeur puis hauteur).

N’oublie pas, toutes ces valeurs sont exprimées en pixel, et l’origine du repère (le point de coordonnées 0,0) est située dans le coin supérieur gauche de la toile.

La méthode strokeRect() est une des quelques méthodes permettant de tracer directement des formes géométriques simples. Elle a deux homologues : fillRect(), qui trace un rectangle plein, et clearRect(), qui trace un rectangle vide. Leurs paramètres sont identiques.

Si l’on souhaite tracer des formes singulières il est possible de le faire avec les chemins. Un chemin est une forme définie point après point. Finalement les méthodes de dessin des rectangles sont des raccourcis, nous pourrions les dessiner avec des chemins et des lignes droites, comme ceci :



ctx.beginPath();
ctx.moveTo(10, 10);
ctx.lineTo(580, 10);  /* Bordure supérieure */
ctx.lineTo(580, 280); /* Bordure de droite */
ctx.lineTo(10, 280);  /* Bordure inférieure */
ctx.lineTo(10, 10);   /* Bordure de gauche */
ctx.stroke();

Les courbes

Traçons maintenant, à l’aide d’un chemin, la courbe grise du logo de Soat.


ctx.beginPath();
ctx.moveTo(canvas.width / 2 - 80, canvas.height / 2 + 40);

/* Des styles globaux */
ctx.lineWidth = 8;
ctx.lineCap = "round"; //Extrémités de lignes arrondies
ctx.strokeStyle = sogray;
/* Une courbe */
ctx.bezierCurveTo(
   canvas.width / 2 - 15, canvas.height / 2 + 30,
   canvas.width / 2, canvas.height / 2,
canvas.width / 2, canvas.height / 2 - 50
);
ctx.stroke();

Un chemin est ouvert par la méthode beginPath(). Ensuite nous pouvons enchaîner autant de tracés que voulu. La méthode closePath() terminera le chemin en nous ramenant au point de départ, nous permettant de concevoir facilement des motifs fermés. Dans notre cas et puisque les deux courbes du logo Soat ne sont pas des formes fermées, nous n’appellerons pas closePath().

Le code est simple. D’abord nous nous plaçons au point de départ souhaité de la courbe grise, grâce à la méthode moveTo() qui attend les coordonnées voulues en paramètres. S’ensuivent quelques paramétrages globaux, puis le tracé d’une courbe de Bézier. Enfin un appel à stroke() provoque le tracé.
Alors pourquoi une courbe de Bézier ? Pour que ça en jette un peu ! Si vous dites « J’ai dessiné deux arcs de cercle » vous ne récolterez au mieux que des haussements de sourcils moqueurs, tandis que Bézier, voilà, bref…
En réalité les deux courbes du logo original ne sont pas des arcs de cercle. Je pense que c’est avec deux courbes de Bézier qu’on peut s’en approcher le plus fidèlement.

Il eut été pertinent que j’écrivisse ici un petit laïus sur les propriétés et le fonctionnement des courbes de Bézier, mais mes compétences en mathématiques ne me le permettent guère, aussi il faudra vous satisfaire d’un lien : Courbe de Bézier.

Dans l’idée la courbe de Bézier est tracée du point de départ courant (le point où nous nous situons lors de l’appel à bezierCurveTo()) au point d’arrivée indiqué par les deux derniers paramètres. Les quatres premiers paramètres sont les coordonnées de deux autres points intermédiaires qui « attirent » la courbe à eux.

N’étant pas un graphiste né j’ai décidé de me fixer sur le centre de la toile pour mon dessin. Tous les appels à canvas.width / 2 et canvas.height / 2 m’y ramènent donc. Les coordonnées des points utilisés pour les tracés de courbes ont tous été déterminés de manière empirique.

La deuxième courbe, noire, est dessinée par un code quasiment identique :


   ctx.beginPath();
   ctx.moveTo(canvas.width / 2 - 70, canvas.height / 2 + 50);
   ctx.strokeStyle = "black";
   ctx.lineWidth = 5;
   ctx.bezierCurveTo(
   canvas.width / 2 - 10, canvas.height / 2 + 30,
   canvas.width / 2 + 30, canvas.height / 2 + 25,
   canvas.width / 2 + 80, canvas.height / 2 + 50
   );

   ctx.stroke();

Le texte So@t

Il ne nous reste plus qu’à tracer le texte « So@t ». Pour faciliter le découpage nous écrirons les deux premières lettres en noir avant le centre de la toile, et les deux dernières après. Ainsi la séparation est aisée et nous n’avons pas besoin de calculer des positions ou les dimensions que prennent ces deux groupes de lettres.

Il est possible d’obtenir la largeur en pixel d’un texte en utilisant la méthode measureText(“So@t”), puis en récupérant l’attribut width du retour de la fonction.


/* Le texte sera centré verticalement sur le point de départ. */
ctx.textBaseline = "middle";
ctx.font = "40pt Arial";
ctx.fillStyle = "black";
/* Les lettres "So" seront dessinées avant le point central de la toile. */
ctx.textAlign = "end";
ctx.fillText("So", canvas.width / 2, canvas.height / 2);

La méthode fillText() trace sur notre toile un texte plein. Une variante appelée strokeText() permet de n’afficher que le contour des caractères. Les deux derniers paramètres de fillText() sont les coordonnées où dessiner le texte. Comme les paramètres globaux textBaseLine et textAlign ont respectivement été définis à middle et end, le texte sera aligné verticalement et se terminera horizontalement sur ce point.

Enfin nous traçons la seconde partie du texte :


/* Les lettres "@t" seront dessinées après le point central de la toile. */
ctx.textAlign = "start";
ctx.fillStyle = sogreen;
ctx.fillText("@t", canvas.width / 2, canvas.height / 2);

Nous avons terminé ! Félicitations à toi, ô lecteur, si tu es parvenu au même résultat que moi. Si tu le veux bien nous le qualifierons d’époustouflant.

Le résultat Le logo Soat réalisé avec une toile HTML5[/caption]

Voici le projet de démonstration complet : SoCanvas.  Pour tester en live ça se passe sur JSFiddle !

Conclusion

Les interfaces de développement de Canvas sont puissantes et offrent un niveau de contrôle très important. En contrepartie le développement peut vite devenir long dans la mesure où l’on dessine presque chaque élément.

Les Canvas n’ont pas encore la chance de pouvoir être construits à l’aide d’outils performants comme l’éditeur pour Flash d’Adobe, mais cela viendra. Aujourd’hui déjà beaucoup de bibliothèques se proposent de vous aider à dessiner des toiles et à concevoir des jeux.

Canvas ne s’arrête pas à ce que nous avons construit et propose beaucoup d’autres fonctionnalités. Parmi celles que nous n’avons pas abordé s’inscrivent les animations, la gestion des images, les ombrages, la transparence, les masques, le contrôle du clavier et de la souris, la gestion des vidéos, du son, etc. Ces points feront l’objet d’autres billets.