En nuestro primer post en esta serie, presentamos spriting, y cómo se puede utilizar para hacer una animación de navegador en la web fácil y eficaz. En la segunda publicación, tenemos algunas animaciones simples en funcionamiento, aunque tenían una buena cantidad de errores y el código no estaba listo para funcionar..
Hoy, vamos a solucionar esos errores y limpiar nuestro código para poder publicarlo en una página sin temor a fallar cualquier código usando un método llamado encapsulamiento.
Para explicar realmente lo que estaba tan mal con el código en nuestro último paso, y por qué la encapsulación es importante, primero debemos explicar el alcance de la variable.
Imagina que estás trabajando con el siguiente código. Tienes una variable útil en tu función hacer esto()
, y te gustaría usar esa misma variable en otra función, Haz eso()
, pero te encuentras con un pequeño problema.
function do_this () var very_helpful_variable = 20;… // Esto muestra '20', como usted espera alerta (very_helpful_variable); function do_that () alert (very_helpful_variable); // ¡Pero esto muestra 'indefinido'!
Su variable funciona muy bien dentro de la función que fue declarada, pero fuera de esa función, ¡es como si nunca hubiera existido! Esto es porque Haz eso()
no está dentro de la alcance de la variable muy_helpful_variable
.
Las variables solo están disponibles dentro del bloque de código donde se declaran, este es su alcance. Una vez que el bloque de código termina de ejecutarse, sus variables se borran..
Echa un vistazo a estos ejemplos:
var w = 1; función a () var x = 2; función b () var y = 3; alerta (w); // trabaja alerta (x); // alerta de obras (y); // trabaja alerta (z); // indefinido alerta (w); // trabaja alerta (x); // alerta de obras (y); // alerta indefinida (z); // función indefinida c () var z = 4; alerta (w); // trabaja alerta (x); // alerta indefinida (y); // alerta indefinida (z); // trabaja b (); // indefinido alerta (w); // trabaja alerta (x); // alerta indefinida (y); // alerta indefinida (z); // indefinido
Primero tenemos la variable. w
, Lo que se declara fuera de cualquier función. Se llama un variable global, y funcionará en cualquier lugar porque su alcance es todo el documento.
La siguiente es la variable X
, Ya que se declara dentro de la función. una()
, Sólo funcionará dentro de esa función. Esto también incluye trabajar dentro de la función. segundo()
, ya que segundo()
está dentro de una()
.
Sin embargo, una variable definida dentro de segundo()
(me gusta y
) no funcionará en la función externa, ya que está fuera de su alcance.
También puede notar que intentamos sin éxito llamar a la función segundo()
desde dentro de la función do()
; los nombres de funciones siguen las mismas reglas que otras variables.
Otra peculiaridad con JavaScript, si empezamos a usar un nombre de variable dentro de una función sin declararla con la palabra clave var
, entonces el navegador asumirá que esa variable debe ser global. Entonces, si no se asegura de declarar siempre sus variables con la var
palabra clave, terminará con variables globales y no se dará cuenta!
Entonces, para resumir: cuando declaramos una variable, podemos usarla dentro de ese bloque de código o dentro de cualquier bloque anidado dentro de ella. Si intentamos usarlo fuera de su alcance, el valor se establece en indefinido
.
Es por esto que en nuestro último post, ponemos el minutero
Variable fuera de las funciones que la utilizaron, ya que aún teníamos que tomar esa variable después de que las funciones hubieran terminado..
temporizador var // Esta es una función de variable global run_right (stage, left) … timer = setTimeout (function () run_right (2, left);, 200);… function stop_running () document.getElementById ('j' ) .style.backgroundPosition = "0px 0px"; // Si 'timer' no se configuró como global, no podríamos detenerlo aquí clearTimeout (timer);
Para poder borrar el temporizador, necesitábamos deja de correr()
estar dentro del alcance de la variable minutero
. Entonces, hicimos minutero
una variable global que podría usarse en cualquier parte, lo que podría estar mal con eso?
En cualquier ámbito dado, es imposible tener dos elementos que se llaman lo mismo. Si intentara tener dos variables diferentes con el mismo nombre, el navegador simplemente escribiría sobre una de ellas. Entonces, si tuviéramos una variable llamada minutero
, y tenía una variable separada también llamada minutero
Eso fue llamado dentro del mismo ámbito, uno de ellos eliminaría y reemplazaría al otro, y tendríamos estragos en nuestro código. Si tuviéramos un variable global llamado minutero
, entonces interferiría con cualquier otra variable llamada minutero contenido en cualquier lugar de la página, incluidas todas y cada una de las bibliotecas de JavaScript adjuntas y archivos externos.
Esta es una gran fuente de dolores de cabeza, usted acaba de ver un complemento de JavaScript realmente bueno en algún lugar, lo descarga en su sitio y, de repente, todos sus otros complementos fallan ... Uno de los complementos fue descuidado con el global Las variables, por casualidad comparten el mismo nombre con otra cosa, su navegador se dispara a sí mismo y toda la página se detiene..
Lo que lo hace aún peor es que nunca notará este problema cuando pruebe el código por primera vez. Al igual que nuestro código de animación del último post, funcionará muy bien por sí mismo. Pero, cuantas más partes agregue, más probabilidades habrá de que haya un conflicto de nombres, y se quedará atascado clasificando una docena de archivos JavaScript diferentes que intentarán descubrir cuáles no se llevan bien..
Ahora puede preguntarse: "¡Las variables globales son muy convenientes! ¿Qué sucede si observo mi código con mucha atención y me aseguro de que no tenga ningún conflicto?" Eso podría funcionar en un mundo perfecto, pero en realidad a menudo tendrá a varias personas trabajando en diferentes partes de la misma página, o tendrá que regresar y actualizar diferentes partes de su código años más tarde, o incluso tendrá un código de terceros en su página que estará fuera de su control (como publicidad pagada).
Así que, en resumen, no querrá variables globales más de lo que querría tener alambres expuestos a lo largo de las paredes de su casa o maquinaria expuesta en su automóvil, es solo una cuestión de tiempo antes de que suceda algo que complique las cosas. Afortunadamente, hay una mejor manera de evitar estos escollos..
Podemos tener todos los beneficios de las variables globales sin los problemas mediante el uso de una técnica llamada encapsulamiento. Piense en ello como si estuviera construyendo un muro alrededor de su código con solo unas pocas puertas especiales, nada puede entrar o salir de ese código a menos que usted lo permita específicamente.
JavaScript tiene un tipo de variable llamada objeto. Los objetos son colecciones de datos definidas por el usuario que contienen información y funciones (denominadas propiedades y metodos, respectivamente). Vamos a escribir una función que crea un objeto especial que tiene todas las funciones que necesitamos "horneadas", e incluso nos permitirá tener más de un robot sin tener que duplicar nuestro código!
Comenzamos por definir una nueva función con un nombre de variable. Tendremos que pasar algunos argumentos a la variable. Voy a pasarle el elemento HTML que estaremos animando, además de algunos valores únicos para la velocidad de carrera y la altura de salto para que podamos variar los de robot a robot..
var RobotMaker = function (robot, run_speed, jump_height) // Pondremos todas nuestras funciones y variables en esta área. // Esto está dentro de nuestro muro 'impenetrable', por lo que nada en este // área entrará en conflicto con otro código. return // Dentro de aquí, colocamos todas nuestras 'puertas' ... // estas serán las únicas formas en que cualquier cosa puede // entrar o salir de este código. // Y, como esto aún está dentro del mismo 'alcance' // que RobotMaker, ¡podemos usar cualquier variable mencionada anteriormente!
Ya que vamos a colocar todas nuestras funciones dentro de nuestro nuevo "muro", ahora sería un buen momento para revisar los errores que tuvimos con el código original. (Puedes ver eso en acción aquí)
Puede observar que si hacemos clic en dos botones de ejecución (o en un botón de correr y saltar) sin hacer clic en Detener botón en medio, J continuará haciendo ambas acciones. Un segundo problema es que no importa en qué dirección J se enfrenta, cuando hacemos clic en la Saltar o Detener botón, se enfrenta a la derecha cada vez. Por último, si hace clic en el botón Saltar botón de nuevo mientras J se está cayendo desde un primer salto, continuará cayendo a través de la página en un bucle sin fin.
Para abordar estas cosas, debemos ser más específicos sobre lo que queremos que suceda con cada una de nuestras funciones:
Cuando hacemos clic en Ejecutar a la derecha:
Cuando hacemos clic en Ejecutar a la izquierda:
Cuando hacemos clic en Stop Running:
Cuando hacemos clic en Saltar:
En primer lugar, vamos a agregar algunas variables más ahora. Dado que el temporizador debe comportarse de manera diferente para correr y saltar, tendremos dos temporizadores separados. También queremos introducir un booleano
(verdadero / falso) variable para rastrear si deberíamos estar mirando hacia la izquierda o hacia la derecha, y haremos una escenario
variable solo para evitar que tengamos que escribir el nombre completo del elemento.
// Dentro de la función de RobotMaker ... var stage = document.getElementById ('stage'); var run_timer, jump_timer; var face_right = true;
Luego vamos a agregar de nuevo nuestras funciones para correr a la derecha, correr a la izquierda y saltar. Estos serán en su mayoría iguales, con algunas diferencias. En primer lugar, todas las referencias al elemento que estamos animando se pueden reemplazar con la variable robot
(que se aprobará como uno de los argumentos en el RobotMaker
función). En segundo lugar, hemos realizado algunos cambios leves en la velocidad de carrera y la altura de salto en las funciones, por lo que podemos variarlos pasando diferentes valores. En tercer lugar, estamos utilizando el face_right
variable para rastrear en qué dirección J está orientada (y en la función de salto, usando face_right
para decidir qué sprite saltar para mostrar). Finalmente, estamos utilizando temporizadores separados para correr y saltar..
// Dentro de la función de RobotMaker ... function run_r (phase, left) face_right = true; if ((left + (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)))) top = top - (top * .1); robot.style.top = top + "px"; jump_timer = setTimeout (function () jmp (arriba, arriba);, 60); else if (arriba) arriba = falso; jump_timer = setTimeout (function () jmp (arriba, arriba);, 60); else if (! up && (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;
Todas estas variables y funciones están dentro de nuestro "muro", por lo que ahora necesitamos hacer "puertas" para poder acceder solo a lo que necesitamos. Estas cuatro "puertas" serán objeto metodos para las mismas cuatro funciones que teníamos anteriormente y haremos referencia a las funciones protegidas anteriores. Además, completaremos nuestra corrección de errores al verificar en cada función si el jump_timer
va, y luego asegurándose de borrar la run_timer
. Recuerde, estos dos temporizadores están dentro del alcance dentro de la RobotMaker ()
Función, para que podamos usarlos aquí. Sin embargo, dado que no son variables globales, no tendremos problemas con ellas en ningún otro lugar..
// Dentro de la función de RobotMaker ... return run_right: function () if (! Jump_timer || jump_timer == no definido) 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: function () if (! jump_timer || jump_timer == undefined) clearTimeout (run_timer); if (face_right) robot.style.backgroundPosition = "0px 0px"; else robot.style.backgroundPosition = "0px -50px"; , jump: function () if (! jump_timer || jump_timer == undefined) clearTimeout (run_timer); jmp (verdadero, robot.offsetTop);
Ahora que hemos escrito una función que crea objetos, podemos usarla tantas veces como queramos para crear objetos que tengan las propiedades de animación que queremos. En la parte inferior de nuestra página, declararemos dos nuevos. RobotMaker
objetos, y pásales el elemento que queremos animar, una velocidad de carrera y una altura de salto.
var j = RobotMaker (document.getElementById ('j'), 1, 1); var j2 = RobotMaker (document.getElementById ('j2'), .8, 5);
Ahora no tenemos peligro de nada en el RobotMaker ()
La función se está filtrando e interfiere con nuestro código, y aún podemos llegar a las funciones que queremos a través de las "puertas" que instalamos así:
Entonces, ahora puedes ver el producto terminado en la pluma hyrgo.
Observe cómo ya no hay problemas con las funciones que interfieren entre sí, y puede operar cada robot individualmente sin afectar al otro. La encapsulación es una técnica increíblemente importante, y realmente debes familiarizarte con ella si quieres hacer un diseño web interactivo..
Si lo desea, por favor revise todo este código, comentado completamente, y puede obtener los sprites usando los siguientes enlaces: aquí están los primeros sprites y aquí están los segundos. Tenga en cuenta que para que el mismo código funcione con ambos sprites, necesitaba hacer el segundo sprite exactamente en el mismo formato y dimensiones que el primero.
¡Así que eso envuelve la parte tres de spriting! En nuestro próximo y último post, reemplazaré esos botones con hacer que nuestros robots sigan el mouse alrededor de la pantalla y le mostraré cómo configurar oyentes del evento y habilitar el soporte en navegadores y dispositivos táctiles.