soy Kseso y esto EsCSS

Javascript con Furoya: sprite jpg animado con velocidad variable y secuencia de frames aleatoria

En este artículo, Furoya nos muestra cómo emular un gif animado a partir de un sprite jpg con controles de reproducción (play/pause), velocidad variable en su ejecución y además la posibilidad de que sus cuadros o frames se muestren de forma aleatoria.

Javascript con Furoya: sprite jpg animado con velocidad variable y secuencia de frames aleatoria

✎ 0
COLABORACIÓN AUTOR INVITADO
Javascript con Furoya: sprites jpg animados con velocidad variable y secuencia de frames aleatoria

Como todos sabemos, mis colaboraciones con el blog no son un curso, ni siquiera un tutorial de javaScript. El objetivo (además de evitar que me aburra en el trabajo) es mostrar diferentes salidas a cuestiones que pueden presentarse durante el desarrollo de una página web, a veces enmascaradas con efectos más caprichosos que útiles.

A estas alturas, ya hay bastantes métodos aprendidos como para empezar a aplicarlos en casos reales. Pero yo sigo con los más delirantes.

¿Recuerdan el artículo y su este efecto Sustituyendo Gif´s animados por Css y animación de sprites Css?

Pues resulta que años atrás no existía la posibilidad de animar imágenes con CSS, y la única opción para manipular un pseudo-'GIF animado' (que terminaba siendo un JPG animado) era javascript. De hecho, aún tiene más posibilidades que CSS para hacer efectos.

El que voy a presentar ahora tiene un antecedente, ya lo publique hace mucho tiempo en algún foro, aunque con una variante que lo conecta con a la serie de artículos anteriores. Es una imagen animada a través de un control deslizante que al moverlo "arrastra" con su velocidad y dirección una serie de cuadros o frames ubicados como sprite, y que dan la ilusión de movimiento en una sola imagen.

Pero en aquella época no solamente carecíamos de @keyframes, sino también de un input type=range en HTML, así que la antigua versión se valía de una scrollbar "flotante" que al arrastrarla le pasaba sus valores con onscroll al contenedor de la imagen. Hoy todo es mucho más fácil:

See the Pen JPG animado. (1) by solipsistaCP (@solipsistacp) on CodePen.

Como se habrán dado cuenta, el trabajo fuerte está en el diseño de la imagen, y luego en pasar los valores justos a los atributos HTML del range.

Los cálculos son realmente fáciles, sólo hay que pensarlos un poco. El CSS nada más ubica la imagen en un contenedor que tiene overflow:hidden y el tamaño exacto de un frame de la secuencia, el resto es casi todo decorativo (ahí "sobra" el formato para igualar el aspecto de los input).

Y la función javascript no tiene nada más que una línea

document.querySelector("form figure>img").style.left = valor +"px";

Como dije, el funcionamiento es muy sencillo, no voy a detenerme en él más que para describirlo de una forma somera.

  • Todos los cuadros de la animación se colocan en una "tira" (en este caso, horizontal) en vez de ordenarlos en capas como se hace con un GIF animado. Esto puede crear archivos enormes, es cierto, pero se puede optimizar el tamaño (no el peso) usando una tabla en vez de una tira; debe haber ejemplos ya publicados por ahí.
  • Si la secuencia es horizontal, el step debe tener el ancho de cada cuadro.
  • El min debería ser "0", pero como vemos que el desplazamiento se hace a la izquierda moviendo la imagen dentro de su contenedor con posiciones left negativas, entonces el mínimo será un número negativo igual al ancho total menos el ancho del último cuadro (bah, de cualquier cuadro, porque todos miden lo mismo). La razón es evidente :
    si tenemos una imagen de 200 de ancho por 100 de alto para ver en un contenedor de 100 × 100, la primera mitad la veremos si la imagen tiene un left:0 , y la última si le damos un left:-100 (ancho total menos un cuadro), porque si le damos -200 se ocultaría completamente por la izquierda. Como el atributo min no acepta un valor mayor que max (no sé por qué), en el ejemplo nos queda min="-2800" max="0".
  • El evento oninput es casi exclusivo de los input's, y se dispara cuando modificamos su valor, pero en tiempo real; algo que lo diferencia de onchange, que lo ve recién cuando salimos del control. Luego llama a la función que tiene como parámetro nada mas que el valor del range, que siempre será una posición que coincida con el comienzo de un cuadro de la imagen, así que aprovechamos primero para escribirla en la barra de título (como una forma de controlarla visualmente) y después la usamos para cambiar el left de la imagen.
  • Nota: para ver esos valores en la titlebar, pueden usar la versión Debug de Codepen.

Pero una "rotación 3D" no es lo único que se puede hacer con JS. Además de iniciar y detener la secuencia hay valores que se pueden modificar de manera aleatoria o con controles más finos que el arrastre de cuadros.

Versión con controles de reproducción y velocidad

Aquí va una segunda versión del efecto, esta vez con una tira vertical (mucho más cómoda para manejar en un editor) y con menos cuadros. Los controles de PLAY y Stop son clásicos, y tienen agregado un deslizante como SPEED y un ya menos común RANDOM que altera la secuencia y la velocidad entre cuadros con valores al azar. Esto último es quizá lo más complejo de imitar en un GIF animado, aunque no es imposible.

See the Pen JPG animado. (2) by solipsistaCP (@solipsistacp) on CodePen.

  • Ya conocemos el setTimeout(instrucción , espera), entonces imaginamos cómo se puede secuenciar el cambio de cuadro. Iniciamos la función que mueve la imagen con laImagen.style.top = posicionDelFrame+"px" a su primer cuadro ("posicionDelFrame" sería una variable inventada con un número que generalmente es "0", para mostrar el cuadro que está arriba).
  • Una vez hecho el cambio de top se ejecuta un setTimeout con el tiempo que tendría el GIF animado entre frames, y que ejecute otra vez su misma función.
  • Claro que antes habría que sumarle a la posición (que como se habrán dado cuenta está en la variable llamada "posicionDelFrame") un valor fijo que es la altura de cada cuadro, para que al cambiar otra vez el top haga visible el cuadro siguiente.
  • Así se repite hasta que llega al borde superior del último cuadro, entonces tendremos que agregar a la función un condicional, para que cuando la variable de posición lo alcance, en vez de seguir sumando vuelva a "0", y el ciclo recomience y se haga infinito.
  • Al menos, hasta que hagamos un STOP, que no será más que un clearTimeout(laVariableDelSetTimeout) que hasta se puede poner como valor de atributo en el HTML del botón, y nos ahorramos escribir la función en el elemento <script>.

Viendo el ejemplo es fácil encontrar todas estas instrucciónes (sí, tienen otros nombres, je), y es más que nada porque está lleno de comentarios.

La velocidad asociada a este PLAY también prescinde de una función, la instrucción para cambiar el valor de var velocidad está dentro del mismo atributo oninput del range.

Y el CSS también iguala los formatos para moz y webkit. (Estoy olvidando injustamente el formato "range" de otros navegadores.)

El boton RANDOM ejecuta una función casi igual a la anterior. La diferencia está en que genera 2 valores aleatorios para poner en el top y en el tiempo del setTimeout.

El primero es más rígido para las cuentas : tiene que crear números enteros entre "0" y "la cantidad de cuadros menos 1", para luego multiplicarlo por la altura de cada cuadro, de esa forma siempre va a devolver un valor igual al comienzo de cada frame, que al pasarse al top de la imagen debería ser negativo; pero esto se resuelve fácil, en el ejemplo no lo multiplico por 160; sino por -160.

No me detengo en detallar estas operaciones porque son cuentas de matemática elemental, no tiene que ver realmente con javascript.

El segundo valor aleatorio es más fácil y más libre, ya que su mínimo y máximo dependen de cómo nos guste que se vea la animación. Se genera un número al azar entre "0" y "casi 100" y luego se le suma "100". De esta forma, el resultado es un número entre "100" y "199.9999999999" (más o menos) que será la velocidad en milisegundos para repetir la función y mostrar el siguiente cuadro.

Si queremos que pueda ser más rápido, le bajamos el número sumado a menos de 100; si queremos que pueda ser más lento, le subimos el número multiplicado a más de 100.

En uno de mis primeros artículos explicaba cómo funciona Math.random(), si lo creen conveniente, le dan una repasada.

El resultado es una calavera que ya no parece que "ríe", sino que al tener movimientos aleatorios parece que "habla".

No tengo los créditos de las imágenes; y es que han aparecido tantas veces en decenas de repositorios para GIF animados que resulta imposible rastrear los autores originales. Espero que no vengan a reclamarme nada.

Autoría

Furoya: Autor del artículo

Artículo original de Furoya.
La intención del autor con sus colaboraciones no es que los artículos sirvan para hacer un copy&paste de códigos sino que comprendas y aprendas la lógica y el cómo trabaja javaScript.
Y a partir de lo expuesto experimentes tú.
El autor del post y el editor del blog te animamos a que plantees tus dudas o reflexiones y que compartas tus realizaciones en base a lo expuesto en los comentarios. Recuerda que puedes incluir pens (ejemplos en Codepen.io) en ellos.