Une animation JavaScript qui fonctionne (Partie 3 de 4)

Dans notre premier article de cette série, nous avons présenté sprite, et comment il peut être utilisé pour réaliser une animation multi-navigateur simple et efficace sur le Web. Dans le second post, nous avons mis en place des animations simples, bien qu’ils aient eu pas mal de bugs et que le code n’était certainement pas prêt à être mis en ligne..

Aujourd’hui, nous allons corriger ces bugs et nettoyer notre code afin de le publier sur une page sans craindre d’écraser le code à l’aide d’une méthode appelée encapsulation.

Portée Variable

Pour vraiment expliquer ce qui n'allait pas avec le code dans notre dernière étape, et pourquoi l'encapsulation est importante, nous devons d'abord expliquer la portée de la variable.

Imaginez que vous travaillez avec le code ci-dessous. Vous avez une variable utile dans votre fonction do_this (), et vous souhaitez utiliser cette même variable dans une autre fonction, do_that (), mais vous rencontrez un petit problème.

 function do_this () var very_helpful_variable = 20;… // Ceci indique '20', tout comme vous attendez alert (very_helpful_variable);  function do_that () alert (very_helpful_variable); // Mais cela montre 'non défini'! 

Votre variable fonctionne très bien dans la fonction déclarée, mais en dehors de cette fonction, c'est comme si elle n'avait jamais existé! Ceci est dû au fait do_that () n'est pas dans le portée de la variable very_helpful_variable.

Les variables ne sont disponibles qu'à l'intérieur du bloc de code où elles sont déclarées, c'est leur portée. Une fois que ce bloc de code est terminé, ses variables sont effacées.

Regardez ces exemples:

 var w = 1; fonction a () var x = 2; fonction b () var y = 3; alerte (w); // fonctionne alert (x); // fonctionne alert (y); // fonctionne alert (z); // non défini alert (w); // fonctionne alert (x); // fonctionne alert (y); // alerte non définie (z); // non défini function c () var z = 4; alerte (w); // fonctionne alert (x); // alerte non définie (y); // alerte non définie (z); // fonctionne b (); // non défini alert (w); // fonctionne alert (x); // alerte non définie (y); // alerte non définie (z); // indéfini

Nous avons d'abord la variable w, qui est déclaré en dehors de toute fonction. Ça s'appelle un variable globale, et cela fonctionnera n'importe où parce que sa portée est le document entier.

Suivant est la variable X, puisqu'il est déclaré à l'intérieur de la fonction une(), cela ne fonctionnera que dans cette fonction. Cela inclut également le travail à l'intérieur de la fonction b (), puisque b () est à l'intérieur de une().

Cependant, une variable définie à l'intérieur de b () (comme y) ne fonctionnera pas dans la fonction externe, car cela est en dehors de sa portée.

Vous pouvez également remarquer que nous avons tenté en vain d’appeler la fonction. b () de l'intérieur de la fonction c (); les noms de fonction suivent les mêmes règles que les autres variables.

Une autre bizarrerie avec JavaScript, si nous commençons simplement à utiliser un nom de variable dans une fonction sans la déclarer avec le mot clé var, alors le navigateur supposera que cette variable doit être globale. Donc, si vous ne vous assurez pas de toujours déclarer vos variables avec le var mot-clé, vous vous retrouverez avec des variables globales sans vous en rendre compte!

Donc, pour résumer: chaque fois que nous déclarons une variable, nous pouvons l’utiliser dans ce bloc de code ou dans les blocs imbriqués qu’il contient. Si nous essayons de l'utiliser en dehors de sa portée, la valeur est définie sur indéfini.

C’est pourquoi, dans notre dernier message, nous avons mis le minuteur variable en dehors des fonctions qui l’utilisaient, car nous avions encore besoin de saisir cette variable après la fin des fonctions.

 var timer; // Ceci est une variable globale function run_right (stage, left) … timer = setTimeout (function () run_right (2, left);, 200);… function stop_running () document.getElementById ('j' ) .style.backgroundPosition = "0px 0px"; // Si 'timer' n'a pas été défini comme global, nous ne pourrions pas l'arrêter ici clearTimeout (timer); 

Pour effacer la minuterie, nous avions besoin arrêter de courir() être dans la portée de la variable minuteur. Donc, nous avons fait minuteur une variable globale qui pourrait être utilisée partout, ce qui pourrait être faux avec cela?

Le problème avec les variables globales

Dans un périmètre donné, il est impossible d'avoir deux éléments qui s'appellent la même chose. Si vous essayez d’avoir deux variables différentes portant le même nom, le navigateur n’écrira que l’une d’elles. Donc, si nous avions une variable nommée minuteur, et avait une variable distincte également nommée minuteur cela s'appelait dans le même champ d'application, l'un d'eux supprimerait et remplacerait l'autre, et notre code ferait des ravages. Si nous avions un variable globale appelé minuteur, alors il interfèrerait avec une autre variable nommée minuteur contenues n'importe où dans la page, y compris toutes les bibliothèques JavaScript et fichiers attachés et les fichiers externes.

C’est une énorme source de maux de tête, vous venez de voir un plug-in JavaScript vraiment soigné quelque part, et vous le téléchargez sur votre site, et soudainement tous vos autres plug-in plantent… L’un des plug-ins était bâclé avec les fichiers globaux. variables, qui partagent le même nom avec quelque chose d'autre, votre navigateur se met à trébucher et la page entière s'arrête brusquement.

Ce qui aggrave encore les choses, c'est que vous ne remarquerez jamais ce problème lors du premier test du code. Comme notre code d'animation du dernier post, cela fonctionnera très bien tout seul. Mais plus vous ajoutez d'éléments, plus vous aurez de chances d'avoir un conflit de noms et vous aurez du mal à trier une douzaine de fichiers JavaScript différents pour essayer de déterminer lesquels ne s'entendent pas..

Maintenant, vous vous demandez peut-être: "Les variables globales sont si pratiques! Et si je surveillais mon code avec beaucoup d'attention et m'assurais que je n'avais aucun conflit?" Cela pourrait fonctionner dans un monde parfait, mais en réalité vous aurez souvent plusieurs personnes travaillant sur différentes parties de la même page, ou devrez revenir et mettre à jour différentes parties de votre code des années plus tard, ou même avoir du code de tiers sur votre page qui sera hors de votre contrôle (comme une publicité payée).

En bref, vous ne voudriez pas plus de variables globales que de câbles exposés le long des murs de votre maison ou de machines exposées dans votre voiture, c'est juste une question de temps avant que quelque chose ne se passe qui gomme les travaux. Heureusement, il existe un meilleur moyen d'éviter ces pièges.

Encapsulation

Nous pouvons avoir tous les avantages des variables globales sans les problèmes en utilisant une technique appelée encapsulation. Pensez-y comme si vous construisiez un mur autour de votre code avec seulement quelques portes spéciales. Rien ne peut entrer ou sortir de ce code sans l'autorisation expresse de celui-ci..

JavaScript a un type de variable appelé un objet. Les objets sont des collections de données définies par l'utilisateur contenant des informations et des fonctions (appelées Propriétés et les méthodes, respectivement). Nous allons écrire une fonction qui crée un objet spécial qui contient toutes les fonctions dont nous avons besoin, et qui nous permettra même d’avoir plus d’un robot sans dupliquer notre code.!

Nous commençons par définir une nouvelle fonction avec un nom de variable. Nous devrons transmettre à la variable quelques arguments, ainsi que l'élément HTML que nous allons animer, ainsi que des valeurs uniques pour la vitesse d'exécution et la hauteur de saut afin que nous puissions les varier d'un robot à l'autre..

 var RobotMaker = function (robot, run_speed, jump_height) // Nous allons mettre toutes nos fonctions et variables dans cette zone. // Ceci est à l'intérieur de notre mur 'impénétrable', donc rien dans cette // zone n'entrera en conflit avec un autre code. return // À l'intérieur, nous plaçons toutes nos 'portes'… // ce sera le seul moyen pour que tout // puisse entrer ou sortir de ce code. // Et, comme cela reste dans la même portée // que RobotMaker, nous pouvons utiliser toutes les variables mentionnées ci-dessus! 

Puisque nous allons placer toutes nos fonctions dans notre nouveau "mur", le moment est venu de revoir les erreurs que nous avons rencontrées avec le code original. (Vous pouvez voir cela en action ici)

Vous remarquerez peut-être que si nous cliquons sur deux boutons d’exécution (ou sur un bouton d’exécution et de saut) sans cliquer sur le bouton Arrêtez bouton entre les deux, J continuera à faire les deux actions. Un autre problème est que peu importe la direction à laquelle J est confronté, lorsque l’on clique sur le bouton Saut ou Arrêtez bouton, il fait face à chaque fois. Enfin, si vous cliquez sur le Saut bouton à nouveau pendant que J tombe d'un premier saut, il continuera à tomber à travers la page dans une boucle sans fin.

Afin de résoudre ces problèmes, nous devons être plus précis sur ce que nous voulons accomplir avec chacune de nos fonctions:

Lorsque nous cliquons sur Exécuter à droite:

  1. Si J saute, ne faites rien et continuez le saut
  2. Si J est parti à gauche, arrêtez de courir à gauche
  3. Courir à droite et animer au bon cadre
  4. Si J arrive au bout de l’étape, arrêtez de courir et tenez-vous face à droite

Lorsque nous cliquons sur Run Left:

  1. Si J saute, ne faites rien et continuez le saut
  2. Si J court, arrêtez-le
  3. Courir à gauche et animer au bon cadre
  4. Si J arrive à la fin de l’étape, arrêtez de courir et restez face à la gauche.

Lorsque nous cliquons sur Arrêter de courir:

  1. Si J saute, ne faites rien et continuez le saut (nous ne voulons pas nous arrêter en plein vol!)
  2. Si vous courez à droite ou à gauche, arrêtez de courir
  3. Si vous faites face à la droite, restez face à la droite. Si face à gauche, debout face à gauche

Quand on clique sur Sauter:

  1. Si J est en train de sauter, ne faites rien et continuez le saut (nous ne voulons plus sauter en l'air!)
  2. Si J fonctionne à droite ou à gauche, arrêtez de courir
  3. Commence le saut. Si J est tourné vers la droite, saute vers la droite. Si face à gauche, saute face à gauche
  4. Atterrir dans la même direction que le saut

Tout d’abord, nous allons ajouter quelques variables maintenant. Étant donné que le chronomètre devrait se comporter différemment pour courir et sauter, nous aurons deux chronomètres distincts. Nous voulons aussi introduire un booléen (vrai / faux) variable pour suivre si nous devrions être tournés vers la gauche ou la droite, et nous ferons un étape variable juste pour nous éviter d'avoir à taper le nom complet de l'élément.

 // Dans la fonction RobotMaker… var stage = document.getElementById ('stage'); var run_timer, jump_timer; var face_right = true;

Ensuite, nous allons ajouter dans nos fonctions pour courir à droite, courir à gauche et sauter. Celles-ci seront généralement les mêmes, avec quelques différences. Tout d’abord, toutes les références à l’élément que nous animons peuvent être remplacées par la variable robot (qui sera passé comme un des arguments dans la RobotMaker une fonction). Deuxièmement, nous avons légèrement modifié la vitesse de course et la hauteur de saut dans les fonctions afin de pouvoir les varier en transmettant des valeurs différentes. Troisièmement, nous utilisons le face_right variable pour savoir dans quelle direction J se trouve face (et dans la fonction de saut, en utilisant face_right décider quel sprite de saut à montrer). Enfin, nous utilisons des minuteries distinctes pour courir et sauter.

 // Dans la fonction RobotMaker… function run_r (phase, left) face_right = true; if ((left + (15 * run_speed)) < (stage.offsetWidth - robot.offsetWidth)) left = left + (15 * run_speed); robot.style.left = left+"px"; switch (phase) case 1: robot.style.backgroundPosition = "-40px 0px"; run_timer = setTimeout(function()run_r(2, left);, 200); break; case 2: robot.style.backgroundPosition = "-80px 0px"; run_timer = setTimeout(function()run_r(3, left);, 200); break; case 3: robot.style.backgroundPosition = "-120px 0px"; run_timer = setTimeout(function()run_r(4, left);, 200); break; case 4: robot.style.backgroundPosition = "-80px 0px"; run_timer = setTimeout(function()run_r(1, left);, 200); break;   else  robot.style.backgroundPosition = "0px 0px";   function run_l(phase, left) face_right = false; if (0 < robot.offsetLeft - (15 * run_speed)) left = left - (15 * run_speed); robot.style.left = left+"px"; switch (phase) case 1: robot.style.backgroundPosition = "-40px -50px"; run_timer = setTimeout(function()run_l(2, left);, 200); break; case 2: robot.style.backgroundPosition = "-80px -50px"; run_timer = setTimeout(function()run_l(3, left);, 200); break; case 3: robot.style.backgroundPosition = "-120px -50px"; run_timer = setTimeout(function()run_l(4, left);, 200); break; case 4: robot.style.backgroundPosition = "-80px -50px"; run_timer = setTimeout(function()run_l(1, left);, 200); break;   else  robot.style.backgroundPosition = "0px -50px";   function jmp(up, top) if (face_right) robot.style.backgroundPosition = "-160px 0px";  else  robot.style.backgroundPosition = "-160px -50px";  if (up && (robot.offsetTop > (20 * (1 / jump_height)))) top = top - (top * .1); robot.style.top = top + "px"; jump_timer = setTimeout (function () jmp (haut, haut);, 60);  else if (up) up = false; jump_timer = setTimeout (function () jmp (haut, haut);, 60);  else if (! up && (robot.offsetTop < 115)) top = top + (top * .1); robot.style.top = top+"px"; jump_timer = setTimeout(function()jmp(up, top);, 60);  else  robot.style.top = "120px"; if (face_right) robot.style.backgroundPosition = "0px 0px";  else  robot.style.backgroundPosition = "0px -50px";  jump_timer = false;  

Toutes ces variables et fonctions sont à l'intérieur de notre "mur", nous devons donc créer des "portes" pour pouvoir accéder uniquement à ce dont nous avons besoin. Ces quatre "portes" seront objet les méthodes pour les mêmes quatre fonctions que nous avions précédemment et référencerons les fonctions protégées ci-dessus. En outre, nous terminerons notre correction des bogues en vérifiant chaque fonction si la jump_timer va, puis en veillant à effacer le run_timer. Rappelez-vous que ces deux minuteries sont visibles partout dans le RobotMaker () fonction, afin que nous puissions les utiliser ici. Cependant, comme ce ne sont pas des variables globales, nous ne rencontrerons aucun problème avec elles ailleurs.

 // Dans la fonction RobotMaker… return run_right: function () if (! Jump_timer || jump_timer == non défini) clearTimeout (run_timer); run_r (1, robot.offsetLeft); , run_left: function () if (! jump_timer || jump_timer == undefined) clearTimeout (run_timer); run_l (1, robot.offsetLeft); , stop_running: function () if (! jump_timer || jump_timer == undefined) clearTimeout (run_timer); if (face_right) robot.style.backgroundPosition = "0px 0px";  else robot.style.backgroundPosition = "0px -50px"; , jump: function () if (! jump_timer || jump_timer == non défini) clearTimeout (run_timer); jmp (true, robot.offsetTop); 

Maintenant que nous avons écrit une fonction qui crée des objets, nous pouvons l’utiliser autant de fois que nous le souhaitons pour créer des objets ayant les propriétés d’animation souhaitées. Au bas de notre page, nous allons déclarer deux nouveaux RobotMaker objets, et passez-les l'élément que vous souhaitez animer, une vitesse de course et une hauteur de saut.

 var j = RobotMaker (document.getElementById ('j'), 1, 1); var j2 = RobotMaker (document.getElementById ('j2'), .8, 5);

Maintenant, nous ne courons plus aucun danger dans le RobotMaker () les fonctions fuient et interfèrent avec notre code, et nous pouvons toujours accéder aux fonctions que nous voulons par les "portes" que nous avons installées comme ceci:

 

Donc, maintenant vous pouvez voir le produit fini sur le stylo hyrgo.

Notez qu'il n'y a plus aucun problème d'interférence entre les fonctions et que vous pouvez utiliser chaque robot individuellement sans affecter l'autre. L'encapsulation est une technique extrêmement importante, et vous devriez vraiment vous y familiariser si vous voulez faire de la conception Web interactive..

Si vous le souhaitez, vérifiez tout ce code, entièrement commenté, et vous pouvez obtenir les sprites en utilisant les liens suivants: voici les premiers sprites et les seconds. Veuillez noter que pour que le même code fonctionne avec les deux sprites, il fallait que le deuxième sprite soit exactement au même format et aux mêmes dimensions que le premier..

Conclusion

Donc, cela termine la troisième partie du spriting! Dans notre prochain et dernier article, je remplacerai ces boutons par un bouton qui obligera nos robots à suivre la souris sur l’écran et à vous montrer comment configurer auditeurs d'événements et activer le support sur les navigateurs et les appareils tactiles.