Animación en JavaScript

By dedalo534

Introducción

En este artículo, echaremos un vistazo al arte de crear animaciones utilizando JavaScript — las animaciones se suelen utilizar para añadir experiencia de usuario, para la gente que utiliza navegadores que pueden soportarlo. Los usos más comunes son expansiones y contracciones suaves de paneles, barras de progresos, y efectos visuales en formularios.

Como cualquiera que haya visto una película sabe, la animación se hace actualmente mediante una gran cantidad de pequeños pasos, que hacen que parezca que algo se está moviendo. La animación es una técnica poderosa; es muy buena llamando la atención. La pega es que llama la atención tanto si es lo que queremos como si no. Los efectos animados pueden hacer lucir mejor una página web, con una mejor experiencia de usuario, pero es como la salsa de chile: no hay que pasarse añadiendo.

El contenido del artículo es el siguiente:

Un ejemplo bastante simple: yellow fade technique

Un uso bastante común de la animación es la Yellow fade technique, donde, a un área de la página que ha cambiado, se le pone un color de fondo amarillo, y poco a poco vuelve a convertirse al color de fondo original. Es una forma bonita y no invasiva de resaltar que algo ha cambiado (por ejemplo, hay nuevo contenido, o hay mensajes de feedback) sin molestar lo que el usuario está haciendo. Se puede echar un vistazo a un ejemplo para ver cómo funciona.

El funcionamiento detrás de esta técnica es que se pone el color de fondo de un elemento a amarillo, y entonces, en una serie de pasos, vuelve a ser el que tenía originalmente. Así que si el color de fondo original era rojo, entonces el color se pone a amarillo, luego a naranja-amarillo, luego naranja, luego rojo-naranja, y finalmente rojo. El número de pasos que se dan dicta cómo cambian los colores, y el tiempo entre pasos dicta cuanto tiempo se tarda en cambiar el color totalmente. En los cambios de colores, podemos usar una ventaja de CSS: un color puede definirse tanto como un triplete de números, ó una cadena hexadecimal. Así, rojo #FF0000 puede especificarse como rgb(255,0,0). Cambiar del amarillo al rojo se puede hacer en 5 pasos, que es ir del rgb(255,255,0) (amarillo) al rgb(255,0,0):

rgb(255,255,0)
rgb(255,192,0)
rgb(255,128,0)
rgb(255,64,0)
rgb(255,0,0)

Ponemos el color de fondo del elemento a rgb(255,255,0), después de un periodo de tiempo (digamos 100 milisegundos), cambiamos ese color de fondo a rgb(255,192,0), y tras otros 100 milisegundos a rgb(255,128,0), y así:

Color Tiempo
rgb(255,255,0) 0
rgb(255,192,0) 100ms
rgb(255,128,0) 200ms
rgb(255,64,0) 300ms
rgb(255,0,0) 400ms

El proceso completo dura 400ms (por debajo de medio segundo), y hace un cambio suave del amarillo al rojo. Convenientemente, aquí solo estamos cambiando una parte del color (el canal de verde; los tres canales del color rgb son el rojo, el verde y el azul), pero cambiar más de un canal a la vez es perfectamente posible. En este ejemplo, estamos cambiando el canal de verde de 255 a 0 en cuatro pasos, lo que significa que se cambia en 64 en cada paso.

Lanzar una acción después de un cierto periodo de tiempo se hace en JavaScript por medio de las funciones setTimeout y setInterval. La función setTimeout ejecuta nuestra acción después de un cierto retardo de tiempo; setInterval ejecuta nuestra acción una y otra vez, con cada ejecución separada por un intervalo de tiempo; esto es ideal para animación. En esencia, lo que tenemos que hacer para conseguir el cambio de fondo, es definir que tenemos que hacer en cada paso, e invocar a la función setInterval para que nos invoque en cada paso. La función setInterval tiene dos parámetros, la función a llamar para ejecutar nuestra acción, y el intervalo de tiempo en milisegundos.

Obviamente, quizás no queramos cambiar siempre del amarillo al rojo, así que nuestra función tendría que ser más genérica. Si conocemos los colores de inicio y fin, y el número de pasos, entonces falta utilizar las matemáticas para saber cuanto hay que cambiar cada color en cada paso. Si definimos un array startcolour como una lista de tres números ([255,255,0]) y endcolour como una lista similar ([255,0,0]), entonces la cantidad en la que tenemos que variar cada color en cada paso es:

var red_change = (startcolour[0] - endcolour[0]) / steps;
var green_change = (startcolour[1] - endcolour[1]) / steps;
var blue_change = (startcolour[2] - endcolour[2]) / steps;

Utilizamos setInterval para cambiar el color de fondo de un elemento en varios pasos:

var currentcolour = [255,255,0];
var timer = setInterval(function(){
    currentcolour[0] = parseInt(currentcolour[0] - red_change);
    currentcolour[1] = parseInt(currentcolour[1] - green_change);
    currentcolour[2] = parseInt(currentcolour[2] - blue_change);
    element.style.backgroundColor = 'rgb(' + currentcolour.toString() + ')';
}, 50);

En cada paso, tenemos currentcolour y cambiamos el color rojo en la cantidad red_change, el color verde en la cantidad green_change, y el color azul en la cantidad blue_change. Entonces, establecemos el color de fondo del elemento al nuevo color: [255,255,0].toString() es "255,255,0", así que para obtener el color rgb(255,255,0) utilizamos toString() para crear rgb(255,255,0) y utilizarlo como el color de fondo del elemento.

No obstante, setInterval llamará a nuestra función indefinidamente, no parará cuando el color de fondo deseado se haya alcanzado. Para parar un intervalo, hay que utilizar clearInterval(); el siguiente código cuenta el número de veces que la acción se ha llamado, y borra el intervalo después del número de pasos correctos:

var currentcolour = startcolour;
var stepcount = 0;
var timer = setInterval(function(){
    currentcolour[0] = parseInt(currentcolour[0] - red_change);
    currentcolour[1] = parseInt(currentcolour[1] - green_change);
    currentcolour[2] = parseInt(currentcolour[2] - blue_change);
    element.style.backgroundColor = 'rgb(' + currentcolour.toString() + ')';
    stepcount += 1;
    if (stepcount >= steps) {
        element.style.backgroundColor = 'rgb(' + endcolour.toString() + ')';
        clearInterval(timer);
    }
}, 50);

Y así es como se hace una animación: un paso en cada momento de tiempo.

¿Cómo conseguir que startcolour y endcolour se inicializen? Una forma fácil de hacerlo es envolver el código de más arriba en una función fade:

fade: function(element, startcolour, endcolour, time_elapsed) {
   ...code from above...
}

y podemos activar el cambio de color de fondo de un elemento con una función como esta:

fade(document.getElementById("yft"), [255,255,60], [0,0,255], 750);

o una "atenuación a rojo", que pone el elemento a rojo y luego lo atenúa a azul (el color de fondo del elemento), como:

fade(document.getElementById("yft"), [255,0,0], [0,0,255], 750);

Este ejemplo cambia el color de fondo, pero se podría cambiar cualquier cosa: el color del primer plano (para efectos sicodélicos de texto), opacidad (para ocultar o mostrar cosas), posición (para mover un elemento en la página), altura y anchura (para hacer crecer un elemento, o reducirlo a la nada antes de que desaparezca).

Animación con librerías de JavaScript

La animación es un efecto muy usado, así que la mayoría de las librerías JavaScript tienen alguna forma de soporte, incluyendo animaciones preconstruidas para los efectos más comunes. Por ejemplo, jQuery tiene un soporte incorporado para hacer que un elemento se vuelva transparente:

$("#myelement").fadeOut();

y una función animate() para hacer código propio más complicado:

$("#block").animate({
    width: "70%",
}, 1500 );

Esto es muy intuitivo - coge el elemento y cambia el atributo CSS width, en 1500 milisegundos, desde el valor que tuviera, al 70% - la función animate  está documentada aquí.

El framework Scriptaculous de Prototype ofrece facilidades del estilo, como Effect.Fade('id_of_element'), y otras muchas. La librería Yahoo UI también ofrece efectos similares:

new Y.Anim({ node: '#demo', to: { width: 70%, }}).run();

Si estamos utilizando una librería JavaScript en nuestro código, ya deberíamos saber que nos ofrecen formas más sencillas de animación que manejar esas animaciones por nuestra cuenta, con setInterval. No obstante, es muy importante entender qué hay bajo la cubierta - esto hará que nuestros scripts mejoren a largo plazo. Esta es la razón por la que empezar por un ejemplo con los principios básicos, antes de entrar con las librerías.

Se pueden encontrar más sobre cómo usar las diferentes librerías JavaScript en dev.opera.com, Introduction to JavaScript toolkits.

Un ejemplo más complejo: moviendo y cambiando el tamaño

Yellow Fade Technique sirve para demostrar las animaciones, pero es un poco, bueno, aburrida. Cuano la mayoría de la gente piensa en animación, piensa en movimiento. El Coyote habría sido menos gracioso si todo lo que él hubiera hecho hubiese sido cambiar de color.

Un buen truco para alertar al usuario de que algo está ocurriendo sin interrumpir su actividad son los mensajes no modales. En vez de mostrar un dialogo de alert(), que implica que usuario debe pinchar sobre el OK para poder continuar, simplemente ponemos el mensaje en un div flotante en la página, que, sin molestar, puede permanecer hasta que el usuario se de cuenta. Otra cosa interesante que podríamos hacer es permitir al usuario volver a ver el mensaje que ya había visto. Así, podemos implementar un mensaje flotante que, cuando se le pinche, se mueva a una esquina de la ventana, y que se le pueda pinchar para tenerlo de vuelta. Podemos ver una breve demo del "zooming message" para tener una idea.

Si estamos haciendo algún trabajo serio de animación, ó cualquier trabajo serio en JavaScript, merecerá más la pena utilizar una librería JavaScript. Esto nos permitirá poder dar la experiencia de usuario que deseamos sin tener que preocuparnos por las matemáticas necesarias para hacer funcionar las animaciones. (Sabemos, no obstante, cómo utilizar las matemáticas y cómo usar setInterval, habiendo leido el primer ejemplo más arriba, pero ahorraremos tiempo y neuronas permitiendo que alguien haga el trabajo duro por nosotros.)

La demo anterior utiliza la librería jQuery para hacer el trabajo, pero como se ha mencionado antes, la mayoría de librerías provee conceptos similares para animación; por tanto deberíamos ser capaces de reimplementar nuestro trabajo utilizando la librería que deseemos. En esencia, lo que tenemos que hacer es lo siguiente:

  1. Mostrar un mensaje flotante centrado en la pantalla
  2. Cuando se pinche sobre él:
    1. Mover su posición horizontal lo más a la derecha
    2. Mover su posición vertical hacia arriba
    3. Cambiar su anchura a 20px de ancho
    4. Cambiar su altura a 20px de alto
    5. Cambiar su opacidad a 20% para que sea bastante transparente
    y ocultar el texto que tiene dentro
  3. Cuando se pinche sobre esta "mini" version del mensaje, se trae de nuevo al centro de la pantalla (por ejemplo, lo contrario de lo que hicimos al encogerlo)

y así, mientras el usuario consigue una imagen clara de lo que ha ocurrido con su mensaje, la transición desde el mensaje hacia el mini-mensaje debería ser animada (para que pueda ver que su mensaje ha encogido hacia la esquina de la ventana).

Hacer esta animación con jQuery es sencillo: utilizamos la función .animate y especificamos que queremos como resultado de la animación (y cuanto va a llevar el conseguirlo):

$(ourObject).animate({
    width: "20px", height: "20px", top: "20px",
    right: "20px", marginRight: "0px", opacity: "0.2"
  }, 300);

Esto cogerá ourObject y, en unos 300 ms, cambiará su anchura y altura a 20px, su tope y posición derecha a 20px, su propiedad de estilo margin-right a 0px, y su opacidad (en los navegadores que lo soporten) al 20%. Después, para hacer que la animación funcione cuando se pinche en el mensaje, falta programar algo en el estilo jQuery:

$(ourObject.click, function(){
  $(this).animate({
    width: "20px", height: "20px", top: "20px",
    right: "20px", marginRight: "0px", opacity: "0.2"
  }, 300)
});

Restaurar el mensaje cuando se pinche sobre él requiere otra llamada a .animate():

$(ourObject).animate({
    width: "400px", height: "75px", top: "50px",
    right: "50%", marginRight: "-200px", opacity: "0.9"
  }, 300);

y con una poca de lógica para saber cuando el mensaje se está mostrando en grande o en pequeño (para saber que animación debe ejecutarse), y algo de CSS para definir el estilo inicial del mensaje (grande, verde, centrado horizontalmente), eso es todo lo que necesitamos. Unas 30 lineas de script. ¡Esto es por lo que utilizar librerías es una muy buena idea!

Transiciones CSS

Finalmente, algunas animaciones (no todas) puede hacerse in utilizar JavaScript para nada!!. Safari y otros navegadores basados en Webkit, y Firefox 3.1 pueden hacer transiciones de un valor CSS a otro sin utilizar JavaScript. El código:

div { opacity: 1; -webkit-transition: opacity 1s linear; }
div:hover { opacity: 0; }

hará que el div se haga transparente en un segundo, en navegador que lo soporte, cuando se pasa el ratón por encima. Estas transiciones CSS son nuevas, no obstante, no están totamente soportadas salvo en las versiones más modernas, asi que si queremos que nuestra animación funcione para la mayoría de nuestro público entonces necesitaremos utilizar scripts para ello.

Resúmen

Esto concluye un vistazo a la funcionalidad de animar páginas web utilizando JavaScript - hemos visto algunos ejemplos creados con los principios básicos utilizando las funciones setTimeout y setInterval, y luego hemos echado un vistazo a cómo usar librerías JavaScript para crear animaciones más deprisa.

Ejercicios

  1. ¿Cúal es la diferencia entre setTimeout y setInterval?
  2. Si setInterval no existe, ¿cómo podríamos emularla?
  3. ¿Cómo podríamos hacer que un elemento pasase del visible total al invisible total en 20 pasos con un intervalo de 1.5 segundos?
  4. ¿Cómo podríamos hacer que un elemento pasase del visible total al invisible total y luego volverlo visible de nuevo en 20 pasos con un intervalo de 1.5 segundos?

Sobre el autor

Foto del autor del artículo, Stuart Langridge

Stuart Langridge es posiblemente la única persona en el mundo en tener un BSc en Informática y Filosofía. Cuando él no esta trasteando con ordenadores, él es un hacker de JavaScript, Django y Python en GCap Media, autor de DHTML Utopia en SitePoint, y un bebedor de cervezas decentes. Además, es una cuarta parte del equipo en LugRadio, un programa de radio de estreno mundial sobre temas de software "Free and Open Source". Sus divagaciones sobre la web, scripts, software open source, y cualesquiera otras muchas cosas, se pueden encontrar en kryogenix.org; Stuart puede ser encontrado fuera buscando en el área de fumadores.

This article is licensed under a Creative Commons Attribution-Noncommercial-Share Alike 3.0 Unported license.

Comments

The forum archive of this article is still available on My Opera.

No new comments accepted.