JavaScript Animație care funcționează (Partea 4 din 4)

În prima parte a acestei serii, am introdus ideea utilizării Procedeul de spriting poate ca o modalitate ușoară de a avea animație interactivă pentru web. În cea de-a doua parte, am făcut niște lucrări de animație, iar în al treilea am curățat codul nostru și l-am pregătit pentru web.

Introducere

Acum, în ultima noastră parte de astăzi, vom trece prin înființarea manipulatoare de evenimente astfel încât, în loc să răspundă butoanelor apăsate, roboții noștri vor urma mouse-ul în jurul ecranului. În acest proces, vom vorbi și despre modul în care codul încrucișat cu browserul încorporat și ecranul tactil sunt activate.

Dacă vă uitați la codul nostru de la ultima oară, veți vedea că, în timp ce codul rulează bine (și cu mai mulți roboți), nu există o modalitate foarte ușoară de a spune codul să ruleze.

Manageri de evenimente

Producători de evenimente sunt comenzi care declanșează executarea anumitor coduri atunci când sunt declanșate anumite evenimente. De exemplu, ați putea avea my_function () executați ori de câte ori un utilizator face clic pe dvs. div cu id-ul 'My_div'. Sau ai putea my_other_function () executați ori de câte ori un utilizator mișcă mouse-ul peste 'My_other_div'.

În teorie, aceasta este o idee destul de simplă și simplă. Din nefericire, odată ce începeți să vă implicați în diferite browsere, acest lucru poate deveni puțin confuz. Într-o lume ideală, fiecare browser web ar interpreta același cod și același cod HTML, iar dezvoltatorii ar scrie codul o singură dată și ar funcționa la fel pentru fiecare utilizator. În lumea reală, diferite browsere pot avea comenzi complet diferite pentru a face același lucru (* tuse * * tuse * Internet Explorer), și astfel, uneori, încercarea de a obține o singură bucată de cod pentru a rula la fel în toate browserele se poate simți ca pisicile herding. Recent, situația a fost mult mai bună, deoarece Chrome, Firefox, Safari și Opera răspund foarte mult la cod, Internet Explorer 9 și 10 au devenit mult mai în conformitate cu standardele decât versiunile anterioare și aproape nimeni nu folosește Internet Explorer 7 sau 6. Deci, pentru codul nostru, vom fi obtinerea manipulatoare de evenimente pentru a lucra atât pentru browsere moderne și Internet Explorer 8.

Ca o notă laterală, acesta este un caz în care într-adevăr se cuvine să folosiți o bibliotecă robustă JavaScript, cum ar fi jQuery. jQuery face totul pentru dvs. în testele cross-browser, astfel încât va trebui doar să introduceți o comandă și biblioteca jQuery o va traduce pentru fiecare browser în spatele scenei. În plus, multe dintre comenzile din jQuery sunt mult mai intuitive și mai simple decât core corectitudinea JavaScript.

Dar, din moment ce sunt încăpățânat și din moment ce aceasta este o oportunitate de învățare, vom continua pe cale dificilă și vom face toate acestea numai cu JavaScript și fără dependențe!

Interacțiunea paginii

Deci, primul nostru pas va fi să decidem exact cum vrem să interacționăm cu pagina. Când mișc mouse-ul peste zona de scenă, vreau ca toți roboții să meargă spre mouse. Când ajung la mouse sau dacă mouse-ul este direct deasupra lor, vreau să nu mai funcționeze. Dacă mouse-ul trece peste ele, vreau să sară. Și în sfârșit, când mouse-ul părăsește zona de scenă, vreau să nu mai funcționeze. Vom începe cu atașarea acestor evenimente în interiorul RobotMaker funcţie:

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

Deci, în liniile de mai sus, am spus că ori de câte ori utilizatorul mută mouse-ul în interiorul elementului de scenă, vom declanșa o funcție numită stage_mousemove_listener () (notați nu includeți parantezele din comandă). În mod similar, când utilizatorul mișcă mouse-ul peste elementul robot, acesta declanșează robot_mouseover_listener (), iar atunci când utilizatorul mișcă mouse-ul în afara scenei, se declanșează stage_mouseout_listener ().

Din păcate, așa cum am menționat mai devreme, Internet Explorer 8 și mai jos are o comandă (similară, dar diferită) de a face același lucru, așa că va trebui să testați pentru a afla ce comandă va înțelege browserul utilizatorului și va face acea metodă.

 dacă (stage.addEventListener) // Vom testa dacă această comandă este disponibilă stage.addEventListener ('mousemove', stage_mousemove_listener, false); robot.addEventListener ("mouseover", robot_mouseover_listener, false); stage.addEventListener ('mouseout', stage_mouseout_listener, false);  altfel // Dacă nu, trebuie să folosim comenzile IE stage.attachEvent ('onmousemove', stage_mousemove_listener); robot.attachEvent ("onmouseover", robot_mouseover_listener); stage.attachEvent ("onmouseout", stage_mouseout_listener); 

Puteți observa că formatul comenzilor este foarte asemănător, dar are unele diferențe majore - spune unul 'AddEventListener' în timp ce cealaltă spune 'AttachEvent'. Unul spune 'Mousemove' în timp ce cealaltă spune 'Onmousemove'. Unul necesită un al treilea parametru, în timp ce celălalt utilizează doar două. Amestecarea oricăreia dintre acestea va determina comanda să nu funcționeze. Acestea sunt tipurile de lucruri care vă vor face să doriți să vă bateți capul pe perete. Din nefericire, acest lucru nu este sfârșitul codării suplimentare pe care va trebui să o facem pentru capacitatea de browser încrucișat.

Funcțiile de ascultare

Apoi, vom scrie funcțiile de ascultare. Vom începe cu funcția care este declanșată atunci când utilizatorul face mouses peste scenă. Deoarece acesta este a mousemove ascultător, această funcție va declanșa de fiecare dată când mouse-ul este mutat în interiorul zonei de scenă (adică se va declanșa de câteva ori pe secundă în timp ce mouse-ul se mișcă). Această funcție va trebui să compare situația robotului cu locația mouse-ului și să facă robotul să se comporte în consecință. De fiecare dată când funcția este declanșată, va verifica dacă robotul trebuie să continue să ruleze aceeași direcție sau să schimbe comportamentul. Deci, va trebui să fie ceva de genul:

 // Inside of RobotMaker // Va trebui sa introducem cateva variabile suplimentare pentru a urmari var mouseX; // Pentru urmărirea poziției orizontale a mouse-ului var running_dir = "; // Pentru urmărirea dacă (și unde) robotul rulează în prezent var stageOffset; // Pentru urmărirea poziției funcției stage_mousemove_listener (e) // Găsiți poziția orizontală a mouse-ul din interiorul scenei ... // Această poziție va fi salvată în 'mouseX' // Apoi vom compara mouseX cu robotul și vom decide dacă trebuie să executați altfel dacă (((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)) // Dacă mouse-ul este în stadiu și peste un robot, opriți și ștergeți running_dir running_dir ="; clearTimeout (run_timer); dacă (face_right) robot.style.backgroundPosition = "0px 0px";  altceva robot.style.backgroundPosition = "0px -50px";  // Dacă niciuna dintre cele de mai sus nu este adevărată, atunci vom lăsa comportamentul nostru curent să continue

Deci, în funcția de mai sus, odată ce vom putea găsi mouseX, îl comparăm cu locul în care se află robotul și declanșează sau opresc diferite funcții de funcționare după cum este necesar. Din păcate, găsirea mouseX este un pic dificil, deoarece poziția mouse-ului este un alt lucru pe care browserele diferite fac diferit. În locul explicațiilor (mai) complicate și mai lungi, este metoda de căutare încrucișată mouseX, ca inspirat de blogul excelent Quirksmode (care este o sursă excelentă pentru studierea JavaScript mai avansată).

 funcția stage_mousemove_listener (e) var posX = 0; dacă (! e) var e = window.event;  dacă (e.pageX) posX = e.pageX;  altfel dacă (e.clientX) posX = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;  mouseX = posX - stageOffset.xpos; // Și găsim mouseX! 

Avem un argument numit e în funcție, chiar dacă noi nu-i predăm nimic. Deoarece acesta este un ascultător de evenimente, putem avea o variabilă automată numită e care stochează informații despre evenimente, cum ar fi datele mouse-ului. Dar, deoarece browserele diferite stochează altfel, trebuie să adăugăm o mulțime de pași suplimentari.

În sfârșit, găsim mouseX prin găsirea posX (care este poziția x a mouse-ului pe pagină) și scăzând cât de departe este scena din extrema stângă a paginii (stocată în stageOffset.xpos). Acest lucru ne oferă cât de departe de marginea stângă a scenei este mouse-ul, cu care putem compara direct robot.offsetLeft. Deoarece etapa poate fi localizată diferit în jurul paginii, în funcție de aspect, va trebui, de asemenea, să găsim o compensare exactă a pixelilor de pe scenă pentru ca funcția să fie exactă și să stocheze acele informații în stageOffset. Din fericire există un truc bun pe care îl putem folosi pentru a găsi compensarea absolută a elementului cu această funcție din blogul lui Vishal Astik.

 // În interiorul RobotMaker var x = 0; var y = 0; funcția find_stage_offset (el) x = el.offsetLeft; y = el.offsetTop; el = el.offsetParent; în timp ce (el! == null) x = parseInt (x) + parseInt (el.offsetLeft); y = parseInt (y) + parseInt (el.offsetTop); el = el.offsetParent;  retur xpos: x, ypos: y;  var stageOffset = find_stage_offset (etapă);

Deci, acum că am scris mousemove ascultător, ceilalți vor fi mult Mai ușor. Pentru robot mouse-ul peste ascultător, trebuie doar să verificăm dacă robotul este deja sărit și, dacă nu, opriți cronometrul de funcționare și faceți-l să sară.

 funcția robot_mouseover_listener () if (! jump_timer || jump_timer === undefined) clearTimeout (run_timer); jmp (adevărat, robot.offsetTop); 

mouseout ascultător este, de asemenea, destul de simplu. Trebuie doar să reinițializăm câteva dintre variabilele pe care le folosim pentru a urmări robotul și dacă robotul nu sare, întoarceți robotul la sprite în picioare.

 funcția stage_mouseout_listener () mouseX = undefined; (jump_timer || jump_timer === undefined) clearTimeout (run_timer); if (face_right) robot.style.backgroundPosition = "0px 0px"; altceva robot.style.backgroundPosition = "0px - 50px ";

Funcții de animație

Funcțiile care animă mișcările de rulare și sărituri nu s-au schimbat prea mult de data aceasta. Tocmai am adăugat variabila de urmărire running_dir, a luat afirmația că verifică dacă robotul este pe punctul de a lovi peretele (din moment ce acest lucru este redundant cu noi mouseout funcția) și adăugați un pic de cod la funcția de salt care verifică din nou dacă robotul ar trebui să înceapă să ruleze dacă mouse-ul se află în scenă după ce aterizează dintr-un salt. Iată codul final (destul de mare):

 funcția run_r (fază, stânga) face_right = true; run_dir = 'r'; dacă ((la stânga + (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 = sus - (partea de sus * 0.1); robot.style.top = top + "px"; jump_timer = setTimeout (funcția () jmp (sus, sus);, 60);  altfel dacă (sus) up = false; jump_timer = setTimeout (funcția () jmp (sus, sus);, 60);  altfel dacă (! 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);    

Deci, acum, avem funcțiile noastre rescrise care funcționează excelent în toate browserele ... cu excepția cazului în acele browsere au intrare touch. Mai avem încă ceva pentru a ne face roboții să ruleze pe tot. Din moment ce ecranele tactile se comporta diferit, va trebui sa facem unele codari suplimentare pe ascultatorii evenimentului.

Susținerea Ecranelor Touch

Trebuie să elaborăm câteva reguli noi pentru ecrane tactile: dacă ecranul este atins oriunde în scenă, robotul va alerga în acel loc până când se ridică degetul. Dacă utilizatorul atinge robotul, robotul va sari. Mai întâi de toate, vom adăuga un anumit număr de operatori de eveniment suplimentar la funcția noastră anterioară și vom scrie codul astfel încât acesta să ruleze automat ori de câte ori RobotMaster se numește funcția.

 (funcția () if (stage.addEventListener) stage.addEventListener ('touchstart', stage_mousemove_listener, false); stage.addEventListener ('touchmove', stage_mousemove_listener, false) ; stage.addEventListener ('mouseout', stage_mouseout_listener, false); altfel stage.attachEvent ('onmousemove'); , stage_mousemove_listener); robot.attachEvent ('onmouseover', robot_mouseover_listener); stage.attachEvent ('onmouseout', stage_mouseout_listener);) ();

Nu va trebui să ne facem griji că ascultătorii de atingere se află în formatul Internet Explorer 8 și dacă vreun dispozitiv nu are suport tactil, acesta va ignora ascultătorii. Acum va trebui să actualizăm stage_mousemove_listener () funcția de a se comporta diferit dacă browserul are capacitatea de atingere.

 funcția stage_mousemove_listener (e) / * * Mai întâi verificăm dacă acesta este un dispozitiv cu ecran tactil (dacă are e.touches) * / if (e.touches) e.preventDefault (); // dorim să anulam ceea ce ar face de obicei browserul dacă ar fi atins acolo // Dacă atingerea a fost în limitele stadiului ... dacă ((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  

S-ar putea să observați că nu mai avem "uși" în noi RobotMaker dar, din moment ce sunăm tot codul nostru cu manipulatorii de evenimente pe care îi atribuim înăuntru RobotMaker, nu mai avem nevoie de ele! Atât pentru etapa noastră, cât și pentru personajele noastre, vom dori să adăugăm un pic de CSS special pentru dispozitivele tactile, astfel încât să nu încerce să taie și să lipsească nicio imagine când un utilizator deține un deget pe ele.

 #stage, .character -webkit-user-select: nici unul; 

În cele din urmă, vom declara toți robotii noștri în partea de jos a paginii, folosind același format ca și funcția de gestionare a evenimentului pentru a rula codul automat când se încarcă pagina - această metodă împiedică, de asemenea, aceste obiecte robot să fie variabile globale, deci singura variabilă globală pe care o avem în acest întreg script este RobotMaker () funcţie.

 (funcția () var j = RobotMaker (document.getElementById ('j'), 1, 1); .getElementById ('j3'), 1.1, .5); var j4 = RobotMaker (document.getElementById ('j4'), .5, .75);) ();

Vă rugăm să finalizați rezultatul final în toată gloria sa!

Concluzie

Vă încurajez foarte mult să studiați întregul cod (și pe deplin comentat!) Și puteți descărca toate cele patru sprite de robot aici.

Animație fericită!

Cod