Actualización: el buen amigo panino5001 me ha hecho notar un grave error en mi código. A mi favor solo puedo decir que esto lo escribí ya muy entrada la madrugada. El problema es que en lugar de usar setTimeout, he usado setInterval y eso es malo por que agota la memoria del navegador si se usa como lo he usado aquí.
Continuando con esta serie de animación en Javascript, pongamos en práctica la función setTimeout de la cual hablamos en la introducción a esta serie de animación en Javascript.
setTimeout toma dos parámetros los cuales especifican; primero) la función a ejecutar, y segundo) el ” tiempo de espera” antes de ejecutar dicha función. Por ejemplo,
function myAlert(){
alert('Hello There!');
}
var tO = setTimeout(myAlert, 400);
El tiempo (400) se especifica en milisegundos. Notarás que he guardado el valor de retorno de setTimeout en una variable (tO). No voy a entrar en detalle por ahora del porque hago esto, pero por el momento piensa que cada que llamas setTimeout (o setInterval) es buena idea guardar una referencia a dicha llamada en una variable por si acaso tengas que cancelar antes de que se ejecute la función.
Hasta ahora todo bien, pero tenemos un problema; la función myAlert se ejecuta solo una vez y jamás vuelve a ser ejecutada. Para enteder por que esto es un problema, hay que entender primero el modelo fundamental de las animaciones en javascript.
¿Como funcionan las animaciones en javascript?
Javascript usa lo que llamamos frames para hacer una animación. En realidad, una animación no es más que un conjunto de varios escenarios estáticos mostrados uno tras otro en un orden específico de modo que el resultado final parezca estar en movimiento. Hasta cierto grado, estas animaciones son una ilución.
Ahora, para poder lograr esta cadena de escenarios, es necesario que nuestra función se ejecute más de una vez, y es ahí donde está el problema con nuestro ejemplo anterior. Para solucionar dicho problema, les presento funciones recursivas.
Recursion en Javascript
Una función recursiva no es más que una función que dentro de si misma se llama a si misma. Veamos un ejemplo:
function recursiva(){
console.log('recursiva');
recursiva();
}
recursiva();
//No intenten esto, estan advertidos.
Si ejecutaras esa función y hecharas un vistazo a la consola de javascript, te darías cuenta que la palabra “recursiva” es imprimida indefinidamente una y otra vez. Esto es por que la función se está llamando a si misma y con cada llamada hace dos cosas. Primero imprime la palabra “recursiva” y luego vuelve a llamarse a si misma. Esto va a continuar por todo el tiempo que mantengamos la ventana del navegador abierta. Si esta función, en lugar de imprimir una palabra, cambiara la posición de un objeto, entonces el objeto jamás dejaría de moverse. Para lograr un efecto más real, necesitamos una manera de poder detenr la recursividad. Para esto podemos usar un contador que nos indique cuando tenemos que detener las llamadas a la función.
var contador = 0;
function recursiva(){
console.log('recursiva');
if(contador++ < 20){
recursiva();
}
}
recursiva();
De esta menera podemos controlar la cantidad de veces que se ejecuta la función. Podemos utilizar otro tipo de controlador para determinar si la función se llama nuevamente o no. Unos ejemplos que se me ocurren es determinar si la función se llamará nevamente en base a cuanto tiempo se ha estado ejecutando, en base a la pocisicón de cierto elemento, en base a las acciones del usuario, o en base a otras características de un elemento como tamaño u opacidad.
¿Y que hay de nuestro buen amigo setTimeout?
El papel de set timeout en todo esto es ayudarnos a mantener un balance apropiado en cuan seguido se llama la función responsable de la animación. Si tomaramos el ejemplo tal como lo hemos estado haciendo, y quicieramos aplicarlo a animación, pronto nos daríamos cuenta que el intervalo entre una llamada a la función y la siguiente es muy mínimo y no es lo apropiado por varias razones, de las cuales menciono dos importantes:
La primera es que las animaciones que se producen son exageradamente rápidas.
La segunda es que los recursos que se consumen son más altos ya que ni bien termina el navegador de renderizar un escenario cuando ya tiene que renderizar otro.
Para resolver este problema, ponemos un itervalo de tiempo entre las llamadas a la función:
var contador = 0;
function recursiva(){
console.log('recursiva');
if(contador++ < 20){
setTimeout( recursiva, 200);
}
}
recursiva();
Por el momento esto es todo. Más adelante hablaremos de más problemas de desempeño como los dos que mencioné anteriormente y de como resolverlos. En nuestra siguiente entrega, ahora sí empezaremos a animar cosas.