JavaScript-animasjon som virker (del 4 av 4)

I den første delen av denne serien presenterte vi ideen om å bruke spriting som en enkel, kryss-leser måte å ha interaktiv animasjon for nettet. I den andre delen fikk vi litt animasjon, og i tredje rydde vi opp koden vår og gjorde den klar for nettet.

Introduksjon

Nå, i vår siste del i dag, vil vi gå gjennom å sette opp hendelseshåndterere slik at i stedet for å svare på klikkte knapper, vil robotene våre følge musen rundt skjermen. I prosessen vil vi også snakke om å gjøre koden til nettleservennlig og berøringsskjerm aktivert.

Hvis du ser på koden fra siste gang, vil du se at mens koden går bra (og med flere roboter), er det ikke en veldig enkel måte å fortelle at koden skal løpe.

Event Handlers

Hendelsehåndterere er kommandoer som forteller at bestemt kode skal løpe når visse hendelser utløses. For eksempel kan du ha my_function () Kjør når en bruker klikker på din div med id 'My_div'. Eller, du kan ha my_other_function () Kjør når en bruker beveger musen over 'My_other_div'.

I teorien er dette en ganske enkel og grei ide. Dessverre, når du begynner å få forskjellige nettlesere involvert, kan dette bli litt forvirrende. I en ideell verden ville hver nettleser tolke samme kode og HTML på samme måte, og utviklere ville skrive kode én gang, og det ville fungere det samme for hver bruker. I den virkelige verden kan forskjellige nettlesere ha helt forskjellige kommandoer for å gjøre det samme (* hoste * * hoste * Internet Explorer), og det kan hende at noen ganger prøver å få et enkelt stykke kode for å kjøre det samme på alle nettlesere, føles som herdingskatter. Nylig har situasjonen blitt mye bedre, siden Chrome, Firefox, Safari og Opera alle reagerer på samme måte som kode, har Internet Explorer 9 og 10 blitt mye mer i tråd med standarder enn tidligere versjoner, og nesten ingen bruker Internet Explorer 7 eller 6 lenger. Så, for vår kode, vil vi få hendelsehåndterere til å jobbe for både moderne nettlesere og Internet Explorer 8.

Som en sidemerking er dette et tilfelle der det virkelig lønner seg å bruke et robust JavaScript-bibliotek, for eksempel jQuery. jQuery gjør alt arbeidet for deg i kryss-nettlesertesting, så du trenger bare å skrive inn en kommando, og jQuery-biblioteket vil oversette det for hver nettleser bak kulissene. I tillegg er mange av kommandoene i jQuery mye mer intuitive og enklere enn kjerne JavaScript også.

Men siden jeg er sta, og siden dette er en læringsmulighet, skal vi fortsette på den harde måten og gjøre alt dette utelukkende med JavaScript og ingen avhengighet!

Side-interaksjon

Så, vårt første skritt vil være å bestemme hvor nøyaktig vi vil samhandle med siden. Når jeg flytter musen over scenen, vil jeg at alle roboter skal løpe mot musen. Når de når musen, eller hvis musen er rett over dem, vil jeg at de skal slutte å løpe. Hvis musen krysser over dem, vil jeg at de skal hoppe. Og til slutt, når musen forlater sceneområdet, vil jeg at de skal slutte å løpe. Vi starter med å legge ved disse hendelsene i RobotMaker funksjon:

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

Så, i de ovennevnte linjene, har vi sagt at når brukeren beveger musen inne i sceneelementet, utløser vi en funksjon som kalles stage_mousemove_listener () (legg merke til at vi ikke inkluderer parentesene i kommandoen). På samme måte, når brukeren beveger musen over robotelementet, utløses det robot_mouseover_listener (), og når brukeren beveger musen utenfor scenen, trigger den stage_mouseout_listener ().

Dessverre, som nevnt tidligere, har Internet Explorer 8 og under en (lignende) annen kommando for å gjøre det samme, så vi må teste for å vite hvilken kommando brukerens nettleser vil forstå og gjøre den metoden.

 if (stage.addEventListener) // Vi vil teste for å se om denne kommandoen er tilgjengelig stage.addEventListener ('mousemove', stage_mousemove_listener, false); robot.addEventListener ('mouseover', robot_mouseover_listener, false); stage.addEventListener ('mouseout', stage_mouseout_listener, false);  else // Hvis ikke, må vi bruke IE kommandoer stage.attachEvent ('onmousemove', stage_mousemove_listener); robot.attachEvent ('onmouseover', robot_mouseover_listener); stage.attachEvent ('onmouseout', stage_mouseout_listener); 

Du kan legge merke til at formatet på kommandoene er veldig liknende, men har noen store forskjeller - en sier 'AddEventListener' mens den andre sier 'AttachEvent'. En sier 'Mousemove' mens den andre sier 'Onmousemove'. Man krever en tredje parameter, mens den andre bare bruker to. Å blande noen av disse vil føre til at kommandoen ikke kjører. Dette er slags ting som vil få deg til å slå hodet mot veggen. Dessverre er dette ikke slutten på den ekstra kodingen vi må gjøre for kryssbrowser-funksjonalitet.

Lyttingsfunksjoner

Deretter skal vi skrive lyttingsfunksjonene. Vi vil starte med funksjonen som utløses når brukeren musker over scenen. Siden dette er en mousemove lytter, vil denne funksjonen utløse hver gang musen flyttes inne i sceneområdet (det betyr at det vil utløse flere ganger et sekund mens musen beveger seg). Denne funksjonen må sammenligne plasseringen av roboten med musens plassering og gjøre roboten oppførsel tilsvarende. Hver gang funksjonen utløses, vil den kontrollere om roboten må fortsette å kjøre i samme retning eller endre atferd. Så, det må være noe slikt:

 // Inside of RobotMaker // Vi må introducere noen ekstra variabler for å spore var mouseX; // For sporing av horisontal musposisjon var running_dir = "; // For å spore om (og hvor) roboten for øyeblikket kjører var stageOffset; // For å spore posisjonen til scenefunksjonen stage_mousemove_listener (e) // Finn den horisontale posisjonen til musen inne i scenen ... // Denne stillingen vil bli lagret i 'mouseX' // Da sammenligner vi 'mouseX' med roboten og bestemmer om vi må kjøre annerledes hvis (((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)) // Hvis musen er i scenen og over en robot, stopp og fjern run_dir running_dir ="; cleartimeout (run_timer); hvis (face_right) robot.style.backgroundPosition = "0px 0px";  ellers robot.style.backgroundPosition = "0px -50px";  // Hvis ingen av de ovennevnte er sant, lar vi vår nåværende oppførsel fortsette

Så, i funksjonen ovenfor, når vi er i stand til å finne mouseX, Vi sammenligner det med hvor roboten er og utløser eller stopper de forskjellige løpefunksjonene etter behov. Dessverre, å finne mouseX er litt vanskelig, siden museposisjon er en annen ting som forskjellige nettlesere gjør annerledes. I stedet for (mer) kompliserte og langvarige forklaringer, her er kryssbrowser-metoden for å finne mouseX, som inspirert av den utmerkede Quirksmode bloggen (som er en god kilde til mer avansert JavaScript-studier).

 funksjon stage_mousemove_listener (e) var posX = 0; hvis (! e) var e = window.event;  hvis (e.pageX) posX = e.pageX;  annet hvis (e.clientX) posX = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;  mouseX = posX - stageOffset.xpos; // Og vi finner mouseX! 

Vi har et argument som heter e i funksjonen, selv om vi ikke overfører det noe. Siden dette er en hendelselytter, kan vi få en automatisk variabel kalt e som lagrer hendelsesinformasjon som musdata. Men fordi forskjellige nettlesere lagrer det annerledes, må vi legge til mange ekstra trinn.

Vi finner endelig mouseX ved å finne POSX (som er x-posisjonen til musen på siden) og subtraherer hvor langt scenen er fra lengst til venstre på siden (lagret i stageOffset.xpos). Dette gir oss hvor langt fra venstre kant av scenen musen er, som vi direkte kan sammenligne med robot.offsetLeft. Siden scenen kan ligge annerledes rundt siden avhengig av oppsettet, må vi også finne den eksakte pixelforskyvningen av scenen for at funksjonen skal være nøyaktig, og lagre den informasjonen i stageOffset. Heldigvis er det et pent trick vi kan bruke til å finne et elements absolutte kompensasjon med denne funksjonen fra Vishal Astiks blogg.

 // Inside RobotMaker var x = 0; var y = 0; funksjon find_stage_offset (el) x = el.offsetLeft; y = el.offsetTop; el = el.offsetParent; mens (el! = null) x = parseInt (x) + parseInt (el.offsetLeft); y = parseInt (y) + parseInt (el.offsetTop); el = el.offsetParent;  returnere xpos: x, ypos: y;  var stageOffset = find_stage_offset (scenen);

Så nå har vi skrevet mousemove lytter, de andre vil være mye lettere. For roboten mus over lytter, vi trenger bare å sjekke om roboten allerede hopper, og hvis ikke, stopp løpestimeren og få den til å hoppe.

 funksjon robot_mouseover_listener () hvis (! jump_timer || jump_timer === undefined) clearTimeout (run_timer); jmp (sant, robot.offsetTop); 

De mouseout lytteren er også ganske enkel. Vi trenger bare å tilbakestille noen av våre variabler vi bruker til å spore roboten, og hvis roboten ikke hopper, returner roboten til stående sprite.

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

Animasjonsfunksjoner

Funksjonene som animerer løpende og hoppende bevegelser har ikke forandret seg mye denne gangen. Vi har nettopp lagt til sporingsvariabelen running_dir, tatt ut uttalelsen som sjekker om roboten er i ferd med å slå på veggen (siden dette er overflødig med vår mouseout funksjon), og legg til litt kode til hoppfunksjonen som kontrollerer igjen hvis roboten skal begynne å løpe hvis musen er innenfor scenen etter at den lander fra et hopp. Her er den endelige koden (ganske stor):

 funksjon run_r (fase, venstre) face_right = true; running_dir = 'r'; hvis ((venstre + (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)))) topp = topp - (topp * 0,1); robot.style.top = topp + "px"; jump_timer = setTimeout (funksjon () jmp (opp, topp);, 60);  annet hvis (opp) opp = false; jump_timer = setTimeout (funksjon () jmp (opp, topp);, 60);  ellers hvis (! opp && (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å, nå har vi våre omskrivne funksjoner som fungerer bra på tvers av alle nettlesere ... med mindre de leserne har berøringsinngang. Vi har fortsatt litt mer å gå for å få robotene våre til å løpe på alt. Siden berøringsskjermene oppfører seg litt annerledes, må vi gjøre noe ekstra koding på våre hørere.

Støtte berøringsskjerm

Vi må gjøre noen nye regler for berøringsskjermbilder: Hvis skjermen berøres hvor som helst i scenen, vil roboten løpe til dette stedet til fingeren løftes. Hvis brukeren berører roboten, vil roboten hoppe. Først og fremst vil vi legge til noen ekstra berøringshendelseshåndtere til vår tidligere funksjon, og vi skal skrive koden på en slik måte at den vil kjøre automatisk når RobotMaster funksjon kalles.

 (funksjon () (hvis (stage.addEventListener) stage.addEventListener ('touchstart', scene_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 trenger ikke å bekymre oss om at berøringslytterne er i Internet Explorer 8-formatet, og hvis en enhet ikke har berøringsstøtte, ignorerer den lytterne. Nå må vi oppdatere stage_mousemove_listener () Fungerer for å oppføre seg annerledes hvis nettleseren har berøringsevne.

 funksjon stage_mousemove_listener (e) / * * Først sjekker vi om dette er en berøringsskjerm enhet (hvis den har e.touches) * / hvis (e.touches) e.preventDefault (); // vi vil avbryte hva nettleseren vanligvis ville gjøre hvis du rørte der. // Hvis kontakten var innenfor scenens grenser ... hvis ((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 kan merke at vi ikke lenger har noen "dører" i vår RobotMaker funksjon, men siden vi ringer all vår kode med hendelseshåndterere som vi tilordner oss innvendig RobotMaker, vi trenger ikke lenger dem! For både vår scene og våre tegn vil vi legge til litt CSS spesielt for berøringsenheter, slik at det ikke vil prøve å kutte og lime inn noen bilder når en bruker holder fingeren på dem.

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

Og til slutt vil vi deklarere alle roboter våre nederst på siden, ved hjelp av samme format som vår hendelseshåndteringsfunksjon for å få koden kjøres automatisk når siden lastes - denne metoden forhindrer også disse robotobjektene å være globale variabler, så Den eneste globale variabelen vi har i hele dette skriptet er RobotMaker () funksjon.

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

Vennligst sjekk sluttresultatet med all sin prakt!

Konklusjon

Jeg oppfordrer deg sterkt til å studere hele (og fullt kommentert!) Koden, og du kan også laste ned alle fire robot sprites her også.

Glad animasjon!