Fabric.js est une bibliothèque JavaScript qui facilite la création graphique dans un élément HTML <canvas>
.
Recopiez le code suivant, dans un fichier index.html qui sera votre page web initiale :
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title>Premier Test FABRIC</title>
</head>
<body>
<div>
<canvas id="c1" style="border:1px solid #000000;"/>
</div>
<div>
<button id="b1">action</button>
</div>
<script type='text/javascript' src='fabric.min.js'></script>
<script type='text/javascript' src='test1.js'></script>
</body>
</html>
Vous aurez besoin de 2 fichiers JavaScript :
La première ligne de votre fichier test1.js doit être :
const canvas = this.__canvas = new fabric.Canvas('c1');
Vous créez ici la constante JavaScript canvas
qui représente un canvas dynamique : les objets graphiques que vous y créez sont mobiles et redimensionnables. Lorsque vous souhaitez créer un canvas statique, votre code devient :
const canvas = this.__canvas = new fabric.StaticCanvas('c1');
L’opérateur new
vous a en fait permis de créer un objet. canvas
est donc un objet JavaScript composé d’attributs (members) et de méthodes (methods)
La documentation relative à cet objet est accessible ici.
En JavaScript, il est possible de définir des objets de différentes manières.
On peut créer des classes qui permettent de définir un modèle. On peut alors créer des objets de cette classe à l’aide de l’opérateur new
Le code new fabric.Canvas('c1')
permet donc de créer un objet de la classe Canvas
. Cette classe est définie dans la bibliothèque Fabric.js.
Un Initialisateur d’objet est une liste entre accolades d’expressions du type clé:valeur
séparées par des virgules.
On peut par exemple définir l’objet o1
suivant :
const o1 = {
x:10,
y:50,
nom:'objet1'
}
On peut manipuler cet objet et accéder à ses différentes propriétés à l’aide de l’opérateur point :
// afficher ses propriétés
console.log('o1=',o1);
// afficher une propriété
console.log('x=',o1.x);
// modifier une propriété
o1.x += 5;
console.log('x=',o1.x);
// ajouter une propriété
o1.z = -20;
console.log('z=',o1.z);
Un objet peut posséder des fonctions (méthodes) :
const o2 = {
x:10,
y:51,
superficie:function(){
return o2.x * o2.y
}
};
console.log("s=",o2.superficie());
La fonction superficie
utilise les propriétés x
et y
de l’objet o2
. Comme cette fonction est elle-même définie dans l’objet o2
, on peut utiliser le mot-clé this
au lieu de reprendre le nom de l’objet. Le code devient alors :
const o2 = {
x:10,
y:51,
superficie:function(){
return this.x * this.y
}
};
console.log("s=",o2.superficie());
L’opérateur d’affectation =
ne permet pas de créer un nouvel objet. Testez le code suivant :
const o2 = {
x:10,
y:51,
superficie:function(){
return this.x * this.y
}
};
const o3=o2;
o2.x=25;
console.log("x=",o3.x);
Vous observez que o2
et o3
sont le même objet.
Pour recopier un objet et en créer un clone, vous pouvez utiliser l’opérateur de décomposition (spread) ...
Modifiez la deuxième partie du code précédent comme suit :
const o3={...o2};
o2.x=25;
console.log("x=",o3.x);
Vous pouvez maintenant observer que o2
et o3
sont bien deux objets différents.
Il n’est pas correct de comparer deux objets à l’aide de l’opérateur classique de comparaison ==
ou ===
. Le test ne retournera true
que si on compare un objet à lui même :
const o3={...o2};
const o4=o3;
console.log("o2=o3:",o2===o3); // false
console.log("o2=o4:",o2===o4); // false
console.log("o3=o4:",o3===o4); // true
La constante canvas
étant un objet, vous savez maintenant comment manipuler cet objet, et accéder à ses différentes propriétés. Vous savez par exemple afficher sa largeur (propriété width
) :
console.log("largeur du canvas : ",canvas.width);
Pour effectuer ce qui est demandé ci-dessous, vous aurez sans doute besoin de la documentation concernant l’objet canvas
backgroundColor
).setWidth
ou setDimensions
La bibliothèque Fabric.js met à votre disposition un large panel de classes, ce qui vous permet de générer aisément des objets qui représentent des formes de base.
Parmi ces formes de base, on trouve :
Chacune des classes correspondantes permet de créer des objets et de les modeler en utilisant les méthodes définies sur leur classe.
Pour créer un rectangle, on procédez comme suit :
// créer un objet
const rect1 = new fabric.Rect({
left: 100,
top: 100,
fill: 'red',
width: 20,
height: 20
});
// et l'ajouter au canvas
canvas.add(rect1);
Remarquez que pour créer notre rectangle, nous lui passons un objet qui défini ses dimension, sa position etc. Ajoutez la propriété angle:45
à votre objet et observez ce qui se produit.
Si vous observez votre page web, vous observez la présence d’un bouton action. Le code HTML vous permet de connaitre son id : b1
.
Le code qui suit vous permet de déclencher l’exécution d’une fonction anonyme lors d’un évènement click sur ce bouton :
document.getElementById("b1").onclick = () => {
console.log("click");
};
Pour faire pivoter le rectangle à la demande, on peut définir notre fonction anonyme comme suit :
document.getElementById("b1").onclick = () => {
rect1.rotate(rect1.angle +30); // ajouter 30° à l'angle du rectangle
canvas.renderAll();
};
La méthode rotate
définie dans la classe Rect
permet de définir l’angle de notre rectangle. La méthode renderAll()
définie dans la classe Canvas
permet de réafficher les objets graphiques du canvas.
Pour faire avancer le rectangle, il suffit de modifie sa propriété left
, qui définit l’abscisse de son angle supéreur gauche :
document.getElementById("b1").onclick = () => {
rect1.set('left', rect1.left+10);
canvas.renderAll();
};
Lorsque le rectangle dépasse la limite droite du canvas, il disparaît. Modifiez ce code pour que dans ce cas, le rectangle réapparaisse sur le bord gauche du canvas.
Ajouter des boutons “b2”, “b3”, et “b4” pour pouvoir déplacer le rectangle vers la droite, vers le haut, vers le bas, toujours en le faisant réapparaître sur le bord opposé lorsqu’il sort du cadre. Pour cela, dupliquez le code qui gère l’évènement sur le bouton “b1” et adaptez-le à chaque cas.
On souhaite maintenant que le rectangle change de couleur à chaque click sur l’un des boutons : d’abord rouge, le rectangle devient bleu, puis jaune, puis redevient bleu, etc. Créez un tableau global avec les trois couleurs puis, dans chacune des quatre fonctions anomymes, ajoutez les lignes de code qui gèrent le changement de couleur.
Pour effectuer le changement de couleur, le même code a été rajouté aux 4 fonctions anonymes. Créez une fonction nouvelle\_couleur
qui prend en argument un tableau t
qui permet de gérer le changement de couleur. Utilisez cette fonction dans chacun,e des quatre fonctions anonymes.
On remarque que le code est presque le même pour la gestion de ces quatre évènements. Ecrivez une fonction deplacer
qui prend en argument
un objet Rect,
une chaine de caractère représentant un id de bouton,
un entier représentant un nombre de pixels et qui gère les mouvements du rectangle dans les quatre directions. Ainsi, les quatre instructions permettant de gérer les clicks s’écrivent sur le modèle suivant :
document.getElementById("b_droite").onclick = () => {
deplacer(rect1,'b_droite',10);
};
Remarquez que l’évènement onclick se voit affecter une fonction anonyme qui exécute une fonction. Testez votre code en affectant directement la fonction deplacer
à l’évènement onclick, et vérifiez que cela ne fonctionne pas.
Pour que cela fonctionne, modifiez votre fonction deplacer
de sorte qu’elle retourne une fonction qui contient le code de la fonction deplacer
. Le code permettant de gérer chacun des 4 évènements s’écrit maintenant selon le modèle suivant :
document.getElementById("b_droite").onclick = deplacer(rect1, 'b_droite', 10);
Avant d’écrire votre code, il vous est très vivement conseiller de lire d’abord le paragraphe suivant, de tester le code qui y est présenté et de tenter de comprendre ce qui se passe.
On constate maintenant que les quatres lignes permettant de gérer les quatre évènements sont quasi identiques. Pour éviter d’écrire quatre fois le même code, créez un tableau contenant les quatre identifiants de boutons, et écrivez une boucle qui parcours ce tableau pour générer les gestions d’évènements document.getElementById(....)
.
Pour les étudiants plus téméraires : vous pouvez tenter d’écrire une fonction gererClicks(id)
qui retourne une fonction qui effectue la gestion des évènements. id
est une chaine de caractères représentant un id de bouton. En appliquant la méthode forEach au tableau créé prédemment, on génère les quatre gestions d’évènements :
["b_droite","b_gauche","b_bas","b_haut"].forEach(id=>gererClicks(id)());
Pour les étudiants plus téméraires et qui auraient réussi l’exércice précédent : modifiez le tableau d’identifiants en créant un tableau d’objets qui donne tous les éléments nécessaires à la gestion des évènements :
const t1 = [
{rect: rect1, id: "b_droite", nbPixels: 10},
{rect: rect1, id: "b_gauche", nbPixels: 10},
{rect: rect1, id: "b_bas", nbPixels: 10},
{rect: rect1, id: "b_haut", nbPixels: 10}
];
puis adaptez la fonction gererClicks
et la boucle forEach
.
En JavaScript, il est assez courant d’écrire des fonctions qui retournent des fonctions. Par exemple, le code ci-dessous :
function f1(x){
return function(){
return x+1;
}
}
Permet de déclarer une fonction f1 qui retourne une fonction. En exécutant le code suivant :
console.log("test1:", f1(5));
On constate que la valeur retournée par f1
est bien une fonction.
Pour obtenir un résultat, on peut écrire le code suivant :
const f2 = f1(5);
console.log("test2",f2());
f2
est bien une fonction, et lorsqu’on l’exécute, on obtient bien une valeur numérique. Une écriture plus rammassée est également possible :
console.log("test3:", f1(5)());