I vårt første innlegg i denne serien introduserte vi spriting, og hvordan det kan brukes til å gjøre enkel og effektiv kryssbrowser animasjon på nettet. I det andre innlegget fikk vi noen enkle animasjoner oppe, selv om de hadde en del feil og koden var absolutt ikke klar til å gå live.
I dag skal vi løse disse feilene og rydde opp koden vår slik at vi kan publisere den på en side uten frykt for å krasje noen kode ved hjelp av en metode som heter innkapsling.
For å virkelig forklare hva som var så galt med koden i vårt siste trinn, og hvorfor innkapsling er viktig, må vi først forklare variabel omfang.
Tenk deg at du jobber med koden nedenfor. Du har en nyttig variabel i funksjonen din do_this ()
, og du vil gjerne bruke den samme variabelen i en annen funksjon, do_that ()
, men du løper inn i et lite problem.
funksjon do_this () var very_helpful_variable = 20; ... // Dette viser '20', akkurat som du forventer varsel (very_helpful_variable); funksjon do_that () alert (very_helpful_variable); // Men dette viser 'undefined'!
Din variabel fungerer bra innenfor funksjonen det ble erklært, men utenfor den funksjonen, er det som om det aldri eksisterte! Dette er fordi do_that ()
er ikke innenfor omfang av variabelen very_helpful_variable
.
Variabler er bare tilgjengelige i koden hvor de er erklært, dette er deres omfang. Når denne koden er ferdig, blir dens variabler slettet.
Ta en titt på disse eksemplene:
var w = 1; funksjon a () var x = 2; funksjon b () var y = 3; alert (w); // virker advarsel (x); // virker advarsel (y); // virker advarsel (z); // undefined varsel (w); // virker advarsel (x); // virker advarsel (y); // undefined alert (z); // undefined funksjon c () var z = 4; alert (w); // virker advarsel (x); // undefined alert (y); // undefined alert (z); // fungerer b (); // undefined varsel (w); // virker advarsel (x); // undefined alert (y); // undefined alert (z); // undefined
Først har vi variabelen w
, som er deklarert utenfor noen funksjoner. Det kalles a global variabel, og det vil fungere hvor som helst fordi omfanget er hele dokumentet.
Neste er variabelen x
, siden det er erklært inne i funksjonen en()
, det vil bare fungere inne i den funksjonen. Dette inkluderer også arbeid innenfor funksjonen b ()
, siden b ()
er inne i en()
.
Imidlertid er en variabel definert inne i b ()
(som y
) vil ikke fungere i ytre funksjon, siden det er utenfor dens omfang.
Du kan også merke at vi forsøkt mislykket å ringe funksjonen b ()
fra innsiden av funksjonen c ()
; Funksjonsnavn følger de samme reglene som andre variabler.
En annen quirk med JavaScript, hvis vi bare begynner å bruke et variabelt navn inne i en funksjon uten å erklære det med søkeordet Var
, så vil nettleseren anta at den variabelen skal være global. Så, hvis du ikke sørger for at du alltid erklære dine variabler med Var
søkeord, vil du ende opp med globale variabler og ikke innse det!
Så, for å oppsummere: når vi erklærer en variabel, kan vi bruke den innenfor den koden eller inne i noen nestede blokker inni den. Hvis vi prøver å bruke den utenfor omfanget, er verdien satt til udefinert
.
Det er derfor i vårt siste innlegg, legger vi tidsur
variabel utenfor funksjonene som brukte den, siden vi trengte å få tak i denne variabelen etter at funksjonene var avsluttet.
var timer; // Dette er en global variabel funksjon run_right (scene, venstre) ... timer = setTimeout (funksjon () run_right (2, venstre);, 200); ... funksjon stop_running () document.getElementById ('j' ) .style.backgroundPosition = "0px 0px"; // Hvis 'timer' ikke var satt som global, kunne vi ikke stoppe det her clearTimeout (timer);
For å klare timeren trengte vi stop_running ()
å være innenfor rammen for variabelen tidsur
. Så laget vi tidsur
en global variabel som kan brukes overalt, hva kan være galt med det?
I et gitt omfang er det umulig å ha to elementer som heter det samme. Hvis du skulle prøve å ha to forskjellige variabler med samme navn, ville nettleseren bare skrive over en av dem. Så, hvis vi hadde en variabel som heter tidsur
, og hadde en egen variabel også kalt tidsur
det ble kalt innenfor samme omfang, en av dem ville slette og ta plass til den andre, og vi ville ha kaos i vår kode. Hvis vi hadde en global variabel kalt tidsur
, da ville det forstyrre noen annen variabel som heter tidsur inneholdt hvor som helst på siden - inkludert alle tilknyttede JavaScript-biblioteker og eksterne filer.
Dette er en stor kilde til hodepine, du har nettopp sett en veldig jevn JavaScript-plugin-inn et sted, og du laster den ned på nettstedet ditt, og plutselig alle andre plugins-krasjer ... En av plugin-modulene var slurvet med global variabler, skjedde å dele samme navn med noe annet, din nettleser reiser over seg selv, og hele siden kommer til sliping.
Det som gjør dette enda verre er at du aldri vil merke dette problemet når du først test koden. Som vår animasjonskode fra det siste innlegget, vil det fungere bra av seg selv. Men jo flere brikker du legger til, desto mer sannsynlig er sjansene for å få en navngiving konflikt, og du vil bli sittende fast sortering gjennom et dusin forskjellige JavaScript-filer som prøver å finne ut hvilke to ikke kommer sammen.
Nå kan du spørre deg selv: "Globale variabler er så praktiske! Hva om jeg bare ser koden min nøye og sørg for at jeg ikke har noen konflikter?" Det kan fungere i en perfekt verden, men i virkeligheten vil du ofte ha flere som jobber på ulike deler av samme side, eller må komme tilbake og oppdatere forskjellige deler av koden år senere, eller til og med ha kode fra tredjeparter på siden din vil være ute av kontroll (som betalte annonser).
Så kort sagt, du vil ikke ha globale variabler mer enn du vil ha utsatt ledninger langs husets vegger eller eksponerte maskiner i bilen din, det er bare et spørsmål om tid før noe skjer som tannkjøtt opp i arbeidet. Heldigvis er det en bedre måte å unngå disse fallgruvene.
Vi kan ha alle fordelene med globale variabler uten problemer ved å bruke en teknikk som kalles innkapsling. Tenk på det som om du bygger en vegg rundt koden din med bare noen få spesielle dører, ingenting kan komme inn eller ut av den koden, med mindre du spesifikt tillater det.
JavaScript har en type variabel kalt en gjenstand. Objekter er brukerdefinerte samlinger av data som inneholder informasjon og funksjoner (referert til som eiendommer og fremgangsmåter, henholdsvis). Vi skal skrive en funksjon som skaper et spesielt objekt som har alle funksjonene vi trenger "bakt" i det, og det vil til og med tillate oss å ha mer enn én robot uten å duplisere koden vår!
Vi starter med å definere en ny funksjon med et variabelt navn. Vi må passere variabelen noen argumenter, jeg skal passere det HTML-elementet som vi vil animere, pluss noen unike verdier for løphastighet og hopphøyde, slik at vi kan variere de fra robot til robot.
var RobotMaker = funksjon (robot, run_speed, jump_height) // Vi legger alle våre funksjoner og variabler i dette området. // Dette er inne i vår 'ugjennomtrengelige' veggen, så ingenting i dette // området vil komme i konflikt med annen kode. returnere // Innsiden her plasserer vi alle våre dører ... // disse vil være den eneste måten noe kan få // inn eller ut av denne koden. // Og siden dette fortsatt er innenfor samme 'scope' // som RobotMaker, kan vi bruke variabler som er nevnt ovenfor!
Siden vi skal sette alle våre funksjoner inn i vår nye "vegg", ville det være en god tid å se på hvilke feil vi hadde med den opprinnelige koden. (Du kan se det i aksjon her)
Det kan hende du merker at hvis vi klikker på to løpeknapper (eller en løp og hoppeknapp) uten å klikke på Stoppe knappen i mellom, vil J fortsette å gjøre begge handlingene. Et annet problem er at uansett hvilken retning J står overfor, når vi klikker på Hoppe eller Stoppe knappen, han står rett hver gang. Til slutt, hvis du klikker på Hoppe knappen igjen mens J faller fra et første hopp, vil han fortsette å falle gjennom siden i en endeløs løkke.
For å kunne håndtere disse tingene må vi være mer konkrete om hva vi vil skje med hver av våre funksjoner:
Når vi klikker Kjør høyre:
Når vi klikker Kjør venstre:
Når vi klikker Stop Running:
Når vi klikker på Hopp:
Først av alt, skal vi legge til noen få flere variabler nå. Siden timeren skal oppføre seg annerledes for å løpe og hoppe, har vi to separate timere. Vi ønsker også å introdusere en boolean
(true / false) variabel for å spore hvis vi skal vende mot venstre eller høyre, og vi vil lage en scene
variabel bare for å redde oss fra å måtte skrive ut hele elementnavnet.
// Inne i RobotMaker-funksjonen ... var stadium = document.getElementById ('scenen'); var run_timer, jump_timer; var face_right = true;
Da skal vi legge tilbake i våre funksjoner for å løpe rett, løp til venstre og hoppe. Disse kommer til å være stort sett det samme, med noen forskjeller. Først av alt, kan alle referansene til elementet som vi animerer erstattes med variabelen robot
(som vil bli sendt som en av argumentene i RobotMaker
funksjon). For det andre har vi gjort noen små endringer i løphastighet og hopphøyde i funksjonene, slik at vi kan variere dem ved å passere forskjellige verdier. For det tredje bruker vi face_right
variabel for å spore hvilken retning J vender mot (og i hoppingsfunksjonen, ved bruk av face_right
å bestemme hvilken hoppesprite som skal vises). Til slutt bruker vi separate timere for å løpe og hoppe.
// Inne i RobotMaker-funksjonen ... funksjon run_r (fase, venstre) face_right = true; hvis ((venstre + (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)))) topp = topp - (topp * .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 * .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;
Alle disse variablene og funksjonene er inne i vår "vegg", så vi må nå "dørene" for å få tilgang til det vi trenger. Disse fire "dørene" vil være gjenstand fremgangsmåter for de samme fire funksjonene vi hadde tidligere og vil referere til de beskyttede funksjonene ovenfor. Også, vi vil fullføre bugfixeringen ved å sjekke inn hver funksjon hvis jump_timer
går, og sørg for å rydde run_timer
. Husk at disse to timers er i bruk hvor som helst inni RobotMaker ()
funksjon, så vi kan bruke dem her. Men siden de ikke er globale variabler, vil vi ikke komme inn i noen problemer med dem andre steder.
// Inne i RobotMaker-funksjonen ... return run_right: function () if (! Jump_timer || jump_timer == undefined) 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: funksjon () hvis (! jump_timer || jump_timer == undefined) clearTimeout (run_timer); hvis (face_right) robot.style.backgroundPosition = "0px 0px"; ellers robot.style.backgroundPosition = "0px -50px"; , hopp: funksjon () hvis (! jump_timer || jump_timer == undefined) clearTimeout (run_timer); jmp (sant, robot.offsetTop);
Nå som vi har skrevet en funksjon som skaper objekter, kan vi bruke den så mange ganger som vi liker å lage objekter som har animasjonsegenskapene vi ønsker. På bunnen av siden vil vi erklære to nye RobotMaker
objekter, og gi dem det elementet vi ønsker å animere, en løpehastighet og en hopphøyde.
var j = RobotMaker (document.getElementById ('j', 1, 1); var j2 = RobotMaker (document.getElementById ('j2'), .8, 5);
Nå har vi ingen fare for noe i RobotMaker ()
funksjonen lekker ut og forstyrrer koden vår, og vi kan fortsatt komme til de funksjonene vi ønsker gjennom "dørene" som vi installerte slik:
Så nå kan du se det ferdige produktet på Hyrgo Pen.
Legg merke til hvordan det ikke lenger er noen problemer med funksjonene som forstyrrer hverandre, og du kan betjene hver robot individuelt uten å påvirke den andre. Encapsulation er en utrolig viktig teknikk, og du bør virkelig bli kjent med det hvis du vil gjøre noe interaktivt webdesign.
Hvis du vil, vennligst sjekk all denne koden, fullstendig kommentert, og du kan få sprites ved hjelp av følgende lenker: her er de første sprites og her er de andre. Vær oppmerksom på at for at den samme koden skulle virke sammen med begge sprites, trengte jeg å lage den andre sprite i nøyaktig samme format og dimensjoner som den første.
Så det bryter opp del tre av sprit! I vårt neste og siste innlegg vil jeg erstatte disse knappene ved å gjøre robotene våre følge musen rundt skjermen, og vise deg hvordan du setter opp hendelse lyttere og aktiver støtte på tvers av nettlesere og berør enheter.