Animazione JavaScript che funziona (parte 4 di 4)

Nella prima parte di questa serie, abbiamo introdotto l'idea dell'utilizzo sprite come un modo semplice, cross-browser di avere animazione interattiva per il web. Nella seconda parte abbiamo lavorato su animazione e nella terza abbiamo ripulito il nostro codice e reso pronto per il web.

introduzione

Ora, nella parte finale di oggi, procederemo attraverso la creazione gestori di eventi in modo che invece di rispondere ai pulsanti cliccati, i nostri robot seguiranno il mouse sullo schermo. Nel processo, parleremo anche di rendere il codice compatibile con il browser e abilitato al touch screen.

Se dai un'occhiata al nostro codice dall'ultima volta, vedrai che mentre il codice funziona bene (e con più robot), non c'è un modo molto semplice per dire al codice di funzionare.

Gestori di eventi

Gestori di eventi sono comandi che indicano che alcuni codici devono essere eseguiti quando determinati eventi vengono attivati. Ad esempio, potresti avere my_function () eseguire ogni volta che un utente fa clic sul tuo div con l'id 'Mio_div'. O potresti avere my_other_function () eseguire ogni volta che un utente sposta il mouse sopra 'My_other_div'.

In teoria, questa è un'idea abbastanza semplice e diretta. Sfortunatamente, una volta che inizi a ottenere diversi browser coinvolti, questo può diventare un po 'confuso. In un mondo ideale, ogni web browser interpreterebbe lo stesso codice e HTML allo stesso modo, e gli sviluppatori scriverebbero il codice una volta e funzionerebbe allo stesso modo per ogni utente. Nel mondo reale, diversi browser possono avere comandi completamente diversi per fare la stessa cosa (* tosse * * tosse * Internet Explorer), e così a volte cercando di ottenere un singolo pezzo di codice per eseguire lo stesso su tutti i browser può sembrare di radunare i gatti. Recentemente, la situazione è migliorata molto, poiché Chrome, Firefox, Safari e Opera rispondono tutti in modo simile al codice, Internet Explorer 9 e 10 sono diventati molto più in linea con gli standard rispetto alle versioni precedenti e quasi nessuno usa Internet Explorer 7 o 6 più. Quindi, per il nostro codice, porteremo i gestori di eventi a lavorare sia per i browser moderni che per Internet Explorer 8.

Come nota a margine, questo è un caso in cui è davvero utile utilizzare una robusta libreria JavaScript, come jQuery. jQuery fa tutto il lavoro per te nei test cross-browser, quindi dovrai solo inserire un comando e la libreria jQuery lo tradurrà per ogni browser dietro le quinte. Inoltre, molti dei comandi di jQuery sono molto più intuitivi e più semplici del core JavaScript.

Ma, dal momento che sono testardo, e dal momento che questa è un'opportunità di apprendimento, continueremo nel modo più duro e faremo tutto ciò solo con JavaScript e senza dipendenze!

Interazione della pagina

Quindi, il nostro primo passo sarà decidere come esattamente vogliamo interagire con la pagina. Quando sposto il mouse sopra l'area del palco, voglio che tutti i robot corrano verso il mouse. Quando raggiungono il mouse o se il mouse si trova direttamente sopra di loro, voglio che smettano di funzionare. Se il mouse li incrocia, voglio che saltino. E infine, quando il mouse lascia l'area del palco, voglio che smettano di correre. Inizieremo con il collegamento di questi eventi all'interno di RobotMaker funzione:

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

Quindi, nelle righe precedenti, abbiamo detto che ogni volta che l'utente sposta il mouse all'interno dell'elemento stage, verrà attivata una funzione chiamata stage_mousemove_listener () (notare che non includiamo le parentesi nel comando). Allo stesso modo, quando l'utente sposta il mouse sopra l'elemento del robot, si innesca robot_mouseover_listener (), e quando l'utente sposta il mouse fuori dallo stage, si innesca stage_mouseout_listener ().

Sfortunatamente, come accennato prima, Internet Explorer 8 e versioni successive hanno un comando (simile ma diverso) per fare la stessa cosa, quindi dovremo testare per sapere quale comando il browser dell'utente comprenderà e fare quel metodo.

 if (stage.addEventListener) // Verificheremo se questo comando è disponibile stage.addEventListener ('mousemove', stage_mousemove_listener, false); robot.addEventListener ('mouseover', robot_mouseover_listener, false); stage.addEventListener ('mouseout', stage_mouseout_listener, false);  else // Se no, dobbiamo usare i comandi di IE stage.attachEvent ('onmousemove', stage_mousemove_listener); robot.attachEvent ('onmouseover', robot_mouseover_listener); stage.attachEvent ('onmouseout', stage_mouseout_listener); 

Si può notare che il formato dei comandi è molto simile, ma presenta alcune differenze importanti - si dice 'AddEventListener' mentre l'altro dice 'AttachEvent'. Uno dice 'MouseMove' mentre l'altro dice 'OnMouseMove'. Uno richiede un terzo parametro, mentre l'altro ne usa solo due. Mescolando qualcuno di questi, il comando non verrà eseguito. Questi sono i tipi di cose che ti faranno venire voglia di sbattere la testa contro il muro. Sfortunatamente, questa non è la fine della codifica aggiuntiva che avremo bisogno di fare per le funzionalità cross-browser.

Funzioni di ascolto

Successivamente, scriveremo le funzioni di ascolto. Inizieremo con la funzione che viene attivata quando l'utente passa sopra il palco. Poiché questo è un MouseMove ascoltatore, questa funzione si attiva ogni volta che il mouse viene spostato all'interno dell'area dello stage (ovvero si attiva più volte al secondo mentre il mouse si muove). Questa funzione dovrà confrontare la posizione del robot con la posizione del mouse e fare in modo che il robot si comporti di conseguenza. Ogni volta che la funzione viene attivata, controlla se il robot deve continuare a seguire la stessa direzione o cambiare comportamento. Quindi, dovrà essere qualcosa del genere:

 // Inside of RobotMaker // Avremo bisogno di introdurre alcune variabili extra per tenere traccia di var mouseX; // Per tracciare la posizione orizzontale del mouse var running_dir = "; // Per tracciare se (e dove) robot è attualmente in esecuzione var stageOffset; // Per tracciare la posizione della funzione stage stage_mousemove_listener (e) // Trova la posizione orizzontale di il mouse all'interno dello stage ... // Quella posizione verrà salvata in 'mouseX' // Quindi confrontiamo 'mouseX' con il robot e decidiamo se è necessario eseguire diversamente se (((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)) // Se il mouse si trova nello stage e su un robot, fermarsi e deselezionare running_dir running_dir ="; clearTimeout (run_timer); if (face_right) robot.style.backgroundPosition = "0px 0px";  else robot.style.backgroundPosition = "0px -50px";  // Se nessuno dei precedenti è vero, allora lasciamo che il nostro comportamento attuale continui

Quindi, nella funzione sopra, una volta che siamo in grado di trovare mouseX, lo confrontiamo con il robot e attiviamo o arrestiamo le diverse funzioni di corsa come necessario. Sfortunatamente, trovando mouseX è un po 'complicato, dal momento che la posizione del mouse è un'altra cosa che i diversi browser fanno diversamente. Al posto di (più) spiegazioni complicate e prolisso, ecco il metodo cross-browser per la ricerca mouseX, ispirato all'eccellente blog Quirksmode (che è un'ottima fonte per lo studio di JavaScript più avanzato).

 function 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; // E troviamo mouseX! 

Abbiamo una discussione chiamata e nella funzione, anche se non passiamo nulla. Poiché questo è un listener di eventi, possiamo avere una variabile automatica chiamata e che memorizza informazioni sugli eventi come i dati del mouse. Ma poiché diversi browser lo memorizzano in modo diverso, dobbiamo aggiungere molti passaggi aggiuntivi.

Alla fine troviamo mouseX trovando posX (che è la posizione x del mouse sulla pagina) e sottraendo quanto è lontano il palco dall'estrema sinistra della pagina (memorizzato in stageOffset.xpos). Questo ci dà quanto lontano dal bordo sinistro del palcoscenico è il mouse, che possiamo direttamente confrontare robot.offsetLeft. Poiché lo stage potrebbe essere posizionato in modo diverso intorno alla pagina a seconda del layout, dovremo anche trovare l'esatto offset dei pixel dello stage affinché la funzione sia accurata e archiviare tali informazioni in stageOffset. Fortunatamente c'è un trucco che possiamo usare per trovare l'offset assoluto di un elemento con questa funzione dal blog di Vishal Astik.

 // Dentro 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);

Quindi ora che abbiamo scritto il MouseMove ascoltatore, gli altri saranno tanto Più facile. Per il robot mouseover ascoltatore, abbiamo solo bisogno di controllare se il robot sta già saltando, e in caso contrario, fermare il timer e farlo saltare.

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

Il mouseout ascoltatore è anche piuttosto semplice. Abbiamo solo bisogno di resettare alcune delle nostre variabili che stiamo usando per tracciare il robot, e se il robot non sta saltando, riporta il robot allo sprite in piedi.

 function stage_mouseout_listener () mouseX = non definito; 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 ";

Funzioni di animazione

Le funzioni che animano i movimenti di corsa e salto non sono cambiate molto questa volta. Abbiamo appena aggiunto la variabile di monitoraggio running_dir, preso la dichiarazione che controlla se il robot sta per colpire il muro (poiché questo è ridondante con il nostro mouseout funzione), e aggiungi un po 'di codice alla funzione di salto che controlla di nuovo se il robot deve iniziare a funzionare se il mouse si trova all'interno dello stage dopo che atterra da un salto. Ecco il codice finale (abbastanza grande):

 function run_r (phase, left) 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 - (in alto * 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);    

Quindi, ora, abbiamo le nostre funzioni riscritte che funzionano perfettamente su tutti i browser ... a meno che quei browser non abbiano input tattili. Abbiamo ancora un po 'di più da fare per far funzionare i nostri robot su tutto. Dal momento che i touch screen si comportano in modo un po 'diverso, avremo bisogno di fare un po' di codice aggiuntivo sui nostri listener di eventi.

Touch screen di supporto

Dobbiamo fare alcune nuove regole per i touch screen: se lo schermo viene toccato in qualsiasi punto del palco, il robot scorrerà fino a quel punto finché il dito non sarà sollevato. Se l'utente tocca il robot, il robot salterà. Prima di tutto aggiungeremo alcuni gestori di eventi di tocco in più alla nostra funzione precedente, e scriveremo il codice in modo che venga eseguito automaticamente ogni volta che RobotMaster la funzione è chiamata.

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

Non dovremo preoccuparci del fatto che gli ascoltatori di touch siano in formato Internet Explorer 8 e, se un dispositivo non dispone di supporto per il tocco, ignorerà gli ascoltatori. Ora dovremo aggiornare il stage_mousemove_listener () funzione di comportarsi diversamente se il browser ha funzionalità touch.

 function stage_mousemove_listener (e) / * * Prima controlliamo se si tratta di un dispositivo touch screen (se ha e.touches) * / if (e.touches) e.preventDefault (); // vogliamo cancellare ciò che il browser farebbe di solito se toccato lì // Se il tocco fosse all'interno dei confini dello stage ... 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.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  

Potresti notare che non abbiamo più nessuna "porta" nella nostra RobotMaker funzione, ma dal momento che stiamo chiamando tutto il nostro codice con i gestori di eventi che stiamo assegnando all'interno RobotMaker, non abbiamo più bisogno di loro! Sia per il nostro stage, sia per i nostri personaggi, vorremmo aggiungere un po 'di CSS appositamente per i dispositivi touch, in modo che non provi a tagliare e incollare le immagini quando un utente tiene un dito su di esse.

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

Infine, dichiareremo tutti i nostri robot nella parte inferiore della pagina, utilizzando lo stesso formato della nostra funzione di gestione degli eventi per far sì che il codice funzioni automaticamente quando la pagina viene caricata - questo metodo impedisce anche che questi oggetti robot siano variabili globali, quindi l'unica variabile globale che abbiamo in questo intero script è il RobotMaker () funzione.

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

Si prega di controllare il risultato finale in tutta la sua gloria!

Conclusione

Ti incoraggio vivamente a studiare l'intero codice (e completamente commentato!), E puoi anche scaricare tutti e quattro i folletti robotici qui.

Felice animazione!