Statique
Comment demarrer
Comme je vous le disait, je vais vous fournir un projet p5-espress qui est juste un projet p5.js avec quelques pré-configurations supplémentaires.
Projet : p5-espress.
En l'ouvrant vous remarquerez que ça ressemble à un projet web comme ce que vous connaissez.
Dans l'index.html on a :
<body>
<main></main>
<script id="p5" src="libs/p5.js"></script>
<script src="script.js"></script>
<script src="libs/espress.js"></script>
</body>
Ce qu'il faut savoir c'est que, oui l'élément <main> est vide mais p5.js va y insérer un <canvas>, qui est un élément sur lequel on peut dessiner grace à du javascript.
Aussi, p5.js va créer tout un tas de fonctions que l'on pourra utiliser pour dessiner.
Votre code est un fichier javascript externe, vous n'avez pas besoin de toucher à l'HTML pour le moment donc autant ne pas le voir et travailler uniquement dans script.js.
Vous pouvez avoir plusieurs fichier de javascript dans votre projet et remplacer le chemin pour exécuter un de vos script
En remplacant ceci
<script src="script.js"></script>
comme ça:
<script src="monAutreScript.js"></script>
Et pour finir, espress repasse après p5.js pour changer quelques éléments et de configuration mais surtourt, il contient la ligne draw() et c'est la seule contrainte "magique" que je n'ai pas pu gommer. Donc votre code qui sert à dessiner est à mettre dans la fonction draw(), qui a l'obligation de se nommer comme ça et qui sera exécutée automatiquement au chargement de la page.
Il vous suffit de démarrer votre liveServer comme d'habitude et de vous concentrer uniquement sur le fichier script.js.
Vous saurez que ça marche si vous voyez un cercle blanc sur fond blanc quand vous ouvrez le projet dans le navigateur.
Travailler avec p5 est plus primitif qu'HTML et CSS, c'est donc plus simple, plus libre, mais aussi demande plus de travail pour arriver au mêeme résultat.
Tout marche et on sait lire le code que l'on a ... ou presque.
circle(), ni par nous, ni par le navigateur, mais par p5 et donc si l'on travail sans p5 plus tard elle n'existera pas. C'est une fonction pour laquelle il faut mettre trois nombres entres ses paranthèses quand on l'exécute.
- Le premier indique la position du centre du cercle sur l'axe X
- le deuxième indique la position du centre du cercle sur l'axe Y
- le troisième indique le diamètre du cercle
Vous remarquerez que le cercle est en haut à gauche de l'espace de dessin. C'est parce que le point (0,0) est le coin en haut à gauche.
On en a parlé brièvement mais les variables sont des noms auxquels sont associé une valeur.
Pour en créer une, il faut utiliser le mot clef let ou const comme ceci
let myVariable;
Et souvent on lui donne une valeur de départ comme ceci
let myVariable = 42;
Si on utilise le mot clef const plutôt que let, comme ceci:
const myVariable = 42;
...alors on aura une erreur si plus tard dans le programme on essaye d'y assigner une nouvelle valeur.
C'est pratique pour nommer nos valeurs et rendre notre code plus lisible. Par exemple, je peux reprendre notre code qui dessine un cercle et l'écrire comme ceci:
function draw() {
let diameter = 100;
circle(50, 50, diameter);
}
et comme ça je peux facilement me rappeler que la troisième valeur est le diamètre et non le rayon.
Mais ça ne s'arrête pas là!
C'est aussi (et surtout) utile pour avoir des lignes de code qui partagent la même valeur. Si je fait ceci, alors j'aurais la garantie que mes cercles seront toujours côte à côte.
function draw() {
let diameter = 100;
circle(50 + 0 * diameter, 50, diameter);
circle(50 + 1 * diameter, 50, diameter);
circle(50 + 2 * diameter, 50, diameter);
circle(50 + 3 * diameter, 50, diameter);
}
Et d'ailleurs, je peux même changer la valeur de ma variable diamètre et ça resterait vrai puisque tout le reste du dessin en dépend.
Pour nous aider, p5 créer une varibable width et une variable height.
widthcontient la largeur de l'espace de dessin, en pixelsheightcontient la hauteur de l'espace de dessin, en pixels
Donc si j'écris ce code
function draw() {
let diameter = 100;
circle(50, 50, diameter);
circle(width - 50, 50, diameter);
circle(50, height - 50, diameter);
circle(width - 50, height - 50, diameter);
}
J'aurai un dessin ressemblant à ceci :
Exercice
Écrivez un progamme qui met un cercle au millieu de l'écran
Solution
function draw() {
circle(width / 2, height / 2, 100);
}
Exercice
Écriver un programme qui place 4 cercles à partir du centre comme ci-dessous.
Votre code doit garantir que les cercles sont tous à la même distance du centre. Vous êtes libres de choisir une valeur pour cette distance.
Solution
function draw() {
let diameter = 100;
let distance = 150;
circle(width / 2 + distance, height / 2, diameter);
circle(width / 2 - distance, height / 2, diameter);
circle(width / 2, height / 2 + distance, diameter);
circle(width / 2, height / 2 - distance, diameter);
}
Scope
Une variable a ce que l'on appelle un "scope" (qui est un mot anglais mais je trouve que "portée" n'aide pas franchement plus à comprendre).
C'est la partie du programme dans laquelle la variable est valide. Autrement dit, si j'essaye de me servir d'une variable hors de son scope alors elle n'existera pas.
Une variable existe à partir du moment où on la créée avec let ou const et existe jusqu'à ce qu'on finisse le bloc de code qui l'englobe. Un bloc étant délimité par une paire d'accolades {}.
Donc dans la réponse au dernier exercice, diameter et distance sont créées au début de la fonction draw() et n'existe que dans la fonction draw(). On peut essayer avec plusieurs console.log() pour essayer d'afficher diameter à plusieurs endroits.
function draw() {
console.log("test 1");
console.log(diameter);
let diameter = 100;
let distance = 150;
console.log("test 2");
console.log(diameter);
circle(width / 2 + distance, height / 2, diameter);
circle(width / 2 - distance, height / 2, diameter);
circle(width / 2, height / 2 + distance, diameter);
circle(width / 2, height / 2 - distance, diameter);
console.log("test 3");
console.log(diameter);
}
console.log("test 4");
console.log(diameter);
Il y a plusieures choses à remarquer dans le résultat
- le test 4 s'exécute en premier, ce qui est normal puisque notre script ne fait que deux choses
- créer la fonction
draw()qui sera executée bien plus tard - tenter d'afficher
"test 4"puis la valeur dediameter - On se prend une erreur parce que, comme expliqué,
diamatern'existe pas en dehors dedraw(), d'autant quedraw()n'a pas été exécuté et donc l'instruction pour créerdiameternon plus du coups - le test 1 viens ensuite et ne marche pas non plus puisqu'évidemment on essaye d'afficher une variable qui n'existe pas encore
- mais on n'a pas les tests 2 et 3 parce que, lorsque l'on a une erreur (comme celle du test 1) la fonction en cours est interrompue. Et d'ailleurs vu que la fonction
draw()a été interrompue on n'est pas allé jusqu'à exécuter les fonctionscircle()et donc nous n'avons pas de dessin
Pour voir le résultat des tests 2 et 3 ils nous suffit donc de retirer le test 1:
Dynamique
On va faire un truc plus intéressant (avant que vous commenciez à me demander pourquoi on ne ferait pas tout ça sur Figma ou Afinity Designer)
On va changer la valeur de distance pendant que le code tourne.
let distance = 150;
function draw() {
let diameter = 100;
circle(width / 2 + distance, height / 2, diameter);
circle(width / 2 - distance, height / 2, diameter);
circle(width / 2, height / 2 + distance, diameter);
circle(width / 2, height / 2 - distance, diameter);
}
Si je bouge distance tout en haut, on va d'abord créer distance puis créer draw() et enfin espress va exécuter draw().
En soi, peu de choses changent, distance est maintenant disponible à tout le script et pas juste l'intérieur de draw().
Ce qui est important est qu'avant on créeait distance avec une valeur de 150 à chaque fois qu'on exécutait draw() et elle était détruite à la fin de draw(). Alors que maintenant on la créer une fois et on la garde pour toute la durée du programme.
Donc si j'y ajoute 5 dans draw()...
let distance = 150;
function draw() {
let diameter = 100;
distance = distance + 5;
// equivalent à: "`distance` prend la valeur `distance + 5`"
// ...ouais je sais, ça agresse les yeux des matheux
console.log(distance);
circle(width / 2 + distance, height / 2, diameter);
circle(width / 2 - distance, height / 2, diameter);
circle(width / 2, height / 2 + distance, diameter);
circle(width / 2, height / 2 - distance, diameter);
}
... les cercles sont un poil plus éloignés les uns des autres, mais le truc rigolo avec espress c'est que vous pouvez appuyer sur Ctrl + Espace (ou Cmd + Espace) pour dessiner une nouvelle image en exécutant draw() une nouvelle fois sans pour autant relancer le programme de zéro.
Vous constaterez que distance augmente bien de 5 à chaque fois, prouvant que la variable persiste bien à chaque nouvelle exécution de la fonction draw(). Et vu que notre dessin dépend de la distance on voit les cercles s'écarter de 5 pixels à chaque nouvelle image.
Et on peut même faire mieux!
p5 nous fournit une variable mouseX qui contient le nombre de pixels entre le point (0,0) et la position sur l'axe X de la souris.
mouseX comme distance, elle est créée une fois au tout début et continuellement mise à jour (mais pas par votre code).
Je vous laisse essayer ce que ça donne si on remplace distance par mouseX:
function draw() {
circle(width / 2 + mouseX, height / 2, diameter);
circle(width / 2 - mouseX, height / 2, diameter);
circle(width / 2, height / 2 + mouseX, diameter);
circle(width / 2, height / 2 - mouseX, diameter);
}
N'oubliez pas de faire Ctrl/Cmd + Espace pour rafraichir le dessin...
... quoique, si vous avez bien pris le temps de comprendre, vous pouvez maintenant utiliser Ctrl/Cmd + L pour rendre votre dessin "live". Ce qui fait que draw() sera exécuté 60 fois par seconde (ou plus en fonction de votre écran).
Et voila pourquoi on ne le fait pas sur Figma ou Afinity Designer. On a programmé un comportement que la machine à exécuté, liant une action utilisateur à une conséquence, une interaction.
For
for() est un mot-clef qui permet de définir un bloc de code que l'on va répéter plusieurs fois.
Il se présente comme ceci:

- Initialisation de la boucle: une instruction qui s'exécute une fois, avant de rentrer dans la boucle.
- condition capturante : une condition qui est verifiée à chaque tour de boucle, on continue de faire des tours tant que la condition est vraie.
- Instruction à executer après chaque tour : généralement là pour faire progresser quelque chose qui nous permettera d'invalider la condition capturante.
Du coup, si j'écris ...
// pour une variable i valant 0
// tant que i est inférieure ou égal à 10
// en augmentant i de 1 à chaque tour...
for (let i = 0; i <= 10; i = i + 1) {
console.log(i); // on affiche i
}
C'est comme si j'écrivais ...
console.log(0);
console.log(1);
console.log(2);
console.log(3);
console.log(4);
console.log(5);
console.log(6);
console.log(7);
console.log(8);
console.log(9);
console.log(10);
Ce qui est plutôt sympa parce que si maintenant j'ai besoin de faire ça de 0 à 150 plutôt que de 0 à 10, je peux juste changer le chiffre dans la condition du for().
for (let i = 0; i <= 150; i = i + 1) {
console.log(i);
}
plutôt que ça ...
console.log(0);
console.log(1);
console.log(2);
console.log(3);
console.log(4);
console.log(5);
console.log(6);
console.log(7);
console.log(8);
console.log(9);
console.log(10);
console.log(11);
console.log(12);
console.log(13);
console.log(14);
console.log(15);
console.log(16);
console.log(17);
console.log(18);
console.log(19);
console.log(20);
console.log(21);
console.log(22);
console.log(23);
console.log(24);
console.log(25);
console.log(26);
console.log(27);
console.log(28);
console.log(29);
console.log(30);
console.log(31);
console.log(32);
console.log(33);
console.log(34);
console.log(35);
console.log(36);
console.log(37);
console.log(38);
console.log(39);
console.log(40);
console.log(41);
console.log(42);
console.log(43);
console.log(44);
console.log(45);
console.log(46);
console.log(47);
console.log(48);
console.log(49);
console.log(50);
console.log(51);
console.log(52);
console.log(53);
console.log(54);
console.log(55);
console.log(56);
console.log(57);
console.log(58);
console.log(59);
console.log(60);
console.log(61);
console.log(62);
console.log(63);
console.log(64);
console.log(65);
console.log(66);
console.log(67);
console.log(68);
console.log(69);
console.log(70);
console.log(71);
console.log(74);
console.log(75);
console.log(76);
console.log(77);
console.log(78);
console.log(79);
console.log(80);
console.log(81);
console.log(82);
console.log(84);
console.log(85);
console.log(86);
console.log(87);
console.log(88);
console.log(89);
console.log(90);
console.log(91);
console.log(92);
console.log(93);
console.log(94);
console.log(95);
console.log(96);
console.log(97);
console.log(98);
console.log(99);
console.log(100);
console.log(101);
console.log(102);
console.log(103);
console.log(104);
console.log(105);
console.log(106);
console.log(107);
console.log(108);
console.log(109);
console.log(110);
console.log(111);
console.log(112);
console.log(113);
console.log(114);
console.log(115);
console.log(116);
console.log(117);
console.log(118);
console.log(119);
console.log(120);
console.log(121);
console.log(122);
console.log(123);
console.log(124);
console.log(125);
console.log(126);
console.log(127);
console.log(128);
console.log(129);
console.log(130);
console.log(131);
console.log(132);
console.log(133);
console.log(134);
console.log(135);
console.log(136);
console.log(137);
console.log(138);
console.log(139);
console.log(140);
console.log(141);
console.log(142);
console.log(143);
console.log(144);
console.log(145);
console.log(146);
console.log(147);
console.log(148);
console.log(149);
console.log(150);
Outre le fait que c'est plus simple de changer une boucle, c'est d'autant plus important pour faire des manipulations répetitives parce que vous n'avez pas remarqués qu'il manquait console.log(83) dans le bloc ci-dessus.
"Pourquoi i?" "C'est quoi i?"
i?" "C'est quoi i?"i est le possible raccourcis de deux mot anglais:
indexque l'on pourrait traduire par "entrée" comme dans la phrase "abaca est la première entrée du dictionnaire".iterationqui est le même mot en francais (à un accent près). Itérer est l'action de répeter le même procédé plusieurs fois. Une itération étant une répétetition.
Mais i est seulement un nom, et en pratique cette variable contient le numéro de l'entrée à laquelle nous sommes dans la boucle. Ou en tout cas c'est quasiment toujours le cas. Rien nous force à ce que ce soi comme ça mais ne pas le faire reviens à intentionnellement rendre son code illisible, et personne devrait faire ça.
Si vraiment le nom i vous perturbe vous pouvez utiliser autre chose comme :
for (let round = 0; round <= 10; round = round + 1) {
// gna gna gna, le truc qu'on fait plein de fois
}
Mais sachez quand même que c'est le nom que presque tout le monde utilisera en dehors de vous. Donc il faudra au moins être capable de le lire.
À partir de là, je vous invite à regarder les fonctions suivantes pour étoffer vos capacités et de jouer un peu avec tout ça avant de résoudre le problème suivant.
- fill : https://p5js.org/reference/p5/fill/
- stroke : https://p5js.org/reference/p5/stroke/
- strokeWeight : https://p5js.org/reference/p5/strokeWeight/
- rect : https://p5js.org/reference/p5/rect/
- random : https://p5js.org/reference/p5/random/
Problème
Validez les étapes une par une.
1.dessinez un carré
Solution
function draw() {
rect(0, 0, 30, 30);
}
2.dessinez une ligne de 10 carrés allant du bord gauche de l'écran au bord droit. (Indice: il faudra utiliser une boucle for)
Solution
function draw() {
for (let i = 0; i < width; i = i + width / 10) {
rect(i, 0, 30, 30);
}
}
3.utiliser une autre boucle for pour dessiner des lignes jusqu'à remplir l'espace de dessin, ce n'est pas grave si ça déborde.
Solution
function draw() {
background(255);
for(let j = 0; j < height; j = j + width / 10){
//ligne
for (let i = 0; i < width; i = i + width / 10) {
rect(i, j, width / 10, width / 10);
}
}
}