JavaScript-animering som fungerar (del 4 av 4)

I den första delen av denna serie introducerade vi idén att använda spriting som en enkel, cross-browser sätt att ha interaktiv animering för webben. I den andra delen har vi lite animering, och i tredje rensade vi upp vår kod och gjorde den redo för webben.

Introduktion

Nu, i vår sista del idag, kommer vi att gå igenom inrättandet händelsehanterare så att istället för att svara på klickade knappar följer våra robotar musen runt skärmen. I processen kommer vi också att prata om att koden är öppen för webbläsarvänlig och pekskärm aktiverad.

Om du tittar på vår kod från förra gången ser du att koden går bra (och med flera robotar), det är inte en väldigt lätt sätt att berätta koden för att köra.

Event Handlers

Händelseshanterare Kommandon som säger att vissa koden ska köras när vissa händelser utlöses. Till exempel kan du ha my_function () kör när en användare klickar på din div med id 'My_div'. Eller kan du ha my_other_function () springa när en användare flyttar musen över 'My_other_div'.

I teorin är det här en ganska enkel och okomplicerad idé. Tyvärr kan det bli lite förvirrande när du börjar få olika webbläsare. I en idealisk värld skulle alla webbläsare tolka samma kod och HTML på samma sätt, och utvecklare skulle skriva kod en gång och det skulle fungera lika för varje användare. I den verkliga världen kan olika webbläsare ha helt olika kommandon för att göra samma sak (* hosta * * hosta * Internet Explorer), och så försöker det ibland att få en enda kod köra för att köra samma på alla webbläsare kan kännas som herding katter. Nyligen har situationen blivit mycket bättre, eftersom Chrome, Firefox, Safari och Opera alla svarar mycket på samma sätt som kod, har Internet Explorer 9 och 10 blivit mycket mer i linje med standarder än tidigare versioner, och nästan ingen använder Internet Explorer 7 eller 6 längre. Så, för vår kod kommer vi att få händelsehanterare att arbeta för både moderna webbläsare och Internet Explorer 8.

Som en sidnot är detta ett fall där det verkligen betalar att använda ett robust JavaScript-bibliotek, till exempel jQuery. jQuery gör allt arbete för dig i cross-browser-testning, så du behöver bara ange ett kommando och jQuery-biblioteket kommer att översätta det för varje webbläsare bakom kulisserna. Dessutom är många av kommandona i jQuery mycket mer intuitiva och enklare än kärnan JavaScript också.

Men eftersom jag är envis och eftersom det här är en lärande möjlighet, fortsätter vi på det svåra sättet och gör allt detta enbart med JavaScript och inga beroenden!

Sidinteraktion

Så vårt första steg är att bestämma hur exakt vi vill interagera med sidan. När jag flyttar musen över scenområdet vill jag att alla robotar ska springa mot musen. När de når musen, eller om musen är direkt ovanför dem, vill jag att de ska sluta springa. Om musen korsar dem, vill jag att dom ska hoppa. Och slutligen, när musen lämnar scenområdet, vill jag att de ska sluta springa. Vi börjar med att fästa dessa händelser inuti RobotMaker fungera:

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

Så i ovanstående rader har vi sagt att när användaren flyttar musen inuti scenelementet kommer vi att utlösa en funktion som heter stage_mousemove_listener () (observera att vi inte inkluderar parenteserna i kommandot). På samma sätt, när användaren flyttar musen över robotelementet triggas det robot_mouseover_listener (), och när användaren flyttar musen utanför scenen triggar den stage_mouseout_listener ().

Tyvärr, som vi nämnde tidigare, har Internet Explorer 8 och nedan ett (liknande men) annat kommando för att göra samma sak, så vi måste testa för att veta vilket kommando användarens webbläsare förstår och göra den metoden.

 om (stage.addEventListener) // Vi kommer att testa för att se om det här kommandot är tillgängligt stadium.addEventListener ('mousemove', stage_mousemove_listener, false); robot.addEventListener ('mouseover', robot_mouseover_listener, false); stage.addEventListener ('mouseout', stage_mouseout_listener, false);  else // Om inte, måste vi använda IE-kommandon stage.attachEvent ('onmousemove', stage_mousemove_listener); robot.attachEvent ('onmouseover', robot_mouseover_listener); stage.attachEvent ('onmouseout', stage_mouseout_listener); 

Du kanske märker att kommandonas format är mycket likartat men har några stora skillnader, säger man 'Addeventlistener' medan den andra säger 'AttachEvent'. En säger 'Mouse' medan den andra säger 'OnMouseMove'. Man kräver en tredje parameter, medan den andra endast använder två. Om du blandar någon av dessa kommer kommandot att inte springa. Det här är de saker som gör att du vill knäcka huvudet mot väggen. Tyvärr är detta inte slutet på den extra kodning som vi behöver göra för cross-browser kapacitet.

Lyssna funktioner

Därefter ska vi skriva lyssningsfunktionerna. Vi börjar med den funktion som utlöses när användaren musar över scenen. Eftersom detta är en mouse lyssnare, kommer denna funktion att trigga varje gång musen flyttas inuti scenområdet (vilket betyder att det kommer att utlösas flera gånger i sekundet medan musen rör sig). Denna funktion kommer att behöva jämföra robotens placering med musens placering och göra roboten i enlighet med detta. Varje gång funktionen utlöses, kommer den att kontrollera om roboten behöver fortsätta att köra i samma riktning eller ändra beteenden. Så måste det vara något så här:

 // Inne i RobotMaker // Vi måste introducera några extra variabler för att spåra var mouseX; // För spårning av horisontell musposition var running_dir = "; // För spårning om (och var) robot körs för närvarande var stageOffset; // För att spåra positionen för scenfunktionen stage_mousemove_listener (e) // Hitta den horisontella positionen av musen inuti scenen ... // Den positionen sparas i 'mouseX' // Då jämför vi 'mouseX' till roboten och bestämmer om vi behöver springa annorlunda om (((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)) // Om musen befinner sig i scenen och över en robot, stoppa och radera run_dir running_dir ="; cleartimeout (run_timer); om (face_right) robot.style.backgroundPosition = "0px 0px";  annars robot.style.backgroundPosition = "0px -50px";  // Om inget av ovanstående är sant, låt vi vårt nuvarande beteende fortsätta

Så, i funktionen ovan, när vi väl kan hitta mouseX, vi jämför det där roboten är och utlösar eller stoppar de olika körfunktionerna efter behov. Tyvärr, att hitta mouseX är lite knepigt, eftersom muspositionen är en annan sak som olika webbläsare gör annorlunda. Istället för (mer) komplicerade och långvariga förklaringar, här är cross-browser-metoden för att hitta mouseX, som inspirerad av den utmärkta Quirksmode-bloggen (som är en bra källa för mer avancerad JavaScript-studie).

 funktion stage_mousemove_listener (e) var posX = 0; om (! e) var e = window.event;  om (e.pageX) posX = e.pageX;  annars om (e.clientX) posX = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;  mouseX = posX - stageOffset.xpos; // Och vi hittar mouseX! 

Vi har ett argument som heter e i funktionen, även om vi inte överför det någonting. Eftersom det här är en händelselysare kan vi få en automatisk variabel som heter e som lagrar händelseinformation som musdata. Men eftersom olika webbläsare lagrar det annorlunda måste vi lägga till många extra steg.

Vi hittar äntligen mouseX genom att hitta posX (vilket är musens x-position på sidan) och subtraherar hur långt scenen ligger längst till vänster på sidan (lagrad i stageOffset.xpos). Detta ger oss hur långt från muskels vänstra kant musen är, vilket vi direkt kan jämföra med robot.offsetLeft. Eftersom scenen kunde vara placerad annorlunda runt sidan beroende på layouten, måste vi också hitta den exakta pixelförskjutningen för scenen för att funktionen ska vara korrekt och lagra den informationen i stageOffset. Lyckligtvis finns det ett snyggt trick som vi kan använda för att hitta ett elements absoluta kompensation med denna funktion från Vishal Astiks blogg.

 // Inside RobotMaker var x = 0; var y = 0; funktion find_stage_offset (el) x = el.offsetLeft; y = el.offsetTop; el = el.offsetParent; medan (el! = null) x = parseInt (x) + parseInt (el.offsetLeft); y = parseInt (y) + parseInt (el.offsetTop); el = el.offsetParent;  returnera xpos: x, ypos: y;  var stageOffset = find_stage_offset (scen);

Så nu har vi skrivit mouse lyssnare, de andra kommer att vara mycket lättare. För roboten mouseover lyssnare, vi behöver bara kontrollera om roboten redan hoppar, och om inte, stoppa körtimern och få den att hoppa.

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

De mouseOut lyssnaren är också ganska enkel. Vi behöver bara nollställa några av våra variabler vi använder för att spåra roboten, och om roboten inte hoppar, returnera roboten till stående sprite.

 funktion stage_mouseout_listener () mouseX = undefined; robot.style.backgroundPosition = "0px 0px"; annars robot.style.backgroundPosition = "0px - 50px ";

Animationsfunktioner

Funktionerna som animerar löpande och hoppande rörelser har inte förändrats mycket denna gång. Vi har just lagt till spårningsvariabeln running_dir, tagit ut uttalandet som kontrollerar om roboten är på väg att slå väggen (eftersom detta är överflödigt med vårt mouseOut funktion) och lägg till en bit kod till hoppfunktionen som kontrollerar igen om roboten ska börja springa om musen ligger inom scenen efter det landar från ett hopp. Här är den sista koden (ganska stor):

 funktion run_r (fas, vänster) face_right = true; running_dir = 'r'; om ((vänster + (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 / hopphöjd)))) topp = topp - (topp * 0,1); robot.style.top = top + "px"; jump_timer = setTimeout (funktion () jmp (upp, topp);, 60);  annars om (upp) upp = false; jump_timer = setTimeout (funktion () jmp (upp, topp);, 60);  annars om (! 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);    

Så nu har vi våra omskrivna funktioner som fungerar bra över alla webbläsare ... om inte webbläsarna har kontakt med inmatningen. Vi har fortfarande lite mer att gå för att få våra robotar att springa på allt. Eftersom pekskärmarna beter sig lite annorlunda måste vi göra extra kodning på våra händelselysare.

Stödja pekskärmar

Vi behöver göra några nya regler för pekskärmar: Om skärmen berörs var som helst i scenen kommer roboten att springa till den platsen tills fingret lyftes. Om användaren rör på roboten kommer roboten att hoppa. Först och främst lägger vi till några extra touch händelsehanterare till vår tidigare funktion, och vi ska skriva koden på ett sådant sätt att den kommer att köras automatiskt när RobotMaster funktion kallas.

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

Vi behöver inte oroa oss för att pekelyttarna är i Internet Explorer 8-formatet, och om någon enhet inte har beröringsstöd, ignorerar den lyssnaren. Nu behöver vi uppdatera stage_mousemove_listener () funktionen att fungera annorlunda om webbläsaren har beröringsfunktion.

 funktion stage_mousemove_listener (e) / * * Först kontrollerar vi om det här är en pekskärmsenhet (om den har e.touches) * / if (e.touches) e.preventDefault (); // vi vill avbryta vad webbläsaren brukar göra om det berörs // Om kontakten ligger inom scenens gränser ... om ((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  

Du märker kanske att vi inte längre har några "dörrar" i vår RobotMaker funktion, men eftersom vi ringer all vår kod med händelsehanterare som vi tilldelar inuti RobotMaker, vi behöver dem inte längre! För både vårt stadium och våra tecken vill vi lägga till lite CSS speciellt för pekdon, så det kommer inte att försöka klippa och klistra in några bilder när en användare håller ett finger på dem.

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

Och slutligen kommer vi att förklara alla våra robotar längst ner på sidan, med samma format som vår händelsehanteringsfunktion för att koden ska köra automatiskt när sidan laddas - den här metoden hindrar också dessa robotobjekt från att vara globala variabler, så Den enda globala variabeln som vi har i hela detta skript är RobotMaker () fungera.

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

Vänligen kassera det slutliga resultatet i all sin ära!

Slutsats

Jag uppmanar dig att studera hela (och fullt kommenterad!) -Koden, och du kan också ladda ner alla fyra robot sprites här också.

Glad animering!