Il est important de noter, qu'à la base ce cours a été créé
dans le cadre d'un travail de maturité réaliser au gymnase Auguste Piccard. La
version présentée est différente du contenu original, cette version à été
modifiée pour les besoins actuels
Pour rentrer directement en matière, sachez que Canvas est en
fait une balise HTML. Son rôle en tant que balise va être de créer un cadre
dans lequel nous allons pouvoir dessiner. D'ailleurs, "canvas" en
français signifie "toile". Cela donne déjà une bonne idée de ce que
nous allons pouvoir faire avec. C'est à ce moment que le Javascript entre en
jeu, son rôle va être de dessiner dans ce cadre. Il faut donc mettre en place
la balise en HTML et ensuite avec le Javascript nous allons dessiner à
l'intérieur.
Cette balise a été créée par Apple pour être utilisée sur son
navigateur, Safari. Elle est apparue en même temps que HTML5. Très vite cette
balise a été implantée dans Firefox. Ensuite c'est Google Chrome et Opera qui
ont réussi à l'interpréter. Finalement, dans sa dernière version, Internet
Explorer a acquis cette balise. Cependant il faut faire attention avec Internet
Explorer car il souffre d'un certain retard, encore aujourd'hui il ne connaît
pas certaines fonctions du Javascript, voilà pourquoi il faudra y être
attentifs quand vous utiliserez Canvas dans Internet Explorer.
Mise en place de notre Canvas
Vous êtes chanceux, la balise s'écrit comme son nom:
<canvas>.
Vous pouvez mettre à Canvas deux
attributs, un pour la hauteur et l'autre pour la largeur. Si vous ne définissez
pas sa taille, ses dimensions de base seront 300 pixels pour la largeur et 150
pour la hauteur.
Rappel: Les attributs pour les
balises sont à
intégrer immédiatement après le nom de la balise et avant le >. Dans notre
cas nous avons besoin de l'attribut height (hauteur) et de l'attribut
width(largeur).
Voici donc logiquement ce que vous devriez obtenir avec ces balises
Oui je sais, vous ne voyez toujours rien, cependant cette
fois notre cadre est créé et il fonctionne, mais la couleur du fond de Canvas
n'existe pas, voilà pourquoi il est invisible. Si vous ne croyez pas à son
existence, vous pouvez définir le style du fond d'une certaine couleur via CSS.
Comme déjà mentionné, les dessins se font via Javascript, il
faut donc que nous puissions accéder à Canvas. Plusieurs techniques sont
disponible, cependant durant ce cours nous allons y accéder avec son
identifiant ( id pour les intimes). Je lui donne comme
id: cvsn
note : j'ai légèrement grisé le fond de la zone pour la mettre en évidence car sinon on ne vois rien. je vous montre dans un oment comment faire pour cela.
Je vous expliquerai plus loin comment accéder à notre Canvas avec Javascript.
Mais avant il nous reste une dernière chose à mettre en place. Avec ce que je
vous ai dit et ce que vous pouvez voir maintenant, vous vous rendez certainement
compte qu'il manque quelque chose entre les deux balises, et vous avez tout à
fait raison. Si vous mettez du code entre les deux balises, le seul moment où il
sera interprété par les navigateurs, c'est si ceux-ci ne supportent pas Canvas.
Voilà pourquoi il peut être utile de mettre un petit message à l'intention de
ceux qui n'auraient pas une version récente de leur navigateur
Voilà pour ce qui est du HTML, nous en avons fini pour ce
chapitre. Jusqu'à la troisième partie nous n'allons plus toucher au HTML et
nous allons uniquement nous concentrer sur le dessin.
Il nous reste une dernière étape avant de pouvoir dessiner
dans notre Canvas, il faut y accéder. Pour cela il nous faut utiliser le
Javascript. Comme vous le savez déjà, on peut placer le Javascript à plusieurs
endroits pour le même effet. Pour ma part, je le mets toujours juste après la
balise de fermeture de Canvas.
Peut être avez vous appris à spécifier le type de script.
Cependant, depuis HTML5, il n'est plus obligatoire de le faire. De plus Canvas
ne fonctionne que sur HTML5, voilà pourquoi je ne précise pas le type.
La première chose à faire est de récupérerl'id de Canvas et de le stocker dans
une variable. Je nomme ma variable
cvsn(Can1). Ne vous inquiétez pas,
nous n'utiliserons pas longtemps cette variable, donc il n'est pas nécessaire
de la mémoriser ou de lui donner un nom spécifique.
Rappel: Pour accéder à un élément HTML avec le
Javascript depuis un id, il faut utiliser la
fonction: document.getElementById('Id_De_Mon_Canvas')dans mon cas je remplace l'id par
cvsx.
<canvasid ="cvs4"width ="150"height = "20">
votre navigateur n'accepte pas Canvas </canvas> <script> var canv1 = document.getElementById('cvs4'); </script>
Maintenant, voyons enfin quelque chose en rapport
spécifiquement avec Canvas, c'est la notion de contexte. Le contexte sert à
définir si nous travaillons en 2D ou en 3D. Actuellement, nous allons
uniquement travailler en 2D. Je reviendrai plus tard dans le cours sur la
question de la 3D. Le contexte sera l'élément central pour dessiner dans
Canvas. Pour récupérer le contexte, il faut utiliser la méthode
getContext('context')
Dans notre cas nous attribuons au contexte une dimension en
2D. Elle va récupérer le contexte de notre Canvas, donc la méthode complète
est:
monElementCanvas.getContext('context').
MonElementCanvas pour nous est stocké dans la variable
can1. Il faut stocker cela dans une nouvelle variable que je nomme context1
(ConTeXt).
<canvasid ="cvs5"width ="10"height = "150">
votre navigateur n'accepte pas Canvas </canvas> <script> var canv1 =document.getElementById('cvs5'); var context1 = canv1.getContext('2d'); </script>
Ceci est le code que vous devrez toujours mettre si vous
voulez utiliser Canvas. Mémorisez le bien car je ne l'écrirais pas à chaque nouvel
exemple.
Voilà, je sais que pour l'instant vous êtes un peu frustré
parce que vous n'avez encore rien vu de concret. Mais nous venons de faire déjà
une grande partie du travail. Maintenant nous pouvons enfin dessiner quelque
chose.
Voilà, la partie concrète commence enfin. Nous allons d'abord
apprendre à mettre en place des formes simples, comme des rectangles ou des
carrés. Je ferai une courte introduction sur les couleurs. Cependant je vous
donnerai plus tard une explication plus détaillée dans la chapitre sur les
couleurs.
Avant de dessiner (oui je sais, vous voulez dessiner mais
soyez encore un peu patient), je voudrais juste vous présenter le système de
coordonnées utilisé par Canvas. Pour cela, rien de mieux qu'un dessin.
Comme vous pouvez le voir au dessus, l'origine des points de
coordonnées est en haut à gauche. C'est important de se rappeler où est
positionné ce point car c'est depuis lui que nous placerons tout nos dessins.
Souvenezvous du sens des axes, car il
ne faut pas inverser les deux valeurs lors de nos dessins.
Ensuite, vous pouvez voir qu'on avance pixel par pixel, cela
reprend ce que je disais plus haut. Avec Canvas vous pouvez vraiment travailler
pixel par pixel.
Nous allons enfin dessiner. Pour cette première forme nous
allons rester dans quelque chose de très simple. Nous allons dessiner un
rectangle. Lorsque nous dessinons un rectangle, nous devons choisir une
position de départ pour le point en haut à gauche de notre rectangle. Ensuite
il faut définir sa hauteur et sa largeur. Pour ce faire nous avons la fonction:
fillRect(positionX, positionY, hauteur, largeur)
Rect signifie que nous voulons un rectangle. Et fill signifie
remplir. Maintenant il faut encore choisir une couleur pour notre rectangle.
Pour cela nous allons utiliser :
fillStyle = "couleur".
Le nom de notre couleur est à donner en anglais. Nous avons
maintenant tout les informations nécessaires pour dessiner notre première
forme. Il faut toujours mettre la définitions de la couleurs avant d'effectuer
le dessin car la fonction fillRect() va utiliser la couleurs qui à été
défini en dernier.
Je remets une dernière fois ce
qu'on a vu au chapitre précédent pour que vous vous rendiez bien compte où vont
les différents éléments. Et maintenant, voilà à droite ce que vous devriez obtenir si
vous avez fait tout juste. Oui, enfin quelque chose est visible. Amusez-vous à
modifier les paramètres passés à la fonction fillRect(). Les deux premiers paramètres
définissent le pixel du coin supérieur gauche puis, les deux suivants, la
largeur et la hauteur du rectangle.
Comme vous le voyez aux lignes 7 et 8, les deux fonctions que
nous avons vues sont a appliquer sur le contexte que nous avons défini. C'est
pour cela que j'ai dit que le contexte était l'élément central de nos dessins.
D'ailleurs on voit que je définis d'abord la couleur et ensuite je fais le
dessin. C'est très important de toujours définir la couleur qu'on veut appliquer
avant de dessiner notre objet, sinon celui-ci n'aura soit pas de couleur, soit
pas la bonne couleur si plusieurs couleurs sont définies.
Ce n'est pas l'unique méthode pour attribuer des couleurs à
nos formes, mais elles seront expliquées dans la suite du cours. Il est inutile
de surcharger vos cerveaux pour l'instant.
Vous avez vu qu'avant nous avons utiliséfill pour
signifier que nous voulions une forme remplie, mais il est aussi possible
d'obtenir des formes juste avec les contours tracés. Pour ce faire il suffit de
changer fill
par stroke. Le mot stroke veut dire
"trait" en français.
Le principe est le même que pour une forme remplie, la seule
chose qu'il faut rajouter est la taille du trait lineWidth
= tailleEnPixel.
Voici maintenant un exemple avec
un rectangle de même dimensions que celui d'avant, cependant celui-ci n'est pas
plein, mais il a juste un contour.
I.2.4 Méthode différente pour afficher un rectangle
Il existe une autre méthode pour afficher un rectangle. Au
lieu de définir le remplissage ou non du rectangle dans la méthode, cela se fait
après. Dans le cas présent, nous avons donc une une nouvelle méthode qui est
celle-ci : rect( posX, posY, largeur, hauteur);
Elle ressemble beaucoup au deux
précédentes, seulement le rectangle est seulement défini au niveau de sa
taille, par encore de la façon dont il doit être dessiné. Il faut donc rajouter
après cette méthode soit
stroke() soit fill().
Note: le stroke() est appliqué sur les
2 rectangles car il se trouve avant les deux méthodes qui créent les rectangles.
Il n'est donc pas utile de le répéter.
L'ordre dans lequel
les dessins sont effectués a une grande importance, je vous montre ci-après
comment pour gérer ce type de problème.
Chaque fois que vous faites un nouveau dessin, dans les cas
précédents des rectangles, ils vont simplement s'ajouter les uns sur les autres.
Il faut donc toujours prendre en compte. Par exemple, lorsque vous peignez, vous
mettez d'abord une couche de fond. Ensuite par dessus vous allez rajouter des
détails. Avec Canvas c'est la même chose. Imaginons que nous voulions le dessin
ci-dessous:
context1.lineWidth
= 5;
context1.stokeStyle = "black";
context1.fillStyle = "grey"; // grand rectangle extérieur vide
context1.strokeRect(50,50,50,80); // rectangle centrale plein context1.fillRect(60,60,30,60);
context1.lineWidth
= 2; // petit rectangle intérieur vide
context1.strokeRect(70,70,10,40);
Alors, qu'est ce que contient ce dessin ? Il y a un premier
rectangle vide, qui contient un autre rectangle qui lui est plein, au centre
duquel il y a un troisième rectangle qui lui aussi est vide. Comme vous le
voyez, pour décrire cette construction, je suis parti de l'élément le plus
profond, pour venir à l'élément du haut. Si on inverse l'ordre de construction
des deux derniers rectangle, le petit sera caché par le moyen qui est plein.
Nous verrons plus loin dans le cours une méthode qui permet
de jouer avec la superposition des éléments.
Jusqu'ici, nous n'avons toujours fait que dessiner, mais vous
avez certainement pensé que si nous pouvions dessiner, nous pouvions aussi effacer
des zones de notre cadre. C'est exactement ce que nous allons faire maintenant. La
méthode est: : clearRect(posX, posY, hauteur, largeur)
Les coordonnées x et y indiquent la position du pixel de départ
(coin supérieur gauche) , la
hauteur et la largeur indiquent la taille que doit avoir ce rectangle qui va
servir à effacer. Voici un exemple:
Attention, il ne faut jamais oublier de placer l'élément
clearRect
après les éléments que nous avons dessinés. Souvenez-vous de l'ordre dans
lequel se dessinent les objets.
L'utilité de pouvoir effacer les zones que nous voulons est
double, la première est de pouvoir effacer tout la zone de dessin Canvas et la
seconde est utile pour les animations. Il y
a une technique simple pour faire cela, elle consiste à envoyer comme
coordonnées de départ (0,0) et comme coordonnées d'arrivées la hauteur et la
largeur du cadre. Il est possible d'accéder à ces valeurs avec Javascript:
lecanvas.width (renvoi la largeur de mon Canvas)
lecanevas.height (renvoi la hauteur de mon Canvas)
I.3Les couleurs et techniques pour remplir le fond
Il existe deux façons de colorer ou remplir nos formes. La
première est par les couleurs, nous avons vu les bases juste avant. Il
existe également d'autres moyens d'assigner des couleurs à des objets.
La seconde façon consiste à charger une image et à l'appliquer dans la forme.
Il est possible de juste appliquer cette image pour faire office de fond, mais
il est aussi possible de la dupliquer pour créer une sorte de pattern.
Nous avons vu la première au chapitre précédent, il suffit de
donner le nom anglais de la couleur. Pour connaître tous les noms de couleurs
qui sont interprétées par le HTML, il vous suffit de faire une recherche sur
internet. Vous pouvez par exemple les trouver sur le lien suivant:http://www.w3schools.com/html/html_colornames.asp
.
Ce site regroupe le nom de toutes les couleurs qui sont
supportées par les dernières versions d'HTML. Il est aussi très utile pour
connaître les normes actuelles de HTML5. Cependant cette méthode d'assignation
des couleurs présente un désavantage. Chaque navigateur est libre de fixer la
couleur par rapport au nom comme il le veut. Voilà pourquoi un vert jaune sera
plus vert sur un navigateur et plus jaune sur un autre. Pour ne pas avoir ce
genre de différence il existe deux autres types de valeurs qui sont
interprétées de la même manière par les navigateurs.
La première est de donner la couleur en code
rgba (rouge, vert,
bleu, alpha). Les valeurs pour le rouge, le vert et le bleu sont
définies entre 0 et 255. Plus le nombre est élevé, plus l'intensité encouleur sera forte. Pour ce qui concerne
alpha, il est possible que vous n'ayez jamais entendu parler de cette valeur.
Elle indique la transparence de la couleur. Sa valeur peut varier entre 0
et 1. A zéro la couleur est totalement transparente et à 1 la couleur est
totalement opaque. Le code complet est un peu différent des autre méthodes, il
faut écrire: rgba (rouge,
vert, bleu, alpha)
Il existe une méthode pour appliquer une transparence globale
qui s'effectuera sur tous les éléments qui seront dessinés après l'appel de
cette méthode: globalAlpha =
valeur entre 0 et 1
Lla dernière façon d'assigner une couleur, c'est le code hexadécimal.
L'hexadécimal est un système de calcul en base 16. Je ne vais pas vous
expliquez comment ces calculs sont effectués, mais voilà à quoi peut ressembler
un code en hexadécimal: #RRVVBB.
Par exemple, pour la couleur verte,
il y a trois façons de la définir: par son nom anglais, green, par son code rgba (0,255,0,1)
ou encore son code hexadécimal, #00FF00.
Rien de mieux qu'un petit exemple pour illustrer tout cela.
Je sais que je me répète mais vous
pouvez voir ici l'importance de l'ordre dans lequel les rectangles sont dessinés pour la
gestion de la transparence.
Une dernière méthode existe, elle
est cependant plus rarement utilisée. La voici:
hsl (dégradé, saturation, luminosité)
Dégradé (hue en anglais) génère un degré de couleur. Au lieu de se
baser sur une échelle de 255 pour une seule couleur, il se base sur 360°. 0°
est rouge, 120° est vert, 240° est bleu.
La valeur pour la saturation se donne
en pourcentage; à 0% la couleur est dans les ton gris, à 100% la couleur est
pleine (comme l'IRC en éclairagisme).
Pour la luminosité c'est le même type de valeur qui est attendue: à 0%
la lumière est noire, à 100% la lumière est blanche. Je vous donnerai un
exemple dans le chapitre sur les animations pour vous rendre compte de
l'utilité d'une telle façon de définir des couleurs.
Voilà encore une des grandes
spécialités de Canvas, les dégradés. Il peut créer des dégradés très adoucis au
niveau des couleurs et de la transition. Il existe deux types de dégradés.
Le
linéaire qui sera le long d'un axe, et le radial avec un point central et un
rayon de diffusion. Commençons avec le radial: createLinearGradient(positionXdépart, positionYdépart,
positionXarrivée, positionYarrivée)
Avec cette fonction, nous pouvons
mettre en place une droite que le dégradé suivra. Il faut intégrer cela à une
variable que nous appelons degrad. N'oubliez pas que cette fonction provient
du contexte. Donc au final il faut faire quelque comme cela:
var degrad=
context1.createLinearGradient()
Ensuite il faut ajouter des
couleurs. Pour ce faire, il existe une fonction spécialement créée pour ajouter
des couleurs à notre dégradé: addColorStop(positionDansLeDégradé,
couleur)
La position dans le dégradé varie
entre 0 (départ)et 1(fin). Donc, cela implique qu'il faut ajouter à chaque fois
une nouvelle couleur, à chaque fois qu'on veut créer un nouveau dégradé. Il
faut donc au moins 2 couleurs pour créer un dégradé. Cette fonction est à
appliquer à notre variable: degrad.addColorStop()
Une fois que nous avons toutes les
couleurs que nous voulons, il faut passer notre variable gradient comme si
c'était une couleur. Donc il faut faire: fillStyle = degrad.
Puis il suffit de
remplir un rectangle de la taille de notre Canvas pour obtenir un joli dégradé.
var degrad =
context1.createLinearGradient(0,150,200,-150); // le sens du dégradé
est en diagonal de bas gauche à haut droite
degrad.addColorStop(0, "red");
// couleur de départ
degrad.addColorStop(1, "lime");
J'ai aussi parlé d'un dégradé de
type radial :
createRadialGradient(posX0, posY0, rayon0,
posX1, posY1, rayon1)
Cette méthode est un peu différente
de la précédente. Les deux points posX0et posY0sont les
coordonnées du centre du premier cercle dont son rayon est défini par le paramètres rayon0.
Il est en de même pour le second cercle (posX1,
posY1, rayon1). Le dégradé s'étant entre les deux cercles.
Voici le résultat qu'on peut
obtenir en mettant le centre de deux cercles au même point:
var degrad =
context1.createRadialGradient(100,75,10,100,75,100);
Canvas accepte tout à fait les
images. Cela se fait en deux étapes, la première est de charger une image avec
Javascript. Pour ce faire il faut faire appel au constructeurnew Image().
Vous devriez déjà avoir vu ce type de construction si vous avez déjà créé vos
propres objets en Javascript.
L'étape suivante consiste à
attribuer un lien pour l'image à charger, c'est la propriété src. Oui,
c'est la même que lors de la mise en place d'un lien HTML. Une autre propriété
de cet objet est onload. Je vais toujours dessiner mes images après
l'appel de la fonction créée avec onload, car il se peut que dans certains cas,
surtout quand l'image est trop grande, que celle-ci soit affichée de manière
saccadée. Ce que nous allons faire, c'est que nous allons attribuer à cette
valeur une méthode propre à Canvas qui dessinera l'image.
Voici la méthode qui va dessiner
notre image dans le Canvas :
drawImage(image, posX, posY). La
variable image fait référence à l'image chargée.
Elle sera dessinée aux positions x et
y indiquées. Voici donc comment afficher
une images dans Canvas
<canvasid ="cvs17"width ="282"height =
"192">
votre navigateur n'accepte pas Canvas </canvas> <script> var canv1 =document.getElementById('cvs17'); var context1 =canv1.getContext('2d'); varimage0 = new Image(); image0.src
= 'img/lyon.jpg'; //adresse de l'image
image0.onload = function () {
context1.drawImage(this,0, 0);
} </script>
Note: this fait
référence à l'objet en court d'utilisation, ici c'est image créée plus haut
Cependant, utiliser la méthode telle quelle présente un
défaut; La taille de l'image ne correspond pas forcément à la taille du Canvas.
Dans l'exemple précédent, j'ai adapté la taille du Canvas pour que l'image
soit entièrement à l'intérieur. Voilà pourquoi il existe une syntaxe plus
complète pour cette méthode qui permet de modifier la taille de l'image à
afficher: drawImage(image,
posX, posY, widthImage, heigthImage).
widthImage définit la largeur de l'image qui sera
affichée (ne tient pas compte de la largeur de l'image originale).
heigthImage
définit la hauteur de l'image qui sera affichée (ne tient pas compte de la
hauteur de l'image originale).copie :
Il est possible que dans certains cas nous voulions découper
notre image pour n'en afficher qu'une certaine partie. À nouveau il existe une
syntaxe différente de la méthode pour pouvoir découper l'image:
cutX et
cutY définissent le point de départ de
la découpe de l'image (par rapport à l'image). cutWidth et
cutHeight
définissent réciproquement la largeur et la hauteur de découpage. Au final on
se trouve avec une nouvelle image rectangle.
Attention cependant à l'utilisation de ce moyen pour modifier
la taille de nos images. Dans mon exemple, vous pouvez voir que je ne respecte
plus le fait que mon Canvas est de forme carrée, du coup mon image est
déformée. De plus, une perte de précision est visible.
Un motif est une image qui va être répétée pour former le
fond d'une page. Il faut donc partir d'une image et ensuite la répéter, soit de
façon horizontale, soit de façon verticale, soit des deux manières. Il existe
une méthode simple pour créer un motif: createPattern(image,
répétition);
Le paramètre répétition peut prendre trois valeurs:
repeat-y: pour une répétition horizontale
repeat-x: pour une répétition verticale
repeat: pour une répétition qui combine les deux
dernières possibilités.
Ensuite il faut attribuer notre motif à la propriété
fillStyle.
La dernière chose à faire consiste à créer un rectangle pleinde la dimension que nous voulons.
C'est maintenant que nous allons commencer à nous amuser. Les
traits nous offrent un très grand nombre de possibilités. Au début ce seront
uniquement des traits droits. Après nous commencerons à faire des cercles et
des ovales. Pour finir, nous nous attaquerons aux courbes.
J'ai toujours eu l'impression d'être un vrai peintre lorsqu'il
est question de traits avec Canvas. Je m'explique, vous êtes un peintre, quelle
est la première chose que vous faites ? Vous choisissez une toile sur laquelle
peindre, ensuite vous trempez votre pinceau dans la peinture de votre choix.
Après commence réellement le dessin, vous placez votre pinceau sur votre toile
à un point de départ et après vous vous déplacez de point en point jusqu'à
obtenir la forme souhaitée. Et bien Canvas utilisera la même méthode de
travail.
Pour choisir la couleur, c'est exactement les mêmes méthodes
qu'avant; fillStyle si nous voulons une forme remplie et
strokeStyle
si nous voulons juste le trait. Nous pouvons encore choisir l'épaisseur du
trait, pour ce faire il faut utiliser lineWidth
que nous avons déjà utilisé avant. Jusque là, rien de bien nouveau.
Maintenant que notre pinceau est prêt, nous devons dire que
nous commençons notre trait. Pour ce faire nous utilisons la méthode de notre
contexte : beginPath()
Une fois celle-ci mise en place, il faut déplacer notre
pinceau jusqu'au point de départ, sans pour autant déjà peindre :
moveTo( x, y)
Cette méthode est exactement là pour ça. Elle nous permet de
nous déplacer dans le Canvas sans pour autant tracer un trait visible.
Maintenant que notre pinceau est à son point de départ, nous allons enfin
pouvoir tracer nos traits avec la méthode :
lineTo( x, y)
Nous pouvons maintenant tracer tout les traits que nous
voulons pour faire notre forme. Une fois qu'on est au dernier point, il existe
une méthode : closePath()
Celle-ci n'est pas obligatoire, elle va uniquement tracer un
trait entre le dernier point et le premier. D'ailleurs il est l'heure de donner
de la couleur. Pour donner la couleur, il suffit soit de mettre fill(),
si nous voulons une forme pleine et stroke() si nous ne voulons que le
trait. Ses deux méthodes doivent se trouver après la dernier trait tracé. Assez
parlé, place à l'exemple:
context2.lineWidth = 5;
context2.fillStyle ="rgba(0,0,150,0.3)";
// rempli en bleu
context2.strokeStyle = "#A00000";
// traits en rouge
context2.beginPath(); context2.moveTo(
10, 10);
context2.lineTo( 10, 140) ;
context2.lineTo( 140, 140) ;
context2.lineTo( 180, 60) ;
context2.stroke(); context2.fill();
context2.beginPath(); // dessin d'un
forme vide context2.moveTo(
20, 240);
context2.lineTo( 70, 280) ;
context2.lineTo( 120, 240) ;
context2.closePath();
// ferme le triangle
context2.stroke(); context2.beginPath();//
dessin d'un forme plein (sans bord) context2.moveTo(
20, 350);
context2.lineTo( 70, 390) ;
context2.lineTo( 120, 350) ; context2.fill();
On peut faire bien plus que de simplement modifier la taille
du trait. Il est aussi tout à fait possible de modifier les extrémités des
traits. La propriété est la suivante: lineCap
Elle peut recevoir trois attributs:
round: pour que l'extrémité du trait
soit arrondie;
butt: le bout est coupé exactement aux
extrémités ( mode par défaut);
quare: le trait est aussi coupé en carré
comme avec butt, mais dans ce cas là il dépasse du point d'arrivée. Le
trait a la même longueur qu'avec le type d'extrémité à
round.
Vous pouvez voir que j'ai été obligé de faire un nouveau
chemin à chaque fois. Si vous ne faites pas cela, seul le dernier
lineCap
est pris en compte.
Une autre transformation existe, il est possible de modifier
la jointure entre les différents traits lorsqu'il y a par exemple des coins.
Cette fois nous devons faire appel à une autre méthode : lineJoin
Comme la précédente, elle possède trois valeurs possibles:
bevel: les joints sont biseautés
round: les joints sont arrondis
miter: les traits sont rallongés jusqu'à
former un angle (mode par défaut)
Il y a une autre propriété des
traits de Canvas. Elle n'est utilisable que lorsque la valeur de lineJoin
est miter. Elle va affecter la longueur maximale qu'il y a entre
l'intersection des deux droites jusqu'à la pointe formée par l'angle. La
longueur dont je parle est celle qui sépare les deux traits noirs les deux
points de liaison (intérieur et extérieur).
Maintenant voici cette fameuse propriété :
miterLimit = nombre; De base sa valeur est à 10. Lorsque la longueur évoquée au
dessus dépasse celle attribuée par miterLimit, l'angle se dessine de la
même façon que lorsque la propriété lineJoin =
'bevel'.
C'est bien joli de pouvoir faire des formes avec des coins,
mais qu'en est-il si nous ne voulons pas de coins? Sachez que Canvas offre la
possibilité de faire des cercles, pour cela il y a une seule et unique méthode:
Je pense que les trois premières valeurs sont simples à
comprendre, les coordonnées x et y pour placer le centre de notre cercle et le
rayon, je pense que vous avez compris. Cependant vous pouvez voir qu'il y a une
question d'angle et de sens, je vais vous expliquer tout cela. Dans certaines
situations, il est possible que nous ne voulions pas faire un cercle complet,
voilà pourquoi il y a une question d'angle de départ et un angle d'arrivée. Le
sens va interférer sur le sens dans lequel le cercle va se construire par
rapport aux angles choisis. Voici une explication en image:
Il est important de toujours garder ce dessin en tête quand
vous dessinez un cercle. Cependant, sachez que cet attribut est optionnel, de
base sa valeur est false (sens horaire).
Une autre information importante: Canvas travaille en
radiant. Ce qui signifie que vous ne pouvez pas simplement donner la valeur en
degré, vous devez faire la transformation de degré à radiant. Cela est simple,
il suffit d'effectuer ce petit calcul:
(Math.PI/180)*angle
Math.PI renvoi la valeur de pi, qui en radiant est
équivalent à 180°. Je divise donc 180° par 180, comme ça je me retrouve avec la
valeur de 1°, il me suffit de multiplier ensuite le tout par la valeur de
l'angle que je souhaite utiliser. Voilà un exemple avec un petit smiley:
Comme vous l'avez ppris au
chapitre sur les couleurs, il est possible d'ajouter des dégradés, voilà le
genre de dessin qu'il estpossible
d'obtenir en jouant avec les dégradés:
Il existe une dernière méthode qui implique les arcs, c'est
celle-ci : arcTo(P1x,
P1y, P2x, P2y, rayon).
Cette méthode va continuer à partir du dernier point atteint.
Le premier couple de points (P1x, P1y) est le point vers lequel l'arc va tendre.
Le second
couple de point (P2x, P2y) est celui d'arrivée de l'arc.
Le rayon définit le rayon du
cercle.
Voilà un petit exemple de mise en place de
cette méthode :
<canvasid ="cvs27"width ="200"height =
"200">
votre navigateur n'accepte pas Canvas </canvas> <script>
var canv2 =document.getElementById('cvs27');
var context2 =canv2.getContext('2d');
context2.strokeStyle ="#f05000";
context2.fillStyle ="#200020"; context2.lineWidth =4;
context2.beginPath(); context2.moveTo(10,
20);
context2.lineTo(90,
20);
// horizontal haut
context2.arcTo(110,
20,130,50,50);
// cercle ext.
context2.lineTo(130,
130);// vertical droite
context2.lineTo(100,
130); // horizontal bas
context2.lineTo(100,
80); // vertical
context2.arcTo(100,
40, 50,50,50); // cercle int
context2.lineTo(10,
50); // horizontal
context2.closePath(); // ferme la forme
context2.stroke(); context2.fill(); </script>
Je viens de vous présenter une façon de faire des formes
non-cubiques avec des cercles, mais très vite nos dessins sont limités. Il
existe une dernière manière de tracer des traits : les courbes. Elles
sont au nombre de 2 types différentes.
1 ° Les courbes de bézier:
bezierCurveTo( p1x,
p1y,p2x, p2y, p3x, p3y) Le premier et le
second couple de coordonnées (p1x,
p1y,p2x, p2y) sont lespoints
de tension (vers lesquels la courbe va tendre). Le troisième couple (p3x,
p3y) est le point d'arrivée.
2° La seconde courbe est la quadratique:
quadraticCurveTo(
p1x, p1y, p2x, p2y)
Le premiercouple de
coordonnées (p1x, p1y) est un point vers lequel la courbe va tendre.
Le dernier couple de coordonnées (p2x, p2y)
est le point de tension (point d'arrivé). Voici une
illustrations des deux courbes :
Si nous devions écrire du texte avec les précédente méthodes,
cela serait très lent et une réelle perte de temps. Heureusement pour nous
Canvas a plus d'un tour dans son sac.
Canvas nous offre deux méthodes pour écrire du texte, les
voici : fillText("texte",
posX, posY) ou
strokeText("texte",
posX, posY)
Je pense que la différence vous saute aux yeux. Dans l'un des
cas, le texte est plein ( fillText() ), dans le second
( strokeText() ) il n'y aura que le contour des lettres
du texte qui sera affiché, l'intérieur sera vide. Il faut tout de fois faire
attention à l'utilisation de la seconde fonction, car celle-ci peut donner un
texte peu visible ou même ne pas afficher que les contours, utilisez donc cette
méthode avec parcimonie. Pour les deux positions (posX,
posY ), elles correspondent au coin
inférieur gauche du texte. Donc si vous mettez comme coordonnées (0,0), le
texte sera en dessus de la zone de dessin visible.
Il existe trois méthodes pour aider à la mise en page du
texte.
1° La première nous permet de modifier la taille et la police du texte :
font= "taille
police1,police2,police3 ..."
Voici un exemple font = "12pt Times, Arial, sans-sherif"
La première valeur à indiquer est la taille de la police,
elle peut s'indiquer soit par sa taille en pixel sois par sa taille en point.
Ensuite, il est possible de mettre plusieurs polices différentes, c'est utile
par exemple lorsque la première police donnée n'est pas prise en compte sur le
navigateur qui va lire la page. Dans l'exemple la taille du texte est de 12pt
et la police est Times. Cependant si le navigateur de ne gère pas la polices
Times, alors il utilisera Arial et s'il ne l'utilise pas alors il utilisera sans-sherif.
2°
La seconde est une méthode pour aligner horizontalement le texte : textAlign =placement
placement peut prendre trois variable;left(gauche), center(centre),right((droite).
3° Il en existe une troisième qui va gérer
son alignement vertical : textBaseline =
placement qui peut aussi prendre trois variable: top(haut),
middle(milieu),bottom(bas).
Elles ne sont pas obligatoires pour écrire du texte, elles
vont simplement modifier la position de base du texte.
Il existe une autre fonction, elle est cependant rarement
utilisée, elle permet de mesurer la taille en pixel d'un texte, cela peut être
utile pour ne pas dépasser du cadre :
measureText(votreTexte)
J'ai des petites informations à donner sur le dernier point:
La première est que vous avez certainement
reconnu la fonction Math.round
(), je l'utilise pour ne pas me retrouver
avec un nombre affolant à virgule.
La seconde est sur ma variable qui teste la
taille de mon texte, je ne récupère que sa longeur, mais je pourrais aussi
récupérer sa hauteur.
Troisième information, j'aurais très bien pu créer
une variable qui stockerait mon texte. Comme ça je la passe non seulement à ma
méthode measureText () mais en plus je sais que c'est aussi elle que
j'écris à l'écran.
Jusqu'ici, vous avez appris les bases de Canvas, mais il
existe encore bien des choses à apprendre. Ce sont plus des petites astuces que
vraiment de nouvelles façons de dessiner, mais elles ont tout de même leur
place dans ce cours.
Il est possible avec Canvas de mettre en place des ombres. Il
faut d'abord définir la projection de l'ombre par rapport à l'objet ombragé.
Pour cela nous avons deux propriétés: shadowOffsetX et
shadowOffsetY. Si vous n'apportez pas de modification aux deux propriétés,
leur valeur sera celle par défaut, l'ombre sera alors placée équitablement
autour de la forme. Il suffit ensuite de leur attribuer une valeur, elle
représente le décalage par rapport à l'objet qui contient l'ombre. Si vous
laissez ça nul, l'ombre sera placée de manière égale autour de la forme.
Ensuite nous pouvons assigner une quantité de flou à cette ombre :
shadowBlur . Plus la valeur attribuée à
shadowBlur est élevée, plus
la quantité et la dissipation de l'ombre seront importantes, à 0 l'ombre est
nulle. Il est inutile de mettre une valeur trop haute, car si elle est trop
haute il n'y aura simplement plus d'ombre. La dernière étape consiste à
attribuer une couleur à notre ombre, encore une fois, Canvas nous offre une
nouvelle propriété: shadowColor
Ensuite c'est tout. Il n'y a pas besoin de dessiner nous même
l'ombre, elle se dessinera automatiquement. Cependant, cela implique qu'il faut
avoir défini notre ombre avant de dessiner la forme qui doit avoir l'ombre.
Voici de quoi illustrer mes propos:
Jusqu'à maintenant, nous avons toujours travaillé dans
l'entier du contexte. Il existe cependant une méthode qui permet de définir une
zone précise dans laquelle les dessins vont se faire. Cette méthode est la
suivante : clip(); Elle doit être appliquée après avoir dessiné une première
forme, par exemple un rectangle, ensuite il suffit de dessiner tout ce que nous
voulons dans le contexte, seulement ce qui se trouve à l'intérieur de cette
zone définie avant sera visible, comme dans l'exemple suivant:
context2.fillStyle ="gainsboro"; context2.strokeStyle = "black";
context2.fillRect
(0,0,
150,150);//
le fond de cnavas est grisé
context2.rect (25,25,
100,100); context2.stroke();
context2.clip(); //
la zone d'affichage = le dernier rectangle
context2.fillStyle ="darkred";
context2.font = "22pt Arial, Times";
context2.fillText ("c'est
coupé",10, 34); //
le haut et les cotés sont coupés
context2.fillText ("c'est
coupé",10, 75);
//
les cotés sont coupés
context2.fillText ("c'est
coupé",10, 134);//
le bas et les cotés sont coupés
Vous vous rappelez que dans les chapitres précédents je
mettais un point d'honneur à dire que l'ordre dans lequel nous dessinions était
important, et bien je ne vous ai pas tout dit. Avec Canvas nous avons la
possibilité de travailler avec la superposition des pixels pour en faire à peu
près ce que nous voulons. D'abord la méthode pour faire ça :
globalCompositeOperation Cette méthode peut trier certains pixels pour nous. Cela
permet d'afficher ou de cacher certains pixels. C'est un outil qu'on peut
retrouver dans certains logiciels de dessin avec le système de calque.
Attention, je ne dis pas que Canvas fonctionne avec un système de calque. Il y
a en tout 16 opérations possibles.
Quatre s'appliquent par rapport au contenu que nous allons créer
(source):
source-over (place la source au dessus)
source-in (seule la source qui chevauche ce qui est déjà dessiné est affichée)
source-out (seule la source qui ne chevauche pas ce qui est déjà dessiné est affichée)
source-atop (la
partie de la source qui chevauche ce qui est déjà dessiné est affichée)
Quatre autres vont s'appliquer par rapport aux dessins qui
ont déjà été fais (destination):
destination-over (la destination est au-dessus du nouvel objet )
destination-in (la destination est affichée sous le nouvel objet)
destination-out (seulela destination qui n'est pas chevauchée par le nouvel objet est affichée)
destination-atop (la partie de la destination qui chevauche le nouvel objet est affichée)
Les quatre dernières font un mixe entre le nouvel élément et
les anciens.
lighter(la zone de chevauchement est plus claire)
darker (la zone de chevauchement est plus foncée)
copy(seul le nouvel objet est affiché)
xor (tout est affiché sauf la zone de chevauchement)
Je vous donne directement un récapitulatif des 12 différents résultats que nous pouvons obtenir en utilisant
la même construction que celle écrite ci-dessus :
source-over
source-in
source-out
source-atop
destination-over
destination-in
destination-out
destination-atop
lighter
darker
copy
xor
II.1.4 Connaître la position d'un point par rapport à un tracé
Il peut être utile dans certains cas de savoir si un point
précis se trouve dans le tracé en cours. Pour ce faire il faut utiliser la méthode:
isPointInPath(posX, posY); Cette fonction retourne true si le point qui est
défini par posX et posY se trouve dans le tracé en cours. Dans l'exemple
suivant je vais définir un rectangle avec la méthode rect(), ensuite je vérifie
si le point donné se trouve à l'intérieur de celui-ci, si c'est le cas alors
j'affiche le rectangle:
<canvasid ="cvs46"width ="150"height =
"150">
votre navigateur n'accepte pas Canvas </canvas> <script>
var canv2 =document.getElementById('cvs46');
var context2 =canv2.getContext('2d');
context2.fillStyle ="deeppink";
var pixelX=60; var
pixelY = 80;
context2.rect
(25,25,
120,120);
if ( context2.isPointInPath(pixelX,
pixelY)) {
context2.fill();//
n'est excécuté que si le point est dans rect.
};
// fin de la condition if {}
 </script>
Le rectangle s'affiche car la valeur retournée par
isPointInPath()
est vraie. Cela se vérifie par le dessin au-dessus où nous pouvons voir que le
point ( 60, 80) est bien situé à l'intérieur du rectangle. Cette méthode est
très utile dans le cas où nous utilisons des variables qui peuvent changer au
cour du temps.
Attention, ne vous attendez pas à créer le nouveau Paint. Je
vais simplement vous introduire des notions un peu plus poussée en Javascript
pour par exemple dessiner un cercle à chaque clic sur notre Canvas.
La première étape est de mettre un événement sur notre balise
Canvas.
Rappel: Il existe plusieurs types d'événements. Pour
les mettre en place, il suffit d'insérer à l'intérieur d'une balise un type
d'événement. Par exemple <canvas onclick="alert('test');">. Dans cet exemple, si vous cliquez sur le cadre, une
nouvelle fenêtre va s'ouvrir avec comme message "test". Pour
récupérer les informations de cet événement, il suffit d'envoyer un attribut: event.
Par exemple: <canvas onclick="maFonction(event);">.
Dans notre cas, nous voulons plusieurs choses:
Un événement qui s'active lorsque notre curseurpasse par dessus le cadre de Canvas
La position de notre balise Canvas dans la page.
La position de la souris dans le Canvas.
Afficher un message avec les informations de positions.
Voici à quoi devrait ressembler votre balise Canvas et la
fonction que nous allons utiliser.
<canvasid ="cvs"width ="300"height =
"150"onmousemove="XYcurseur(event)">;
votre navigateur n'accepte pas Canvas </canvas>
function XYcurseur(event)
{ // code de la fonction
};
// fin de la function {}
Plusieurs étapes importantes sont maintenant à mettre en
place dans notre fonction. La première est d'effacer l'entier du cadre, sinon
le texte se superpose et il deviendrait très rapidement illisible. L'étape
suivante consiste à récupérer la position du cadre Canvas dans la page en
tenant compte du défilement vertical de la page. Pour ce faire, il existe une
méthode qui retourne les positions exactes des objets de type blocs :
getBoundingClientRect()
(à appliquer sur le canvas et non sur le context
exemple : moncanvas.getBoundingClientRect()).
Maintenant que nous avons cette position, nous pouvons
calculer la position du curseur par rapport au cadre de Canvas. Pour ce faire
il faut encore récupérer une donnée, c'est la position du curseur, c'est à ce
moment que nous avons besoin de la variable de type event que nous avons
transmit à notre fonction:
event.clientX (Coordonnée x de la souris)
event.clientY (Coordonnée y de la souris)
<canvas id ="cvs47"width ="250"height =
"150 onmousemove="XYcurseur(event)">
votre navigateur n'accepte pas Canvas
</canvas> <script> var canv47 =
document.getElementById('cvs47'); var context47 =
canv47.getContext('2d'); context47.font="18pt
Arial"; context47.fillStyle="blueviolet";
function XYcurseur(event)
{ context47.clearRect(0,0,canv47.width,canv47.height); // efface le cadre var XYrect =
canv47.getBoundingClientRect();
// action avec le canvas et pas le context var
Xcurseur = Math.round(event.clientX- XYrect.left); var Ycurseur =
Math.round(event.clientY
- XYrect.top);
context47.fillText("Position de la
souris",10, 35);
context47.fillText("X="+Xcurseur, 70, 70);
context47.fillText("Y="+Ycurseur, 70,
105); };
 </script>
Dans le chapitre précédent, nous avons travaillé avec
l'événement onmousemove
= fonction(event), mais dans ce chapitre, si nous voulons
dessiner un cercle à chaque fois que nous cliquons sur le Canvas, nous devons
utiliser onclick=
fonction(event).
Le reste est presque comme le dernier exemple, il faut
récupérer la position du curseur et ensuite dessiner un cercle ayant comme
centre la position du curseur au moment du clic. Voilà à quoi cela devrait
ressembler (mon exemple implique des nombres aléatoires):
Note: Vous pouvez voir que pour obtenir un nombre
entre 0 et 255, j'utilise à la ligne 14 un Math.floor() (donne
l'entier inférieur ou égal), car sinon il
serait possible d'obtenir comme résultat 256, ce qui n'est pas possible quand
nous travaillons avec des couleurs. Ceci est un exemple de l'utilisation des nombres
aléatoires avec Canvas. Non seulement la taille des cercles va varier, mais en
plus chaque couleurs sera différente.
Canvas est une surface de pixels. Jusqu'à présent
nous avons seulement effleuré cette idée. Nous allons maintenant réellement en
parler. Dans ce chapitre nous allons pouvoir travailler pixel par pixel,
couleur par couleur. Dit comme ça, cette tâche peut paraître faramineuse, mais
vous comprendrez vite que ce n'est pas le cas.
Je sais que je vais me répéter, mais Canvas est une surface
de pixels. Et si nous pouvons dessiner tous les pixels que nous voulons grâce à
des méthodes qui vont faire le travail à notre place, nous pouvons aussi
accéder à chaque pixel, à chaque couleur de chaque pixel. Pour cela il existe
deux méthodes : createImageDate( x, y);
getImageData( x, y, width, height);
Ces deux méthodes crée un tableau qui contiend toutes
les valeurs de nos couleurs pour chaque pixel. Cependant elles ne fonctionnent
pas de la même manière. createImageDate( x, y) crée une image vierge. Nous allons donc
pouvoir créer une image de A à Z avec cette méthode. Les dimensions de cette
image sont définies par x et
y. getImageData(
x, y, width, height) crée un tableau de pixels en récupérant ceux présents sur le canvas
déjà créé. Les pixels récupérés sont un rectangle. Le coin supérieur gauche de
ce rectangle sera la position définie par x et
y, ensuite sa taille sera
définie par width et
height.
Les deux valeurs: x et
y indiquent la taille du
contexte. Donc si notre contexte fait 150 par 150, les valeurs
x et y sont 150. Ensuite un point important à comprendre est comment est construit
ce tableau. Il va stocker la couleur de chaque pixel, mais comme nous le
savons, une couleur est composée de quatre données, c'est le système RGBA. Cela
signifie que les quatre premières cases de notre tableau sont uniquement la
couleur du premier pixel. Voici un petit récapitulatif de l'apparence du
tableau en question:
Pixel 1
Pixel 2
Pixel 3
Pixel [4]
Red
[0]
[4]
[8]
[12]
Green
[1]
[5]
[9]
[13]
Blue
[2]
[6]
[10]
[14]
Alpha
[3]
[7]
[11]
[15]
Cela implique que pour parcourir l'ensemble
des pixels, il faut sauter de 4 en 4.
Note: Je récupère les informations de notre objet
imageD
avec la propriété data .
.J'applique une couleur de fond à mon contexte, ensuite je
récupère l'ensemble des valeurs des couleurs et je demande à ce qu'un alert()me renvoie la valeur de la couleur du premier pixel. Vous pouvez voir que
c'est bien les bonnes valeurs qui sont retournées (dans cet exmple rouge :
1*16=16, vert : 1*16=16, bleu A=1 => 10*16=160)
Je pense qu'à ce moment fuse une grande question dans votre
tête, pourquoi est-ce que la valeur alpha est 255 alors que nous avons définis
que sa valeur était 1? Comme je l'ai dis plus tôt, pour définir alpha, nous
donnons des valeurs comprisent entre 0 (transparent) et 1 (opaque). Hors, les
couleurs du pixels ne sont pas stockées ainsi. Je ne sais pas si c'est une
erreur de la part des personnes qui s'occupent de Canvas ou quelque chose comme
ça, mais n'oubliez pas cette information, car lorsque nous voudrons modifier
des valeurs alpha, nous devrons travailler entre 0 et 255, pas entre 0 et 1
comme lorsque nous définissons une couleur.
Vous vous rappelez de notre précédente photo
photo du Lyon ?
Imaginons maintenant que nous voulions faire certains outils pour
travailler la photo et que nous voulions appliquer un filtre de couleur par
dessus. C'est là que la récupération de tous les pixels peut être utile et vous
allez voir que c'est non seulement très simple mais aussi très rapide.
Tout d'abord il existe une nouvelle méthode à apprendre pour
pouvoir retourner les tableaux modifiés :
putImageData(
imageData, x, y); Cette fonction va retourner les valeurs du tableau de pixels
enregistré dans la variableimageD. La position
x et y indique le
coin supérieur gauche à partir duquel les pixels de imageD vont être
affichés.
Il faudra aussi mettre en place une boucle pour modifier les
pixels. Pour ce faire il faut en utiliser une de type for qui fera
l'ensemble des cases du tableau. Pour récupérer la longueur du tableau vous
pouvez utiliser: mapP.length. Ensuite souvenez-vous ce que j'ai dis
avais, pour passer de pixels en pixels, il faut sauter de 4 en 4, c'est pour ça
qu'à chaque tour de boucle il faudra incrémenter notre variable i par + 4.
Dans cet exemple, j'applique un filtre rouge sur l'ensemble de
l'image. Pour ce faire je diminue la valeur du vert et du bleu tout en
augmentant celle du rouge.
Note: Le premier pixel représente la valeur pour le
rouge, le second le vert et le troisième le bleu. Je ne m'occupe pas de la case
du tableau qui gère l'alpha.
Je vous montre un autre exemple où, au lieu d'augmenter
l'intensité d'une couleur, je vais mettre la photo en noir et blanc. Je vais
donc me retrouver avec uniquement des gris. Si vous avez déjà un peu jouer avec
les couleurs, vous n'êtes certainement pas sans savoir que les valeurs pour le
rouge, le vert et le bleu sont égales les unes aux autres. C'est exactement ce
principe que je vais utiliser ici:
Note: La seule chose qui est modifiée par rapport à
l'exemple précédent, c'est dans la boucle for.
Dans les deux exemples que je vous ai montrés, j'utilise comme fond une image,
mais il est tout à fait possible d'appliquer ces transformations de pixels sur
des dessins que vous auriez fait vous-mêmes. Ce que vous venez d'apprendre
représente un vrai avantage de Canvas par rapport à ses concurrents, comme SVG.
C'est un outil très puissant mais aussi très rapide. Vous avez aussi pu vous
rendre compte que bien que nous touchions au pixels eux-même, il n'y a eu
aucune perte de qualité des images. C'est dû au fait que nous avons travaillé
pixel par pixel.
Avant de nous lancer à corps perdu dans les animations, il y
a certains points importants à savoir sur le contexte. Actuellement nous
n'avons jamais réellement joué avec le contexte. Sachez cependant que nous
possédons de puissantes méthodes qui vont révolutionner votre vision de Canvas.
Jusqu'à maintenant nous avons toujours travaillé sur une
version unique du contexte. Mais avec deux méthodes :
save() et
restore(). Nous pouvons sauvegarder avec save() l'état du
contexte sur lequel nous étions en train de travailler. Ensuite avec
restore()
nous pouvons restaurer l'état du contexte. Il suffit d'appliquer
save()
avant que nous ne modifions et restore() lorsque nous avons fini de
travailler avec la version modifiée du contexte.
Ce genre de méthode peut être très utile lors de la création
d'animations. Par exemple, imaginons que nous voulions dessiner un moulin qui
tourne, il serait intéressant de pouvoir dessiner d'abord le moulin, ensuite de
dessiner les pales de ce moulin, mais il serait fastidieux de redessiner le
dessin par rapport à chaque nouveau tour de boucle (c'est la boucle du temps).
C'est pour ça que nous allons toujours dessiner les 4 pales de la même manière,
c'est le contexte que nous allons modifier. C'est beaucoup plus rapide que de
tout redéfinir manuellement.
Je me rends bien compte que pour l'instant tout cela peut
sembler un peu flou, cependant il est important que vous gardiez en tête ce que
vous venez d'apprendre, nous en aurons vite besoin.
Je pense que si vous devez vous remémorer vos cours sur les
transformations, il y en a trois auxquelles vous devez penser:
rotate (angle)
: la rotation ( le coin supérieur gauche de Canvas est le centre de
rotation )
translate( x, y)
: le déplacement du contexte
scale( x, y)
: l'échelle du contexte (le coin supérieur gauche de Canvas ne bouge pas)
Ces trois transformations s'appliquent uniquement sur le
contexte. Ce qui veut dire que dès que vous aurez appliqué une de ces
transformation, le reste de tout vos dessins en sera affecté. Je pense qu'un
exemple ne serait pas de trop:
context52.strokeStyle="red";
context52.lineWidth=5;
function draw()
{ context52.moveTo(80,10);
context52.lineTo(10,60);
context52.moveTo(10,40);
context52.lineTo(10,60);
context52.lineTo(30,60);
context52.stroke(); }; draw();
context52.translate(75,0); // déplacement à droite context52.scale(0.5,0.5); //
réduction 1/2
draw();
context52.scale(2,2);
// doublement de l'échelle ( retrouve taille
initiale) context52.translate(0,75);
// déplacement vers le bas
context52.rotate(Math.PI/180*90);
// rotation de la zone de 90° horaire draw();
La première flèche est affichée selon la position normale du
contexte. La seconde flèche est translatée de 75 pixels dans la direction x,
ensuite les dimensions du contexte sont réduites de moitié. Voilà ce que cela
donne étape par étape, le fond vert représente le contexte visible dans le
Canvas, les bords noirs représentent le cadre de notre Canvas:
Note: Ce n'est pas parce qu'une partie du contexte n'est
plus visible dans le cadre que celle-ci n'existe plus.
Pour la troisième flèche, les dimensions sont d'abord
multipliées par deux. Ensuite il subit une translation de 75 pixels dans la
direction y avant de subir une rotation de 90°. Voici les différentes étapes:
Je vois que vous avez peur juste en lisant le mot
matricielles, et bien vous avez tort. Ce sont ici simplement deux nouvelles
fonctions que je vous propose. Au lieu d'être des méthodes qui ne modifient
qu'une seule transformation du contexte, avec celles-ci, nous pouvons modifier
l'ensemble des transformations possibles :
transform ( a, b, c ,
d, e, f);
setTransform( a, b,
c, d, e, f);
Je vais d'abord vous expliquer la différence entre les deux
méthodes. Quelles que soient les modifications qui ont été faites,
setTransform()
applique des transformations comme si aucune autre n'avait été faite avant.
Quant à transform(), elle va s'appliquer sur le contexte dans l'état où
il est, donc si il y a déjà eu des modifications du contexte, les
transformations appliquées par transform() vont "continuer"
celles déjà présents.
Maintenant, parlons des lettres, elles représentent pour les
deux méthodes la même transformation:
a: Change l'échelle à l'horizontale
b: Biaise le contexte
à l'horizontale
c: Biaise le contexte à la verticale
d: Change l'échelle à la verticale
e: Déplace le contexte à l'horizontale
f: Déplace le contexte à la verticale
Je pense qu'un exemple serait très utile car je comprends que
cela soit des notions compliquées à comprendre dès la première lecture. Voici
le premier exemple avec un setTransform()
context53.fillStyle="black"; context53.fillRect(0,0,200,150);
context53.setTransform( 1, 0, 0, 1, 0, 0);
// cache le premier !
context53.fillStyle="blue"; context53.fillRect(0,0,200,150);
context53.setTransform( 1, 0, 0, 1, 20, 50);
//décale
+20 à droite, + 50 en bas
context53.fillStyle="red"; context53.fillRect(0,0,200,150);
context53.setTransform( 0.5, 0.5, 0.3, 0.2, 0,
60);
context53.fillStyle="yellow"; context53.fillRect(0,0,200,150);
Il y a en tout 4 rectangles qui sont affichés dans cet
exemple. Le premier est à la position de base cependant celui-ci n'est pas
visible. Cela est du au fait que le second rectangle est par-dessus. Nous
pouvons donc en tirer la conclusion que
setTransform( 1, 0, 0, 1, 0, 0);
est en fait le contexte de base. Le second rectangle est décalé par rapport au
premier. C'est à partir du quatrième rectangle que nous nous rendons compte que
setTransform();
remet le contexte dans sa forme d'origine avant d'y
appliquer des transformations. Nous pouvons le voir avec le déplacement
horizontal. Le contexte du troisième rectangle est déplacé de 50 pixels vers la
droite et 20 vers le bas. Hors le contexte du quatrième contexte n'est pas déplacé à
l'horizontale, comme nous l'indiquons dans ses attributs.
Voici le même exemple mais où je remplace le
setTransform();
par
transform();, vous allez alors comprendre la différence entre ces
deux méthodes:
<canvasid ="cvs54"width ="300"height =
"300">
votre navigateur n'accepte pas Canvas </canvas> <script> var canv54 =document.getElementById('cvs54'); var context54 =canv54.getContext('2d');
context54.fillStyle="black"; context54.fillRect(0,0,200,150);
context54.transform( 1, 0, 0, 1, 0, 0);
// cache le premier !
context54.fillStyle="blue"; context54.fillRect(0,0,200,150);
context54.transform( 1, 0, 0, 1, 20, 50);
//décale
+20 à droite, + 50 en bas
context54.fillStyle="red"; context54.fillRect(0,0,200,150);
context54.transform( 0.5, 0.5, 0.3, 0.2, 0,
60);
context54.fillStyle="yellow"; context54.fillRect(0,0,200,150); </script>
Note: vous pouvez voir que je n'ai modifié aucune
valeur, la seule chose qui a été modifiée est la méthode.
À nouveau nous avons quatre rectangles. Le premier est caché
par le second, nous pouvons donc en conclure que
transform( 1, 0, 0, 1, 0, 0)
est la matrice de base, donc aucune transformation n'est appliquées sur
le contexte. Le second rectangle est lui aussi déplacé de 50 pixels vers la
droite et 20 vers le bas, comme dans l'exemple précédent. Par contre, le quatrième
rectangle n'est pas à la même position que dans l'exemple précédent. Puisque
les modification produient par
transform()
s'ajoutent les unes aux
autres, le quatrième rectangle est déplacé de 100 pixels vers le bas (50 +
60)et de 20 pixels vers la droite (20 +
0).Nous observons donc bien que toutes
les transformations effectuées par
transform()
s'ajoutent les unes aux
autres.
Enfin le dénouement, voilà le moment que vous attendez tous:
les animations. Inutile de vous le préciser, c'est à ce moment que Canvas
dévoile vraiment tout sa puissance.
Nous en avons fini avec les nouvelles méthodes, nous allons
maintenant nous concentrer sur la mise en place de boucles pour animer notre
Canvas.
II.5.1 Théorie sur les animations, récupération du temps
Une animation se passe toujours dans le même ordre:
Une première image est dessinée
Tout est effacé
Une nouvelle image est dessinée, légèrement
modifiée par rapport à la précédente
Tout est effacé
Une nouvelle image est dessinée, légèrement
modifiée par rapport à la précédente
Ainsi de suite
Comme vous le voyez, le principe est assez simple, en fait il
n'y aura qu'une seule chose technique et nouvelle durant ce chapitre, c'est la
façon dont nous allons récupérer le temps. Actuellement, la plupart des écrans
d'ordinateurs affichent une fréquence de 60 images/seconde C'est ainsi que les
animations peuvent sembler fluide pour vous. Depuis quelques années, avec
l'arrivée de la 3D, certains écrans peuvent monter jusqu'à 120 images/seconde,
cela permet d'afficher simultanément deux images (c'est la technique utilisée
pour donner une illusion de 3D). Vous avez peut être déjà entendu parler des
deux méthodes suivantes : setTimeout(); setInterval()
Rappel: Pour utiliser setInterval(), il faut écrire
setInterval(function, intervalle). Pour utiliser
setTimeout() il faut
utiliserla même construction. Il ne
faut pas oublier d'écrire les intervalle en ms, 1000ms = 1seconde.
Ces deux méthode présentent un inconvénient important, imaginons
que nous voulions dessiner plusieurs choses mais pas en même temps. Il est
possible que le temps alloué pour dessiner un dessin soit coupé par le temps
plus court d'un autre dessin. Ce qui implique qu'en utilisant ces fonctions,
certains dessins ne seront pas dessinés par manque de temps. Il existe
aujourd'hui une nouvelle méthode : requestAnimationFrame()
Cette fonction calcule le temps qu'il faut pour dessiner un
nouvel objet, du coup nous pouvons dessiner plusieurs objets, sans qu'ils
soient dessinés simultanément mais pour que l'image soit fluide.
Malheureusement cette fonction va nous poser un problème,
elle n'est actuellement pas standardisée, voilà pourquoi nous allons mettre en
place une petite fonction pour que la méthode soit fonctionne correctement:
Note: L'objet windowest un objet qui
représente une fenêtre ouverte dans lenavigateur.
Comme vous le voyez, j'ai mis en place une fonction de
secours qui permet, dans le cas où le navigateur utilisé n'interprète pas cette
fonction, d'utiliser la fonction setTimeout à une fréquence de 60
images/seconde.
Dans notre première méthode, nous allons créer une balle qui
va rebondir contre les bords de notre Canvas. Le principe est assez simple il
nous faut une fonction (que je nomme animate()) qui va s'occuper de dessiner la
balle. Cette fonction doit effacer l'ensemble du Canvas, elle doit dessiner une
balle par rapport à une position x et y (qui est le centre de la balle).
Ensuite il faut vérifier si la balle n'a pas touché un des bords, si c'est le
cas, il faut inverser la vitesse correspondante au bord touché.
Cette animation a donc cinq variables:
le diamètre de la balle
la position x qui est au centre de la balle (donc le diamètre de la balle divisé par 2)
la position y qui est au centre de la balle (donc le diamètre de la balle divisé par 2)
Mon but, dans cet exemple, est de mettre en place des courbes
de Bézier qui vont automatiquement se dessiner, elles devront disparaître au
bout d'un certain temps et je veux que la coloration des courbes change au fil
du temps. Voilà ce que vous devriez obtenir comme résultat final :
Pour la position je crée deux variables, dernierX et dernierY
qui vont toujours stocker la dernière position à laquelle est allée la courbe,
comme ça la courbe va être suivi. Je mets aussi une variable pour gérer la
couleur. Je vais utiliser le type de couleur hsl (expliqué au chapitre
des
couleurs). Cette variable sera modifiée à chaque nouveau tour de boucle.
Je modifie le type de courbe, d'abord je modifie la méthodelineCap,
pour lui mettre comme valeur round. Ainsi le bout de
la courbe est plus
naturel. Ensuite je modifie la taille des traits. Je mets comme taille minimum
5px, ensuite j'y ajoute un nombre aléatoire qui va jusqu'à 10.
Pour le dessin en lui même, j'utilise le dernier point
comme point de départ, ensuite je modifie ce point; je fais la longueur et
la hauteur que je multiplie par
un nombre aléatoire. Ensuite je trace ma courbe de bézier. Je modifie la
couleur, j'applique une ombre qui part sur le noir et je termine le trait.
Je mets ensuite une autre fonction en place. Celle-ci a pour
but de diminuer petit à petit la valeur des courbes précédentes pour qu'elles
disparaissent entièrement. J'applique donc une transformation de la couleur de la
surface totale. La seule modification que j'effectue est que je mets une valeur
alpha, pour le fond, je le fais en noir.
Puisque j'applique une coloration de transparence sur
l'ensemble du dessin, il faut que avant de dessiner ma courbe, donc au moment
où j'entre dans la fonction qui la dessine, jesauvegarde le contexte. Je restaure le contexte à la fin de cette
fonction.
Note: Dans cet exemple j'utilise deux setInterval,
j'ai dit au préalable que cela posait problème car il était alors possible que
certaines images ne soient pas affichées, ici c'est malheureusement le cas.
Cependant, si j'avais mis deux requestAnimeFrame(), le taux de
rafraichissement aurait été trop élevé, du coup l'animation aurait été trop
rapide. Voilà pourquoi je décide dans ce cas précis d'utiliser deux setInterval.
ATTENTION : vous ne pouvez pas mélanger
setInterval ou setTimeout avec requestAnimeFrame(). Il est impératif de
choisir une seule solution pour l'ensemble des éléments Canvas présents sur une
page.
Voici un nouvel exemple dans lequel nous allons créer un
système solaire. De quoi avons nous besoin pour faire un système solaire ? Nous
avons besoin d'un soleil, de planètes et de satellites. Pourquoi cet exercice
est-il intéressant ? Le fait qu'il y ait un soleil, des planètes et encore des
satellites nous oblige à jongler entre différents niveaux de contexte à
sauvegarder.
Vous pouvez voir que je fais quelque chose de bizarre avec le
contexte et le dessin des planètes et satellites. Je ne les place pas comme ils
se positionnent autour du soleil. Je les place sur un axe, je déplace ensuite
le contexte de manière à ce que le coin supérieur gauche du contexte dans
lequel j'ai dessiné mes objets soit au centre du cadre visible. Je fais cela
car l'angle autour duquel va se produire la rotation est le coin supérieur
gauche du contexte. Place maintenant à ce que vous devriez obtenir:
Note: Je le répète, les planètes et le satellite ne
sont pas placés par rapport à leur position finale ou à la position du soleil.
Note: La variable i sera une variable qui va
s'incrémenter à chaque tour, c'est avec cette variable que nous allons modifier
la rotation du contexte.
Maintenant, la fonction animate(),
effacement du
cadre,
mise en place du fond noir,
dessin du soleil et de la première planète :
Note: Comme vous le voyez, je multiplie la vitesse
des planètes par la valeurs i, cela permet d'avoir une valeur pour
l'angle.
Note: Pour dessiner la première planète, je
travaille dans un contexte différent de celui dans lequel je dessine le soleil
mais je le restaure lorsque j'ai fini de dessiner la planète.
Dessin de la seconde planète et de son satellite.
Incrémentation de i et fin de la fonctionanimate():
Note: J'utilise un save() alors que je n'ai pas
restauré le contexte de base. Il est donc possible d'utiliser plusieurs
sauvegardes et plusieurs restaurations. C'est un système qui est dit "par
empilement". Ce qui veut dire que lorsque j'utiliserestore(),
c'est par rapport au dernier contexte qui a été sauvegardé.
Voici le premier exemple d'une application de Canvas dans un
vrai mini-programme. Celui-ci est une horloge où le but de canvas est
uniquement d'indiquer les heures. Le travail des calculs est effectué par
Javascript. Il reste que c'est un exemple intéressant car il comporte beaucoup
de parties dédiées à Canvas.
Je met en place une seule fonction dont la tâche sera de
toujours effacer le contexte et de tout redessiner dessus. Voici donc le début
de ma fonction:
function horloge(){
var heure =
new
Date(); context59.save();
context59.clearRect( 0,
0, 300,
300);
context59.translate( 150,
150);
context59.rotate(-Math.PI/2);
context59.strokeStyle = "red";
context59.fillStyle = "white";
context59.lineWidth = 5;
context59.lineCap = "square"; // il faut faire Dessin du cadran et des aiguilles
context59.restore() window.requestAnimFrame(function()
{ horloge() } );
} horloge();
L'objet Date() rrenvoie à la valeur du temps au moment où il est créé, voilà pourquoi
je recrée cette variable à chaque fois que j'appelle cette fonction.
Lorsque je fais le dessin des aiguilles je les positionne
toujours par rapport au coin supérieur gauche, voilà pourquoi je déplace le
contexte au centre du cadre et j'applique une rotation de 90° dans le sens
inverse des aiguilles d'une montre. J'effectue la rotation car la position de
base des aiguilles et à 12:00.
Pour le principe des barres pour les heures et les minutes,
je fais un simple trait, je fais une rotation du contexte et je recommence
jusqu'à avoir fait le tour complet du cadran.
// ce code s'insère dans le code décrit ci-dessus (context59)
context60.save();
// Marques des heures
for(var i=0; i<12;
i++){
Pour la boucle des heures, je fais la boucle
tant que i est plus petit que 12. Cela car le premier trait est fait à 0, c'est
la marque qui indiquera 12:00. Ensuite je fais 11 traits de plus. Ce qui au
total fait 12 traits pour les heures et donc est le bon nombre.
La boucle i > for pour les minutes repose sur le même
principe que pour les heures. Je fais cependant cette fois un test à
l'intérieur pour savoir si le nombre i modulo 5 n'est pas égal à 0. Si
c'est le cas, cela signifie qu'il y a déjà un trait qui est dessiné, celui des
heures.
Pour les deux rotations des contextes, c'est 180 divisé par
la moitié du nombre de traits à faire. Siles deux valeurs sont doublées, alors c'est 360 divisé par 12 ou 60,
c'est donc la bonne rotation.
Le résultat que vous obtenez est celui que j'ai moi-même
choisi. Cependant, libre à vous de créer votre propre horloge. Il en est de
même pour les aiguilles que nous allons tout de suite dessiner.
La première chose à faire, c'est d'obtenir l'heure, la minute
et la seconde précise, c'est ici que nous allons utiliser l'objet
Date():
Je pense que la construction de la 4ème ligne soulève quelques questions. Je vais
traduire cette ligne en français. hr est égal à hr si il n'est pas plus grand
ou égal à 12, sinon il faut soustraire 12 de hr. Je fais cela car,
contrairement aux minutes et aux secondes, il n'y a pas 24 traits. Du coup, dès
qu'il 13:00, alors l'heure est réduite de 12, donc il ne reste que 1.
var sec = heure.getSeconds(); // la
varaible heure est définie au début du programme var min = heure.getMinutes();
var hr = heure.getHours(); hr = hr>=12
? hr-12 : hr;
Pour afficher la position des éguilles, 'additionne le temps qui s'est déjà
écoulé en minutes et en secondes pour les heures et juste en secondes pour les
minute. Je divise cependant la valeur de PI par un nombre différent qu'il y a
de secondes dans une minutes et de minutes dans une heure. Pour 360 j'ai fais 6
(la moitié des heures) fois 60 (nombre de minutes dans 1 heure). Pour 21600
j'ai fais 360 (nombre de minutes dans 6 heure) fois 60 (nombre de seconde dans
1 minutes). Je pense que du coup, je n'ai pas besoin de vous expliquer d'où
vient le 1800..
Ceci est un embryon de programme. C'est juste pour vous
montrer un domaine d'application un peu scientifique dans lequel Canvas peut se
montrer extrêmement puissant.
Dans cet exemple, nous permettons à l'utilisateur de donner
les coefficients d'une courbe, ensuite nous la dessinons, mais nous dessinons
aussi sa dérivée et sa dérivée seconde.
Je ne vais cependant pas vous présenter du code. Je fais cela
car il est long et répétitif, de plus il est d'un niveau qui peut parfois être
assez complexe, voilà pourquoi je vais vous donner une présentation théorique
de son fonctionnement. Je vous rappel que vous pouvez retrouver l'exemple avec
le code complet.
III.2.1 Dessin des axes et mise en place de l'interface
La première étape est la mise en place de l'axe. Voici celui
que j'utilise (voir //Dessin de l'axe).
Cet axe, bien que simple, permet de facilement représenter
n'importe quelle droite ou courbe. Par ailleurs, avec les marques présentes sur
les axes, il est facile pour n'importe qui de voir les valeurs. Il faudra
cependant faire très attention avec cette question d'échelle. Il faudra que
l'échelle pour les courbes et les droites soit la même.
<canvas id="cvs" width="700" height="700" style="border:1px solid
#9E9E9E; float:left;"> Désolé, votre navigateur ne supporte pas
Canvas. Mettez-vous à jour </canvas> <button onClick="f1+=1;
MAJ();">Afficher F(x)</button>
// mise en place des champs boutons <button onClick="f2+=1;
MAJ();">Afficher F'(x)</button> <button onClick="f3+=1;
MAJ();">Afficher F''(x)</button> <input type="text" value="0"
id="x6">* x6//
mise en place des champs boutons <input type="text" value="0"
id="x5">* x5 <input type="text" value="0"
id="x4">* x4 <input type="text" value="1"
id="x3">* x3 <input type="text" value="4" id="x2">* x2
<input type="text" value="3" id="x1">* x1 <input
type="text" value="2" id="x0">* x0 <button
onClick="MAJ();">Modifier</button> <script> var
cvs = document.querySelector('#cvs'); var ctx =
cvs.getContext('2d'); var f1=0; var f2=0; var f3=0; MAJ(); function MAJ(){
// lire les valeurs contenue dans les
champs texte x6 =
document.getElementById('x6').value; x5 =
document.getElementById('x5').value; x4 =
document.getElementById('x4').value; x3 =
document.getElementById('x3').value; x2 =
document.getElementById('x2').value; x1 =
document.getElementById('x1').value; x0 =
document.getElementById('x0').value; ctx.clearRect(0, 0,
cvs.width, cvs.height); axe(); draw(); }
function axe(){ //Dessin de l'axe ctx.strokeStyle
="rgb(0,0,0)"; ctx.beginPath(); ctx.moveTo(0,350);
ctx.lineTo(700,350); ctx.stroke(); ctx.beginPath();
ctx.moveTo(350,0); ctx.lineTo(350,700); ctx.stroke(); var a
= 700/20; for(var i = 0; i<20;i++) { ctx.beginPath();
ctx.moveTo(a*i ,345); ctx.lineTo(a*i ,355) ctx.stroke();
ctx.beginPath(); ctx.moveTo(345 ,a*i); ctx.lineTo(355 ,a*i)
ctx.stroke(); } } //
fin la fonction axe function draw(){
//Dessin de la fonction if(f1 > 1){ f1 = 0; } if(f2 >
1){ f2 = 0; } if(f3 > 1){ f3 = 0; } if(f1
== 1){
//affiche la courbe ctx.strokeStyle = "rgb(0, 0, 0)"; ctx.beginPath();
ctx.moveTo(0, 0); for(var x=0;x<700;x++){ var a = (x -
350)/40; y = (350 -
(x6*Math.pow(a,6)+x5*Math.pow(a,5)+x4*Math.pow(a,4)+x3*Math.pow(a,3)+x2*Math.pow(a,2)+x1*Math.pow(a,1)+x0*Math.pow(a,0))*40);
ctx.lineTo(x,y); x = x-0.9; }
// fin for ctx.stroke(); }
// fin if if(f2 == 1){
//affiche la 1ere dérivée de la courbe ctx.strokeStyle = "rgb(0, 0, 100)";
ctx.beginPath(); ctx.moveTo(0, 0); for(var
x=0.0;x<700;x++){ var a = (x - 350)/40; y = (350 -
(x6*6*Math.pow(a,5)+x5*5*Math.pow(a,4)+x4*4*Math.pow(a,3)+x3*3*Math.pow(a,2)+x2*2*Math.pow(a,1)+x1*1)*40); ctx.lineTo(x,y); if(Math.round(y)-350 < 1){
if(Math.round(y)-350 > -1){ ctx.moveTo(x,0);
ctx.lineTo(x,700); } } x = x -0.9; }
// fin for
ctx.stroke(); } // fin if
f2 if(f3 == 1){
//affiche la seconde dérivée de la courbe ctx.strokeStyle = "rgb(0,
100, 0)"; ctx.beginPath(); ctx.moveTo(0, 0); for(var
x=0.0;x<700;x++){ var a = (x - 350)/40; y = (350 -
(x6*6*5*Math.pow(a,4)+x5*5*4*Math.pow(a,3)+x4*4*3*Math.pow(a,2)+x3*3*2*Math.pow(a,1)+x2*2)*40); ctx.lineTo(x,y); if(y-350 < 1){ if(y-350 > -1){ ctx.moveTo(x,0); ctx.lineTo(x,700); } } x = x-0.9;
} ctx.stroke(); }
// fin if f3 } /// fin de la fonction
draw </script>
* x6
* x5
* x4
* x3
* x2
* x1
* x0
il faut ajouter un système de boutons (
voir
// mise en place des champs boutons)
et de cases
(// mise en place des
champs texte)
pour que l'utilisateur puisse utiliser la fonction qu'il souhaite. Ces
boutons et ces champs ne sont pas dans le script, mais directement sur la page
html. Les trois premiers boutons ont pour but d'afficher ou non les
courbes et les dérivées. Dans le code j'utilise simplement une boucle
conditionnelle à chaque fois que je veux redessiner le tout. Les sept cases qui
suivent vont permettre à la personne qui utilise cette application de choisir
les coefficients de xn.
Dans mon exemple la mise à jour se fait avec le bouton
"modifier". Il serait par contre, tout à fait possible de faire en
sorte que la mise à jour de l'affichage se fasse dès que l'un des chiffres dans
l'une des cases est modifié.
La boucle principale, celle qui va dessiner ou non les
courbes, est appelée (MAJ() ) dès que l'utilisateur appuie sur le bouton
"modifier" ou sur l'une des trois premier boutons. Nous allons
d'ailleurs nous attaquer maintenant à la boucle principale.
Bien, nous avons déjà placé les bases avant. Nous avons une
boucle principale, nous avons trois variables pour savoir si il faut dessiner
ou non les courbes, ensuite nous avons sept variables qui stockent nos sept
coefficients pour xn. Donc la première chose à faire dans notre boucle principal
est de récupérer toutes nos variables. Une fois celle-ci récupérées, nous
pouvons effectuer des tests pour savoir si il faut afficher ou non nos droites.
Si c'est le cas, alors il faut dessiner.
Pour le tracé des droites je commence toujours par le point
(0,0). Pour dessiner, j'utilise une boucle de type for(). Elle va
s'effectuer sur tous les x qui sont égaux à la largeur de notre canvas.
Cependant, il est impossible d'utiliser la valeur de x telle quelle, il va
falloir la transformer à chaque tour de boucle. Vous vous demandez peut être
pourquoi il faut faire cela, c'est pour deux raisons.
La première est la question de l'échelle vue auparavant. Si
par exemple je choisis d'avoir 20 unités dans mon axe, il va falloir que je
divise x par le double du nombre d'unités, ce qui fait 40 si vous prenez 20
unités.
La seconde raison est due au point de départ de la droite,
rappelez-vous j'ai choisi (0,0), hors ce point ne correspond par au centre de
l'axe, mais au coin supérieur gauche. Hors la première valeur de x est 0. Il
faut donc soustraire à x la moitié de la largeur du canvas. Il faudra effectuer
les mêmes modifications sur le point y.
C'est ainsi que je dessine la fonction f(x). J'apporte une
petite modification pour les deux dérivées. J'effectue dans une boucle un test
pour savoir si la valeur de y est égale à 0. Si c'est le cas alors je dessine
une droite verticale à la position x du point où y est égal à 0. Ainsi je
permets à l'utilisateur de voir visuellement ce que représente les zéros des
dérivées par rapport à la fonction de base. Au final, en cliquant sur le bouton
"afficher F(x)", vous pouvez visualiser la fonction x3+4x2+3x+2
En en cliquant sur le bouton
"afficher F'(x)" , vous pouvez voir qu'il y a
deux droites horizontales qui correspondant aux deux maximums et minimums de la
fonction x3+4x2+3x+2.
Ceci n'est pas la méthode optimal pour dessiner les
fonctions, cette méthode est très limitées et il est impossible d'y inclure des
racines ou des fonctions trigonométrique. Il ne faut pas oublier qu'il s'agit
simplement d'une introduction et pas d'une méthode complète et optimal.
Maintenant faites comme moi, appliquez vos acquis pour mener
à bien un projet de plus grande envergure. Il reste encore beaucoup de domaines
à mettre en place avec Canvas. L'utilisation de Canvas pour créer des exemples
physiques peut être quelque chose de très éducatif. Si vous êtes quelqu'un de
plus artistique, cherchez un peu sur Internet, vous trouverez beaucoup
d'exemples d'animations possibles avec Canvas. Ce que je vous ai montré durant
ce cours n'est qu'un échantillon. Faites vos propres expériences et vos propres
tests.
Un autre domaine dans lequel vous pouvez travailler avec
Canvas est la 3D. Au lieu de travailler uniquement avec Javascript, il va
falloir travailler avec une API, WebGL. Cette API est tirée de la bibliothèque
OpenGL. Depuis quelque temps, de vrai chefs-d'œuvre commencent à apparaître sur
le net. Il y a même des jeux vidéos qui commencent à voir le jour, pour
l'instant très simples.
Il est même possible que cette façon de créer des univers 2D ou 3D pourrait
remplacer l'actuel Flash. Cela pourrait changer le visage du mini-jeux sur
Internet. Mais nous n'y sommes pas encore, il y a encore beaucoup à faire avec
Canvas et je vous conseille de vous intéresser à cette nouvelle balise qui
pourrait d'ici quelques années devenir le standard du nouveau Web.
Crée un rectangle. Le coin supérieur gauche est défini
par posX et posY. Ses dimensions sont ensuite définies par les
paramètres largeur et hauteur.
fillRect( posX, posY, largeur, hauteur)
Dessine un rectangle plein. Le coin supérieur gauche est
défini par posX et posY. Ses dimensions sont ensuite définies
par les paramètres largeur et hauteur.
strokeRect( posX, posY, largeur, hauteur)
Dessine uniquement les contours d'un rectangle. Le coin
supérieur gauche est défini par posX et posY. Ses dimensions
sont ensuite définies par les paramètres largeur et hauteur.
clearRect( posX, posY, largeur, hauteur)
Nettoie une zone spécifique de Canvas avec un rectangle.
Le coin supérieur gauche est défini par posX et posY. Ses
dimensions sont ensuite définies par les paramètre largeur et hauteur.
Couleurs,
Style et Ombre
Méthode
Description
createLinearGradient( posX0,
posY0, posX1, posY1)
Crée un dégradé linéaire
suivant une droite. Le point de départ est défini par les coordonnées posX0
et posY0. Le point d'arrivée de la droite est défini par posX1
et posY1.
Crée un dégradé radial entre
deux cercles. Le centre du premier cercle est défini par posX0 et posY0,
son rayon est défini par r0. Le centre du second cercle est défini par
posX1 et posY1, son rayon est défini par r1.
addColorStop(stop,
couleur)
Ajoute une couleur à un
dégradé et un endroit où elle doit s'arrêter. Le paramètre stop est
une valeur entre 0 et 1 qui représente la position entre le départ et la fin
du dégradé. Le paramètre couleur définit la couleur à atteindre au
point.
createPattern(image, repetition)
Répète un élément spécifique
dans une certaine direction. Le paramètre image est l'objet qui
contient l'image à répéter. Le paramètre repetition définit le mode de répétition.
Propriété
Description
fillStyle
Attribue la couleur pour
remplir le dessin.
strokeStyle
Attribue la couleur pour
tracer le dessin.
shadowColor
Attribue la couleur pour
l'ombre.
shadowBlur
Attribue la dissipation de
l'ombre.
shadowOffsetX
Attribue la distance horizontale
de l'ombre depuis l'objet qui contient l'ombre.
shadowOffsetY
Attribue la distance verticale
de l'ombre depuis l'objet qui contient l'ombre.
Composition
Propriété
Description
globalAlpha
Attribue une transparence sur
l'ensemble du contexte
globalCompositionOperation
Modifie la composition globale
du contexte et définit l'ordre dans lequel les éléments sont affichés
(superposition).
Traits
Méthode
Description
beginPath()
Commence un nouveau tracé.
moveTo( posX, posY)
Se déplace au point donné par posX et posY sans tracer de traits.
lineTo(posX,
posY)
Se déplace au point donné par posX et posY en traçant un trait.
arcTo( posX0, posY0, posX1, posY1,
r)
Dessine un arc de cercle entre deux droites. Le point de
départ est défini par posX0 et posY0. Son point d'arrivée est
défini par posX1 et posY1. Son rayon est défini par r.
arc( posX, posY, r, angle0, angle1,sens)
Dessine un cercle ou un arc de cercle. Son centre est
défini par posX et posY. Le rayon est défini par r.
L'angle de départ est défini par angle0 et celui d'arrivée par angle1.
Le paramètre sens définit dans quel sens sera dessiné le cercle.
quadraticCurveTo( pX, pY, posX, posY)
Dessine une courbe quadratique en partant du dernier
point atteint. Les paramètres pX et pY définissent le point
vers lequel la courbe va tendre. Le point d'arrivée de la courbes est défini
par posX et posY.
bezierCurveTo( p0X, p0Y, p1X,p1Y,
posX, posY)
Dessine une courbe de Bézier en partant du dernier point
atteint. Les paramètres p0X et p0Y définissent le première
point vers lequel la courbe va tendre, le second point vers lequel la courbe
va tendre est défini par p1X et p1Y. Le point d'arrivée de la
courbes est défini par posX et posY.
closePath()
Dessine un trait entre le dernier point et le premier
(permet de fermer une forme).
fill()
Remplit le dessin créé avec le tracé.
stroke()
Dessine le tracé qui a été fait.
clip()
Définit une seule zone dans laquelle les dessins faits
seront visibles.
isPointInPath()
Renvoie la valeur true si le point est situé dans
le tracé en cours.
Style des traits
Propriété
Description
lineCap
Attribue le style de l'extrémité d'un trait.
lineJoin
Attribue le style de coin entre deux droites.
lineWidth
Attribue la largeur des traits.
miterLimit
Attribue la longueur maximum du style de coin miter.
Texte
Méthode
Description
fillText( texte, posX, posY, longeurMax)
Dessine un texte plein. Le paramètre texte contient
le texte à afficher. Il sera positionné par rapport à posX et posY.
Le dernier paramètre est optionnel, il permet de définir la longueur maximum
allouée en pixel au texte.
strokeText( texte, posX, posY, longeurMax)
Dessine les contours d'un texte. Le paramètre texte contient
le texte à afficher. Il sera positionné par rapport à posX et posY.
Le dernier paramètre est optionnel, il permet de définir la longueur maximum
allouée en pixel au texte.
measureText(texte)
Retourne un objet qui contient la largeur du texte
spécifié par le paramètre texte.
Dessine une image qui est donnée par le paramètre image.
Le coin supérieur gauche de l'image est défini par posX et posY.
Il est possible de modifier ses dimensions avec les paramètres largeur et
hauteur. Il est aussi possible de ne sélectionner qu'une partie de
l'image donnée en paramètre. Pour ce faire il faut définir le point de départ
de l'image prise par coupeX et coupeY. Les dimensions de cette
sélection sont définies par coupeLargeur et coupeHauteur.
Transformations
Méthode
Description
translate( x, y)
Translate le contexte par rapport aux paramètres donnés
par x et y.
rotate( angle)
Tourne le contexte selon l'angle donné par angle.
scale( echelleX, echelleY)
Modifie l'échelle du contexte selon echelleX et echelleY.
Remet la matrice de base du contexte avant d'appliquer
les transformations comme un transform(). Une translation est
effectuée par les paramètres x et y. Un changement d'échelle
est fait par echelleX et echelleY. Les paramètres deviationVerticale
et deviationHorizontale vont déformer le contexte.
Applique des transformations sur le contexte. Une
translation est effectuée par les paramètres x et y. Un
changement d'échelle est fait par echelleX et echelleY. Les
paramètres deviationVerticale et deviationHorizontale vont
déformer le contexte.
Manipulation des pixels
Méthode
Description
createImageData( largeur, hauteur, imageData)
Crée un nouvel objet ImageData vide. Elle fonctionne de
deux manières différentes, soit il faut utiliser les paramètres largeur
et hauteur, soit elle prend les dimensions d'un autre objet du même
type donné par le paramètre imageData.
getImageData( posX, posY, largeur, hauteur)
Retourne un objet ImageData qui copie les pixels pour
une zone spécifiée du contexte. Le coin supérieur gauche de cette zone a pour
coordonnées posX et posY. Ses dimensions sont données par largeur
et hauteur.
putImageData( imageData, posX, posY)
Applique les informations de l'objet ImageData dans le
contexte. Le paramètre imageData contient l'objet dont il faut
appliquer les informations. Les coordonnées du coin supérieur gauche où est
appliqué l'objet sont définis par posX et posY.
Propriété
Description
width
Retourne la largeur de l'objet ImageData.
height
Retourne la hauteur de l'objet ImageData.
data
Retourne un objet qui contient les informations de
l'objet ImageData.
Méthode
Description
save()
Sauvegarde l'état du contexte en cours d'utilisation.
restore()
Retourne la dernière version sauvegardée du contexte.
getContext('dimension')
Accède au contexte de l'objet Canvas. Les dimensions du
contexte sont à définir par le paramètre dimension.
Webographie
Le site w3schools est un site à connaître et à visiter
régulièrement. Il présente l'ensemble des des fonctions, méthodes et autres
propriétés des différents langages pour le web. Il est très utile car il
informe lorsqu'il y a des problèmes de compatibilité avec les différents
navigateurs.
Un article de Alvaris Falcon qui présente un ensemble
d'applications de canvas ou d'animations très abouties. Site en anglais mais
facile à comprendre:
Le tutoriel donné ici est écrit par idsquare, il est
très complet et présente des exemples faciles à comprendre. Il n'aborde
malheureusement pas les animations. Le site en lui-même est très intéressant
car il présente un large choix de tutoriels pour la création de sites web.
Le site toulibre offre un ensemble d'exemples avec le
code. Il manque peut être de texte mais si les bases de Canvas sont acquises
alors l'ensemble est très facile à comprendre.
L'exemple présent sur braincracking est un exemple que
je présente moi-même (j'ai apporté quelques modification à ma version) dans le
chapitre sur les animations.
Open Class Room anciennement le site du zero est
un site qui propose énormément de tutoriels très complets sur tout ce qui
touche à l'informatique. Je recommande ce site à toutes les personnes qui
souhaitent commencer avec des bases solides. De plus il a une communauté très
active.
Le site la ferme du web propose un tutoriel sur
Canvas, bien que court, il contient une page très utile avec l'ensemble des
méthodes et des propriétés propres à Canvas.