JavaScript-animatie die werkt (deel 4 van 4)

In het eerste deel van deze serie introduceerden we het idee om te gebruiken spriting als een eenvoudige, browserinteractieve manier om interactieve animaties voor internet te hebben. In het tweede deel werkten we met animatie en in de derde fase hebben we onze code opgeruimd en klaargemaakt voor het web.

Invoering

Nu, in ons laatste deel vandaag, zullen we door het opzetten lopen event handlers zodat in plaats van te reageren op geklikte knoppen, onze robots de muis over het scherm zullen volgen. Tijdens dit proces zullen we ook praten over het gebruiksvriendelijk maken van de code en het aanraakscherm ingeschakeld.

Als u onze code van de vorige keer bekijkt, ziet u dat terwijl de code goed werkt (en met meerdere robots), er geen eenvoudige manier is om de code te laten zien.

Event Handlers

Handlers voor evenementen zijn opdrachten die aangeven dat bepaalde code moet worden uitgevoerd wanneer bepaalde gebeurtenissen worden geactiveerd. Dat zou je bijvoorbeeld kunnen hebben my_function () uitvoeren wanneer een gebruiker op uw klikt div met de id 'My_div'. Of, je zou kunnen hebben my_other_function () uitvoeren wanneer een gebruiker zijn muis beweegt 'My_other_div'.

In theorie is dit een vrij eenvoudig en duidelijk idee. Helaas kan dit een beetje verwarrend zijn als je verschillende browsers begint te gebruiken. In een ideale wereld zou elke webbrowser dezelfde code en HTML op dezelfde manier interpreteren, en ontwikkelaars zouden code een keer schrijven en het zou voor elke gebruiker hetzelfde werken. In de echte wereld kunnen verschillende browsers compleet verschillende opdrachten hebben om hetzelfde te doen (* hoest * * hoest * Internet Explorer), en dus soms proberen om een ​​enkel stuk code te krijgen om hetzelfde in alle browsers te gebruiken, het voelt aan als katten hoeden. Onlangs is de situatie veel beter geworden, want Chrome, Firefox, Safari en Opera reageren allemaal erg op code, Internet Explorer 9 en 10 zijn veel meer in overeenstemming met de standaarden dan eerdere versies en bijna niemand gebruikt Internet Explorer 7 of 6 meer. Dus voor onze code zullen we event handlers laten werken voor zowel moderne browsers als Internet Explorer 8.

Als een kanttekening is dit een geval waarbij het echt loont om een ​​robuuste JavaScript-bibliotheek te gebruiken, zoals jQuery. jQuery doet al het werk voor u in cross-browser testen, dus u hoeft slechts één opdracht in te voeren en de jQuery-bibliotheek vertaalt deze voor elke browser achter de schermen. Bovendien zijn veel van de opdrachten in jQuery veel intuïtiever en eenvoudiger dan de kern JavaScript.

Maar omdat ik koppig ben, en omdat dit een leerkans is, gaan we door op de harde manier en doen dit alles alleen met JavaScript en geen afhankelijkheden!

Pagina-interactie

Dus, onze eerste stap zal zijn om te beslissen hoe we precies willen omgaan met de pagina. Als ik mijn muis over het toneelgebied beweeg, wil ik dat alle robots naar de muis rennen. Als ze de muis bereiken of als de muis er recht boven staat, wil ik dat ze stoppen met rennen. Als de muis over ze gaat, wil ik dat ze springen. En tot slot, wanneer de muis het toneelgebied verlaat, wil ik dat ze stoppen met rennen. We beginnen met het toevoegen van deze gebeurtenissen binnen de RobotMaker functie:

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

Dus, in de bovenstaande regels, hebben we gezegd dat wanneer de gebruiker de muis binnen het fase-element beweegt, we een functie genaamd activeren stage_mousemove_listener () (merk op dat we de haakjes niet opnemen in de opdracht). Op dezelfde manier wordt de muis geactiveerd wanneer de gebruiker de muis over het robotelement beweegt robot_mouseover_listener (), en wanneer de gebruiker de muis buiten het podium beweegt, wordt deze geactiveerd stage_mouseout_listener ().

Helaas, zoals we eerder hebben vermeld, heeft Internet Explorer 8 en lager een (vergelijkbare maar) andere opdracht om hetzelfde te doen, dus we zullen moeten testen om te weten welke opdracht de browser van de gebruiker zal begrijpen en die methode zal gebruiken.

 if (stage.addEventListener) // We zullen testen om te zien of dit commando beschikbaar is stage.addEventListener ('mousemove', stage_mousemove_listener, false); robot.addEventListener ('mouseover', robot_mouseover_listener, false); stage.addEventListener ('mouseout', stage_mouseout_listener, false);  else // Zo niet, dan moeten we IE-commando's stage.attachEvent ('onmousemove', stage_mousemove_listener) gebruiken; robot.attachEvent ('onmouseover', robot_mouseover_listener); stage.attachEvent ('onmouseout', stage_mouseout_listener); 

Je zult misschien opmerken dat het formaat van de commando's erg op elkaar lijkt, maar enkele grote verschillen vertoont - zegt iemand 'AddEventListener' terwijl de andere zegt 'AttachEvent'. Een zegt 'Mousemove' terwijl de andere zegt 'OnMouseMove'. Een vereist een derde parameter, terwijl de andere slechts twee gebruikt. Als u een van deze samenvoegt, wordt de opdracht niet uitgevoerd. Dit zijn de dingen die ervoor zorgen dat je met je hoofd tegen de muur wilt slaan. Helaas is dit niet het einde van de extra codering die we moeten doen voor cross-browserfunctionaliteit.

Luisterfuncties

Vervolgens gaan we de luisterfuncties schrijven. We beginnen met de functie die wordt geactiveerd wanneer de gebruiker over het podium migreert. Aangezien dit een is mousemove luisteraar, deze functie wordt geactiveerd elke keer dat de muis binnen het toneelgebied wordt verplaatst (wat betekent dat het meerdere keren per seconde zal worden geactiveerd terwijl de muis beweegt). Deze functie moet de locatie van de robot vergelijken met de locatie van de muis en ervoor zorgen dat de robot zich overeenkomstig gedraagt. Elke keer dat de functie wordt geactiveerd, controleert deze of de robot dezelfde richting moet blijven volgen of gedrag moet veranderen. Het moet dus zoiets zijn als dit:

 // Inside of RobotMaker // We zullen een paar extra variabelen introduceren om var mouseX te volgen; // Voor het volgen van de horizontale muispositie var running_dir = "; // Voor het volgen als (en waar) de robot momenteel wordt uitgevoerd var stageOffset; // Voor het volgen van de positie van de stagefunctie stage_mousemove_listener (e) // Zoek de horizontale positie van de muis binnenin het podium ... // Die positie wordt opgeslagen in 'mouseX' // Vervolgens vergelijken we 'mouseX' met de robot en besluiten we of we anders moeten rennen als (((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)) // Als de muis zich in het werkgebied en boven een robot bevindt, stop en verwijder running_dir running_dir ="; clearTimeout (run_timer); if (face_right) robot.style.backgroundPosition = "0px 0px";  else robot.style.backgroundPosition = "0px -50px";  // Als geen van de bovenstaande waar is, laten we ons huidige gedrag doorgaan

Dus, in de bovenstaande functie, als we eenmaal in staat zijn om te vinden mouseX, we vergelijken het met waar de robot zich bevindt en activeren of stoppen de verschillende actieve functies als dat nodig is. Helaas, vinden mouseX is een beetje lastig, omdat muispositie iets anders is dat verschillende browsers anders doen. In plaats van (meer) gecompliceerde en langdradige verklaringen, hier is de cross-browser methode om te vinden mouseX, zoals geïnspireerd door het uitstekende Quirksmode-blog (wat een geweldige bron is voor geavanceerder JavaScript-onderzoek).

 functie 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; // En we vinden mouseX! 

We hebben een argument genaamd e in de functie, ook al geven we er niets aan. Omdat dit een gebeurtenislistener is, kunnen we een automatische variabele laten gebruiken e die gebeurtenisinformatie zoals muisgegevens opslaat. Maar omdat verschillende browsers het anders opslaan, moeten we veel extra stappen toevoegen.

We vinden het eindelijk mouseX door te vinden posx (dit is de x-positie van de muis op de pagina) en het aftrekken van hoe ver de fase is helemaal links op de pagina (opgeslagen in stageOffset.xpos). Dit geeft ons hoe ver van de linkerrand van het podium de muis is, waarmee we direct kunnen vergelijken robot.offsetLeft. Omdat de fase afhankelijk van de lay-out anders rond de pagina kan worden geplaatst, moeten we ook de exacte pixelverschuiving van de fase vinden om de functie nauwkeurig te laten zijn en deze informatie op te slaan in stageOffset. Gelukkig is er een handige truc die we kunnen gebruiken om de absolute offset van een element met deze functie te vinden in de blog van 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);

Dus nu dat we het hebben geschreven mousemove luisteraar, de anderen zullen zijn veel gemakkelijker. Voor de robot mouseover luisteraar, we hoeven alleen maar te controleren of de robot al aan het springen is, en zo niet, stop dan de uitvoertimer en laat hem springen.

 functie robot_mouseover_listener () if (! jump_timer || jump_timer === undefined) clearTimeout (run_timer); jmp (true, robot.offsetTop); 

De mouseout luisteraar is ook vrij eenvoudig. We moeten alleen enkele van onze variabelen die we gebruiken om de robot te volgen, resetten en als de robot niet springt, breng de robot dan terug naar de staande sprite.

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

Animatiefuncties

De functies die de loop- en springbewegingen animeren zijn deze keer niet veel veranderd. We hebben zojuist de tracking-variabele toegevoegd running_dir, haalde de verklaring weg die controleert of de robot op het punt staat de muur te raken (aangezien dit overbodig is met onze mouseout functie), en voeg een stukje code toe aan de sprongfunctie die opnieuw controleert of de robot moet beginnen met rennen als de muis zich in de fase bevindt nadat hij na een sprong landt. Hier is de definitieve code (vrij groot):

 functie run_r (fase, links) 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 (up, top);, 60);  else if (up) up = false; jump_timer = setTimeout (function () jmp (up, top);, 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);    

Dus nu hebben we onze herschreven functies die goed werken in alle browsers ... tenzij die browsers invoerinvoer hebben. We moeten nog een beetje meer doen om onze robots overal op te laten draaien. Omdat de aanraakschermen zich een beetje anders gedragen, moeten we wat extra codering uitvoeren bij onze gebeurtenisluisteraars.

Ondersteunende aanraakschermen

We moeten een aantal nieuwe regels voor aanraakschermen maken: als het scherm ergens in het werkgebied wordt aangeraakt, zal de robot naar die plek rennen totdat de vinger wordt opgetild. Als de gebruiker de robot aanraakt, springt de robot. Allereerst zullen we wat extra touch event-handlers toevoegen aan onze eerdere functie, en we gaan de code zo schrijven dat deze automatisch wordt uitgevoerd wanneer de RobotMaster functie wordt aangeroepen.

 (function () if (stage.addEventListener) stage.addEventListener ('touchstart', stage_mousemove_listener, false); stage.addEventListener ('touchmove', stage_mousemove_listener, false); stage.addEventListener ('touchend', stage_mouseout_listener, false) ; stage.addEventListener ('mousemove', stage_mousemove_listener, false); robot.addEventListener ('mouseover', robot_mouseover_listener, false); stage.addEventListener ('mouseout', stage_mouseout_listener, false); else stage.attachEvent ('onmousemove' , stage_mousemove_listener); robot.attachEvent ('onmouseover', robot_mouseover_listener); stage.attachEvent ('onmouseout', stage_mouseout_listener);) ();

We hoeven ons geen zorgen te maken dat de touchlisteners de indeling Internet Explorer 8 hebben en als een apparaat geen aanraakondersteuning heeft, negeert het de luisteraars. Nu moeten we het updaten stage_mousemove_listener () functie om zich anders te gedragen als de browser over aanraakmogelijkheden beschikt.

 functie stage_mousemove_listener (e) / * * Eerst controleren we of dit een touchscreen-apparaat is (als het e.touches heeft) * / if (e.touches) e.preventDefault (); // we willen annuleren wat de browser gewoonlijk zou doen als daar aangeraakt // Als de aanraking binnen de grenzen van het werkgebied lag ... 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.offsetLinks) && (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  

Je zou kunnen opmerken dat we geen 'deuren' meer hebben in onze RobotMaker functie, maar omdat we al onze code bellen met event handlers die we binnen toewijzen RobotMaker, we hebben ze niet langer nodig! Voor zowel onze stage, als onze personages, zullen we een beetje CSS willen toevoegen speciaal voor touch-apparaten, zodat het niet zal proberen om afbeeldingen te knippen en te plakken wanneer een gebruiker een vinger op ze houdt.

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

En ten slotte zullen we al onze robots aan de onderkant van de pagina declareren, gebruikmakend van hetzelfde formaat als onze gebeurtenishandlerfunctie om de code automatisch te laten lopen wanneer de pagina wordt geladen - deze methode voorkomt ook dat deze robotobjecten globale variabelen zijn, dus de enige globale variabele die we in dit hele script hebben is de RobotMaker () functie.

 (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);) ();

Lees het eindresultaat in al zijn glorie!

Conclusie

Ik moedig je sterk aan om de volledige (en volledig gecommenteerde!) Code te bestuderen, en je kunt alle vier robotsprites hier ook downloaden.

Blij geanimeerd!