Une animation JavaScript qui fonctionne (Partie 4 de 4)

Dans la première partie de cette série, nous avons introduit l’idée d’utiliser sprite comme un moyen facile, multi-navigateur, d’avoir une animation interactive pour le Web. Dans la deuxième partie, nous avons eu quelques animations, et dans la troisième, nous avons nettoyé notre code et l'avons préparé pour le Web..

introduction

Maintenant, dans notre dernière partie d’aujourd’hui, nous allons passer en revue gestionnaires d'événements de sorte qu'au lieu de répondre aux boutons cliqués, nos robots suivront la souris autour de l'écran. Dans le même temps, nous parlerons également de la possibilité de rendre le code convivial pour le cross-browser et l’écran tactile..

Si vous jetez un coup d’œil à notre code de la dernière fois, vous verrez que, s’il fonctionne bien (et avec plusieurs robots), il n’existe pas de moyen très simple de dire au code de s’exécuter..

Gestionnaires d'événements

Gestionnaires d'événements sont des commandes qui indiquent à certains codes de s'exécuter lorsque certains événements sont déclenchés. Par exemple, vous pourriez avoir ma_fonction () exécuter chaque fois qu'un utilisateur clique sur votre div avec l'identifiant 'my_div'. Ou, vous pourriez avoir ma_autre_fonction () courir chaque fois qu'un utilisateur déplace sa souris 'mon_autre_div'.

En théorie, c'est une idée assez simple et directe. Malheureusement, une fois que vous avez commencé à impliquer différents navigateurs, cela peut être un peu déroutant. Dans un monde idéal, chaque navigateur Web interpréterait le même code et le même code HTML de la même manière, tandis que les développeurs écriraient du code une fois, et que cela fonctionnerait de la même manière pour tous les utilisateurs. Dans le monde réel, différents navigateurs peuvent avoir des commandes complètement différentes pour faire la même chose (* toux * * toux * Internet Explorer), et parfois essayer d’obtenir le même code sur tous les navigateurs peut donner l’impression de rassembler des chats. Récemment, la situation s'est nettement améliorée, car Chrome, Firefox, Safari et Opera réagissent de manière très similaire au code. Internet Explorer 9 et 10 sont devenus beaucoup plus conformes aux normes que les versions précédentes et presque personne n'utilise Internet Explorer. 7 ou 6 plus. Donc, pour notre code, nous allons faire fonctionner les gestionnaires d’événements pour les navigateurs modernes et Internet Explorer 8..

En passant, c’est un cas où il est vraiment utile d’utiliser une bibliothèque JavaScript robuste, telle que jQuery. jQuery effectue tout le travail pour vous lors des tests sur plusieurs navigateurs. Vous n'avez donc qu'à entrer une commande et la bibliothèque jQuery la traduira pour chaque navigateur en coulisse. En outre, de nombreuses commandes dans jQuery sont beaucoup plus intuitives et plus simples que le code JavaScript principal..

Mais, puisque je suis têtu et qu'il s'agit d'une opportunité d'apprentissage, nous allons continuer à la dure et faire tout cela uniquement avec JavaScript et sans dépendances.!

Interaction de page

Notre première étape sera donc de décider comment nous voulons interagir avec la page. Lorsque je déplace la souris sur la scène, je veux que tous les robots courent vers la souris. Quand ils atteignent la souris, ou si la souris est directement au-dessus d’eux, je veux qu’ils arrêtent de courir. Si la souris passe dessus, je veux qu'ils sautent. Et enfin, lorsque la souris quitte la scène, je veux qu’elle cesse de courir. Nous allons commencer par attacher ces événements à l’intérieur du RobotMaker une fonction:

 stage.addEventListener ('mousemove', stage_mousemove_listener, false); robot.addEventListener ('mouseover', robot_mouseover_listener, false); stage.addEventListener ('mouseout', stage_mouseout_listener, false);

Ainsi, dans les lignes ci-dessus, nous avons dit que chaque fois que l'utilisateur déplace la souris à l'intérieur de l'élément stage, nous allons déclencher une fonction appelée stage_mousemove_listener () (notez que nous n'incluons pas les parenthèses dans la commande). De même, lorsque l’utilisateur déplace la souris sur l’élément du robot, il se déclenche. robot_mouseover_listener (), et lorsque l'utilisateur déplace la souris en dehors de la scène, il se déclenche stage_mouseout_listener ().

Malheureusement, comme nous l'avons mentionné précédemment, Internet Explorer 8 et les versions antérieures disposent d'une commande (similaire mais différente) pour effectuer la même chose. Nous devrons donc tester pour savoir quelle commande le navigateur de l'utilisateur comprendra et utiliser cette méthode..

 if (stage.addEventListener) // Nous allons tester si cette commande est disponible stage.addEventListener ('mousemove', stage_mousemove_listener, false); robot.addEventListener ('mouseover', robot_mouseover_listener, false); stage.addEventListener ('mouseout', stage_mouseout_listener, false);  else // Sinon, nous devons utiliser les commandes IE stage.attachEvent ('onmousemove', stage_mousemove_listener); robot.attachEvent ('onmouseover', robot_mouseover_listener); stage.attachEvent ('onmouseout', stage_mouseout_listener); 

Vous remarquerez peut-être que le format des commandes est très similaire, mais présente quelques différences majeures - on dit 'addEventListener' tandis que l'autre dit 'attachEvent'. On dit 'mousemove' tandis que l'autre dit 'onmousemove'. L'un nécessite un troisième paramètre, tandis que l'autre n'en utilise que deux. Si vous mélangez ces éléments, la commande ne sera pas exécutée. C'est le genre de choses qui vous donneront envie de vous cogner la tête contre le mur. Malheureusement, ce n'est pas la fin du codage supplémentaire que nous devrons faire pour la capacité de plusieurs navigateurs.

Fonctions d'écoute

Ensuite, nous allons écrire les fonctions d’écoute. Nous allons commencer par la fonction qui est déclenchée lorsque l'utilisateur passe la souris sur la scène. Puisque c'est un déplacer la souris écouteur, cette fonction se déclenche chaque fois que la souris est déplacée à l’intérieur de la scène (c’est-à-dire qu’elle se déclenchera plusieurs fois par seconde lorsque la souris est en mouvement). Cette fonction devra comparer la position du robot à la position de la souris, et obliger le robot à se comporter en conséquence. Chaque fois que la fonction est déclenchée, elle vérifie si le robot doit continuer à exécuter la même direction ou changer de comportement. Donc, il faudra que ce soit quelque chose comme ceci:

 // À l'intérieur de RobotMaker // Nous devrons introduire quelques variables supplémentaires pour suivre var mouseX; // Pour suivre la position horizontale de la souris var running_dir = "; // Pour suivre si (et où) le robot est en cours d'exécution var stageOffset; // Pour suivre la position de la fonction d'étape stage_mousemove_listener (e) // Trouver la position horizontale de la souris à l'intérieur de la scène… // Cette position sera sauvegardée dans 'mouseX' // Ensuite, nous comparerons 'mouseX' au robot et déciderons si nous devons exécuter différemment if (((robot.offsetLeft + (15 * run_speed) )) < (mouseX - robot.offsetWidth)) && running_dir !== 'r' && (!jump_timer || jump_timer === undefined)) // If the mouse is in the stage and to the right of the robot, make run right, if not already running_dir = 'r'; clearTimeout(run_timer); run_r(1, robot.offsetLeft);  else if ((mouseX < robot.offsetLeft - (15 * run_speed)) && running_dir !== 'l' && (!jump_timer || jump_timer === undefined))  // If the mouse is in the stage and to the left of the robot, make run left, if not already running_dir = 'l'; clearTimeout(run_timer); run_l(1, robot.offsetLeft);  else if ((robot.offsetLeft < mouseX) && ((robot.offsetLeft + robot.offsetWidth) > mouseX) && running_dir! == "&& (! jump_timer || jump_timer === undefined)) // Si la souris est dans la scène et sur un robot, arrêtez et efface running_dir running_dir ="; clearTimeout (run_timer); if (face_right) robot.style.backgroundPosition = "0px 0px";  else robot.style.backgroundPosition = "0px -50px";  // Si rien de ce qui précède n'est vrai, nous laissons notre comportement actuel se poursuivre

Donc, dans la fonction ci-dessus, une fois que nous sommes en mesure de trouver mouseX, nous le comparons à l'emplacement du robot et déclenchons ou arrêtons les différentes fonctions en cours d'exécution en fonction des besoins. Malheureusement, trouver mouseX est un peu délicat, car la position de la souris est une autre chose que différents navigateurs font différemment. Au lieu d’explications (plus) compliquées et longues, voici la méthode multi-navigateurs pour trouver mouseX, comme inspiré de l'excellent blog Quirksmode (qui est une excellente source pour une étude JavaScript plus avancée).

 fonction stage_mousemove_listener (e) var posX = 0; if (! e) var e = window.event;  if (e.pageX) posX = e.pageX;  else if (e.clientX) posX = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;  mouseX = posX - stageOffset.xpos; // Et nous trouvons mouseX! 

Nous avons un argument appelé e dans la fonction, même si nous ne transmettons rien. Puisqu'il s'agit d'un écouteur d'événement, nous pouvons avoir une variable automatique appelée e qui stocke des informations sur les événements comme les données de la souris. Mais comme différents navigateurs le stockent différemment, nous devons ajouter de nombreuses étapes supplémentaires..

Nous trouvons finalement mouseX en trouvant posX (qui est la position x de la souris sur la page) et en soustrayant la distance entre la scène et l'extrême gauche de la page (stockée dans stageOffset.xpos). Cela nous donne la distance qui sépare la souris du bord gauche de la scène, que nous pouvons directement comparer robot.offsetLeft. Étant donné que la scène peut être située différemment sur la page en fonction de la mise en page, nous devrons également trouver le décalage de pixel exact de la scène pour que la fonction soit précise et stocker ces informations dans stageOffset. Heureusement, il existe une astuce intéressante pour trouver le décalage absolu d'un élément avec cette fonction du blog de Vishal Astik.

 // Inside RobotMaker var x = 0; var y = 0; function find_stage_offset (el) x = el.offsetLeft; y = el.offsetTop; el = el.offsetParent; while (el! == null) x = parseInt (x) + parseInt (el.offsetLeft); y = parseInt (y) + parseInt (el.offsetTop); el = el.offsetParent;  return xpos: x, ypos: y;  var stageOffset = find_stage_offset (stage);

Alors maintenant que nous avons écrit le déplacer la souris auditeur, les autres seront beaucoup Plus facile. Pour le robot passer la souris écouteur, il suffit de vérifier si le robot est déjà en train de sauter, et dans le cas contraire, arrêtez le chronomètre.

 function robot_mouseover_listener () if (! jump_timer || jump_timer === non défini) clearTimeout (run_timer); jmp (true, robot.offsetTop); 

le sortie de souris auditeur est également assez simple. Nous avons juste besoin de réinitialiser certaines de nos variables que nous utilisons pour suivre le robot. Si le robot ne saute pas, renvoyez le robot dans l'image-objet qui se tient debout..

 function stage_mouseout_listener () mouseX = non défini; running_dir = "; if (! jump_timer || jump_timer === undefined) clearTimeout (run_timer); if (face_right) robot.style.backgroundPosition =" 0px 0px "; autre robot.style.backgroundPosition =" 0px - 50px ";

Fonctions d'animation

Les fonctions qui animent les mouvements de course et de saut n'ont pas beaucoup changé cette fois-ci. Nous venons d'ajouter la variable de suivi running_dir, pris la déclaration qui vérifie si le robot est sur le point de frapper le mur (car cela est redondant avec notre sortie de souris fonction) et ajoutez un peu de code à la fonction de saut qui vérifie à nouveau si le robot doit commencer à courir si la souris se trouve dans la phase qui suit son atterrissage après un saut. Voici le code final (assez gros):

 fonction run_r (phase, gauche) face_right = true; running_dir = 'r'; if ((left + (15 * run_speed)) < (mouseX - robot.offsetWidth)) // if mouse is to the right, run 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 if ((left + (15 * run_speed)) < mouseX)  // if mouse if above, stop robot.style.backgroundPosition = "0px 0px"; running_dir =";  else  // if mouse is to the left, run left running_dir = 'l'; run_l(1, robot.offsetLeft);   function run_l(phase, left) face_right = false; running_dir = 'l'; if (mouseX < robot.offsetLeft - (15 * run_speed)) // if mouse is to the left, run 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 if (mouseX < (robot.offsetLeft + robot.offsetWidth - (15 * run_speed))) // if mouse overhead, stop robot.style.backgroundPosition = "0px -50px"; running_dir =";  else  // if mouse is to the right, run right running_dir = 'r'; run_r(1, robot.offsetLeft);   function jmp(up, top) running_dir ="; 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 * 0.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 * 0.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; if (mouseX !== undefined) if (((robot.offsetLeft + (15 * run_speed)) < (mouseX - robot.offsetWidth)) && running_dir !== 'r') // make run right, if not already running_dir = 'r'; clearTimeout(run_timer); run_r(1, robot.offsetLeft);  else if ((mouseX < robot.offsetLeft - (15 * run_speed)) && running_dir !== 'l')  // make run left, if not already running_dir = 'l'; clearTimeout(run_timer); run_l(1, robot.offsetLeft);    

Nous avons donc maintenant nos fonctions réécrites qui fonctionnent très bien sur tous les navigateurs… sauf si ces navigateurs ont une entrée tactile. Il nous reste encore un peu de chemin à parcourir pour que nos robots fonctionnent sur tout. Étant donné que les écrans tactiles se comportent un peu différemment, nous aurons besoin de coder davantage nos auditeurs d’événements..

Soutenir les écrans tactiles

Nous devons établir de nouvelles règles pour les écrans tactiles: si vous touchez l'écran n'importe où dans la scène, le robot se dirigera vers cet endroit jusqu'à ce que le doigt soit levé. Si l'utilisateur touche le robot, le robot va sauter. Tout d’abord, nous allons ajouter quelques gestionnaires d’événements tactiles supplémentaires à notre fonction précédente, et nous allons écrire le code de manière à ce qu’il s’exécute automatiquement à chaque RobotMaster la fonction s'appelle.

 (function () if (stage.addEventListener) stage.addEventListener ('touchstart', stage_mousemove_listener, false); stage.addEventListener ('touchmove', stage_mousemove_listener, false); stage.addEventListener ('touchmove', stage_mousemove_listener, false); stage.addEventListener ('touchend', stage_mouseout_list) ; stage.addEventListener ('mousemove', stage_mousemove_listener, false); robot.addEventListener ('mouseover', robot_mouseover_listener, false); stage.addEventListener ('mouseout', stage_mouseout_listener, false);  

Nous n'aurons pas à nous soucier du fait que les écouteurs tactiles sont au format Internet Explorer 8, et si un périphérique n'a pas de support tactile, il ignorera les écouteurs. Maintenant nous devrons mettre à jour le stage_mousemove_listener () fonction de se comporter différemment si le navigateur a la capacité tactile.

 function stage_mousemove_listener (e) / * * Commençons par vérifier s’il s’agit d’un appareil à écran tactile (s’il possède des e.touches) * / if (e.touches) e.preventDefault (); // nous voulons annuler ce que le navigateur ferait habituellement s'il y était touché // Si le contact était dans les limites de la scène… if ((e.touches [0] .pageX> stageOffset.xpos) && (e.touches [ 0] .pageX < (stageOffset.xpos + stage.offsetWidth)) && (e.touches[0].pageY > stageOffset.ypos) && (e.touches [0] .pageY < (stageOffset.ypos + stage.offsetHeight))) // we set the mouseX to equal the px location inside the stage mouseX = e.touches[0].pageX - stageOffset.xpos;  else  // if the touch was outside the stage, we call the mouseout listener stage_mouseout_listener();  /* * If the touch is directly on the robot, then we stop the run timer and make the robot jump */ if ((e.touches[0].pageX > robot.offsetLeft) && (e.touches [0] .pageX < (robot.offsetLeft + robot.offsetWidth)) && (e.touches[0].pageY > (stageOffset.ypos + stage.offsetHeight - robot.offsetHeight)) && (e.touches [0] .pageY < (stageOffset.ypos + stage.offsetHeight)) && (!jump_timer || jump_timer === undefined)) clearTimeout(run_timer); jmp(true, robot.offsetTop);   else  // Finding the mouseX for non-touch devices… // All of our non-touch device code here  

Vous remarquerez peut-être que nous n’avons plus de "portes" dans notre RobotMaker fonction, mais puisque nous appelons tout notre code avec les gestionnaires d'événements que nous assignons à l'intérieur RobotMaker, on n'a plus besoin d'eux! Pour notre scène et nos personnages, nous voudrons ajouter un peu de CSS spécialement pour les appareils tactiles afin d'éviter toute opération de copier-coller d'images lorsqu'un utilisateur les maintient enfoncées..

 #stage, .character -webkit-user-select: none; 

Enfin, nous allons déclarer tous nos robots au bas de la page, en utilisant le même format que notre fonction de gestionnaire d’événements pour que le code s’exécute automatiquement lors du chargement de la page. Cette méthode empêche également ces objets de robot d’être des variables globales. la seule variable globale que nous ayons dans tout ce script est la RobotMaker () une fonction.

 (function () var j = RobotMaker (document.getElementById ('j'), 1, 1); var j2 = RobotMaker (document.getElementById ('j2'), .8, 5); var j3 = RobotMaker (document .getElementById ('j3'), 1.1, .5); var j4 = RobotMaker (document.getElementById ('j4'), .5, .75);) ();

S'il vous plaît vérifier le résultat final dans toute sa gloire!

Conclusion

Je vous encourage fortement à étudier le code entier (et entièrement commenté!), Et vous pouvez télécharger les quatre sprites de robot ici.

Bonne animation!