Re @joanC
Voici la fin de l’explication du code.
Ce qu’il faut également garder à l’esprit c’est que la difficulté de compréhension d’un code dépend certe du nombre de fonctionnalité que tu ne connais pas qu’il utilise ainsi que le nombre d’élément de syntaxe de langage différent que tu ne connais pas.
Mais ce n’est pas tout, a difficulté dépend également de la manière dont celui-ci est proprement écrit ordonné et commenté ce qui est pour le coup indépendant de toi.
Il n’est pas important de toujours comprendre tout ce que tu manipules. Dans ce sens la réponse de @kneelnrise est une des manières d’appréhender un code inconnu. On se réfère aux grosses sections, et on fait des déductions par rapport au nom. Tester le code permet également de mieux le comprendre, chose que dans mon cas je n’ai pas faites.
La vrai question c’est : comment savoir, quand on a un code, quel partie je devrais lire et quel partie je ne devrais pas lire pour le comprendre. Car comment savoir qu’on aurait pas dû s’attarder sur une partie qui nous en a pas appris plus, avant d’y avoir consacrer du temps ?
Et c’est là qu’à mon sens la propreté du code et son rangement sont ce qui fait la différence entre 2 codes qui font la même chose.
Un code bien rangé, facile à lire en diagonale est un bon code ce qui n’est pas le cas ici. En tant que débutant il est difficile de comprendre quelles parties sont bonne à lire, et quelle parties sont bonne à ignorée.
Je te laisse me donner ton avis sur cette page qui explique comment produire du code JavaScript qui justement permet différent niveau de relecture de manière à savoir exactement quel niveau de compréhension tu souhaites avoir sur un code. Celui de @kneelnrise ou le miens. Cela pourra peut-être t’aider à choisir tes futurs code ou à les écrires proprement.
Coder proprement en JavaScript par l’exemple : Upload d’image
En attendant, chose promise, chose due :p
/* Déclaration d'une fonction (qui aurait dû être définie avant le premier `if` de l'environnement
global par convention afin d'avoir
1. Une partie déclaration
2. Une partie logique. C'est ce qui est expliqué dans le lien de l'article plus haut.
Cette fonction prend en premier paramètre `event`.
À ce stade je ne sais pas trop ce que cela fait mais d'après le nommage
Ça analyse un événement pour en déduire que le « boutton » gauche fait quelque chose.
On parle probablement du clique gauche de la souris, nous verrons plus loin. */
function detectLeftButton(event) {
// Si l'objet event à une propriété qui se nomme "buttons" alors...
if ('buttons' in event) {
// dans le cas ou cette propriété vaut 1, on retourne true
// sinon on retourne false.
return event.buttons === 1;
// Si l'objet event à une propriété qui se nomme "keyCode" alors...
} else if ('keyCode' in event) {
// dans le cas ou cette propriété vaut 1, on retourne true
// sinon on retourne false.
return event.keyCode === 1;
// Si il n'y a ni buttons ni keyCode alors on test button (sans s).
} else {
// dans le cas ou cette propriété vaut 1, on retourne true
// sinon on retourne false.
return event.button === 1;
}
// La question ici c'est. Pourquoi ce test ? Pourquoi event pourrait t-il tantôt avoir « buttons », tantôt « keyCode » et tantôt « button ».
// On pourrait fouiller un peu sur Google mais pour le moment j'emets l'hypothèse que c'est pour des raisons de compatibilité navigateur. J'emet également l'hypothèse qu'en fonction du type d'événement et de l'état de `event` les 3 valeurs pourrait retourner `0` sous entendu le bouton n'est pas enfoncé.
// Nous en saurons p-e plus, plus loin, donc on recherchera plus tard si vraiment cela nous bloque.
}
// Idem que au dessus. D'après le nom on s'attend à ce que cette fonction nous retourne à quel endroit
// se trouve notre « Brush » soit pinceau. Si on réfléchi 2 secondes. Les seuls manière de dessiner sur
// un écran c'est avec sa souris ou son doigt (ou un stylet) c-à-d qu'en réalité sous ce nom on veut probablement
// indiquer ou sera positionné la sourie à un moment donnée. Pour connaître la position de quelque chose sur un plan
// 2D (context en 2D), il nous faut une valeur de position horizontal et vertical. Ça tombe bien les paramètres `xRef` et `yRef` semblent
// représenter ses valeurs.
function getBrushPos(xRef, yRef) {
// Ici `var` est au bon endroit. Il est défini au sommet d'une fonction. Il est donc dans le `scope` de la fonction et ne sera utilisable
// que dans celle-ci. On affecte à la varialbe `bridgeRect` la valeur de `tableau.getBoundingClientRect()`.
// On se souvient que `tableau` est un `HTMLElement` (un `HTMLCanvasElement` plus exactement). Je sais que `getBoundingClientRect()` retourne un objet
// permettant de situer le canvas par rapport à la fenêtre du navigateur mais je n'en suis plus sur. Je demande à Google et je file dans les images car
// visuellement c'est plus simple. Je vois qu'il contient 6 propriété : `left`, `top`, `right`, `bottom`, `width`, `height`.
// `left`, `top`, `right`, `bottom` désigne la position des 4 bords du cadre par rapport au haut de l'affichage du navigateur. Ces valeurs changent donc
// quand on scroll dans la page car elles représente la position du canvas dans l'espace visible par rapport au bord haut et gauche.
var bridgeRect = tableau.getBoundingClientRect();
// On s’aperçoit ici que `bridgeRect.right-bridgeRect.left` représente en réalité la valeur `width` car soustraire la valeur la plus éloigné du bord gauche
// à celle la plus proche nous donne le morceau représentant la largeur. On aurait donc tout simplement pu utiliser `bridgeRect.width`. Idem avec `bridgeRect.height`.
// Grâce à `xRef-bridgeRect.left` je devine qu'on cherche à obtenir la valeur de la position de la souris non pas par rapport à la fenêtre du navigateur,
// Mais par rapport à la position du canvas dans cette fenêtre. Enfin vu qu'on multiplie la largeur du canvas dans la fenêtre du navigateur par sa valeur réelle,
// je suppose que c'est au cas où il y ai un zoom dans le navigateur. Pour finir, on tronque les valeurs à l'entier inférieur `Math.floor`.
return {
x: Math.floor((xRef-bridgeRect.left) / (bridgeRect.right-bridgeRect.left) * tableau.width),
y: Math.floor((yRef-bridgeRect.top) / (bridgeRect.bottom-bridgeRect.top) * tableau.height)
};
// On a donc la position de notre souris sur le `canvas` ou x = 0 et y = 0 veut dire qu'on dessine dans le coin haut gauche et que toute les autres valeurs permette
// de situer la souris dans le canvas.
}
// D'après le nom, on va dessiner un point.
// D'après les paramètres, on va dire ou dessiner le point.
function drawDot(mouseX,mouseY){
// Je ne connais pas `beginPath`, go Google.
// À priori c'est pour tracer un trait, un `path`, et ça indique que vis à vis
// De ce que l'on va faire ensuite
// cela représente le début des instructions.
tablCanvas.beginPath();
// Je suppose donc qu'on va dessiner une portion de cercle, « un arc » et que pour le dessiner on va
// donner la position en `mouseX` et en `mouseY` du centre de l'arc, en donner l'éloignement vis à vis de ce centre (`brushRadius`)
// ensuite la valeur 0 je ne sais pas, puis je suppose en voyant `2*Math.PI` que l'on trace un arc complet (soit un cercle)
// ensuite la valeur `true` je ne sais pas. Je suis curieux, je file voir la doc sur Google.
// Je m’aperçois que `0` (troisième valeur) correspond à l'angle de départ. Cela est logique car il faut bien commencer son tracé d'arc quelque part.
// Ensuite la quatrième valeur (`2*Math.PI`) dit ou on va terminer sont tracé. On aura donc fait un tour. La valeur `true` indique que l'on effectue le
// tracé dans le sens anti-horraire. Ici ça ne change rien puisque le cercle est complet, peut importe de quel sens part l'arc, on aura
// un cercle complet. On pourrait donc ne rien mettre ou mettre `false`.
tablCanvas.arc(mouseX, mouseY, brushRadius, 0, 2*Math.PI, true);
// On remplit l'intérieur du cercle avec la couleur `#000`.
tablCanvas.fillStyle = '#000';
// On a `globalCompositeOperation` et ça je sais pas ce que c'est, je demande. Ça permet de définir comment on veut ajouter le contenu !
// D'après l'exemple que j'ai vu. Ça ressemble à une gomme ! On n'est donc pas entrain d'écrire,
// Mais entrain d'effacer le contenu en arrière plan. On est donc entrain de créer un « point d'effacement ».
// Probablement pour effacer l'image qu'on mettra en fond.
tablCanvas.globalCompositeOperation = "destination-out";
// Et on effectue l'action (ce qui signifie qu'entre `beginPath` et `fill` c'était la préparation de comment on allait faire).
tablCanvas.fill();
}
// Comme avec `load` sur `img`, on écoute ici sur le canvas les mouvements de la souris avec `mousemove`.
// De la même manière qu'affecter `img.src` déclenchait le `load`, ici le mouvement dans la zone du canvas va
// déclencher le `mousemove`.
// Tu remarquera que le `load` avait été affecté avec `img.onload` alors que la le `mousemove` est affecté avec `addEventListener("mousemove"`. En réalité si le code était plus propre, on aurrait remplacé `img.onload` par `img.addEventListener("load"` comme pour `mousemove`. La différence est que en `ajoutantUnÉcouteurDÉvénement` on permet plus tard dans le code d'en ajouter d'autre, et puis d'autre. Alors qu'en utilisant `img.onload` on ne peut définir qu'un seul événement pour cette action à travers tout le code. C'est une mauvaise pratique. `img.onload` est l'équivalent de `<img onload="">` comme `img.src` est l'équivalent de `<img src="">`. `addEventListener` quand à lui est une vrai alternative JavaScript pour bien manipuler ce qu'il se passe sur les éléments du navigateur.
// On peut donc dire que cela ce lit :
// Quand la souris bouge...
tableau.addEventListener("mousemove", function(e) {
// trouver ou elle se trouve sure le canvas (par rapport à lui, et pas au navigateur)
var brushPos = getBrushPos(e.clientX, e.clientY);
// vérifier si le clique gauche de la souris est enfoncé (donc c'était bien pour couvrir tous les navigateurs).
var leftBut = detectLeftButton(e);
// et SI le clique gauche est enfoncé...
if (leftBut == 1) {
// ... gomer à la position indiquée !!!
drawDot(brushPos.x, brushPos.y);
}
// La valeur `false` ici signifie que l'événement sera écouté pendant la phase de remonté d'événement `bubbling`.
// Si il était à vrai, il serait écouté pendant la phase de `capture`.
// Cela signifie que si je bouge ma souris au dessus du canvas, je la bouge également au dessus de tous les éléments HTML qui
// entoure le canvas. Si chacun de ses éléments écoutait le mouvement de la souris il se déclencherait s'il était tous avec cette valeur
// à false ans l'ordre `canvas -> parent du canvas -> parent du parent du canvas -> etc`.
// Par contre avec cette valeur à true ce serait dans l'ordre `etc. -> parent du parent du canvas -> parent du canvas -> canvas`
// Pour résumer quand un mouvement de souris est effectué au dessus du canvas dans `<div><ul><canvas></canvas></ul></div>`
// on peut intercepter les événement dans cette ordre
// div.addEventListener(..., ..., true) -> ul.addEventListener(..., ..., true) -> canvas.addEventListener(..., ..., true)
// puis -> canvas.addEventListener(..., ..., false) -> ul.addEventListener(..., ..., false) -> div.addEventListener(..., ..., false)
}, false);
// Même punition avec `touchmove`. Celui-ci est déclenché quand des doigts (ou stylets) entre en
// contact avec l'écran dans la zone du canvas.
tableau.addEventListener("touchmove", function(e) {
// On empêche le comportement par défaut, cela évite que la page scroll quand on pause
// le doigt sur le canvas.
e.preventDefault();
// Dans le cas d'un événement `touchmove`, le premier paramètre de la fonction de retour (callback) `e` contient un tableau `targetTouches`.
// Ce tableau contient tous les endroits (en x et y) sur le canvas ou les doigts sont en contact avec l'écran.
// Cela permet de gérer si on le voulait plusieurs trait d'effacement. Ici on ne gère que le premier doigt en contact, c-à-d l'indice `0` du tableau.
var touch = e.targetTouches[0];
// Si cette indice retourne un objet contenant les coordonnée x et y du doigt.
if (touch) {
// Alors on trouve la position du doigt non pas par rapport à l'écran du navigateur (ce que renvoi `e.targetTouches[0]`) mais
// on la trouve par rapport au canvas (noter que `brushPos` devrait être déclarer au même endroit que `touch` et être affecté dans le if.
var brushPos = getBrushPos(touch.pageX, touch.pageY);
// et on gomme l'image à cette position.
drawDot(brushPos.x, brushPos.y);
}
}, false);
PS : Je serais toi je renommerai ce post « Relecture d’une fonction JavaScript : niveau de lecture », j’éditerais un peu mon message que toute cette conversation soit profitable à plus de monde que toi seul :)
Bonne continuation !