duduromeroa.com

#javascript #Canvas #animación #interaccion

Javascript para interacción jugable: imágenes animadas e interactivas y uso de sprites con API canvas en web



#javascript #Canvas #API #javascript

Aclaración: Contenido para quienes ya comprenden y practican HTML, CSS y JS. Para profundizar en Javascript ir a esta sección o a cualquiera de los libros mostrados abajo en la bibliografía.



Redibujando animaciones con request Animation Frame()

El método requestAnimationFrame() trabaja similar a un parpadeo, imperceptible pero necesario, que es usado para actualizar y mostrar las varias secuencias de una imagen que percibimos animada desde una página web. La razón de ese parpadeo es que, en un área formada por pixeles (por ejemplo, una página web) todo elemento que se vea animado debe mantener este ciclo: dibujarse-aparecer-desaparecer-volver a dibujarse, para que así la animación sea perceptible.

Para mdn Mozilla Web docs "El método requestAnimationFrame proporciona una forma más fluida y eficiente de realizar una animación al llamar a una imagen (cada vez) que el sistema está listo para pintar (o redibujar) la imagen".

Por otro lado, la velocidad en que esas secuencias de imágenes serán mostradas (o redibujadas), una a una, será medida como una frecuencia de actualización o de ciclos por segundo.

Para ejemplificar eso, intente el lector pestañear tantas veces como pueda durante un segundo. ¿Cuántas veces fue posible? En sistemas digitales (como cine digital o videojuegos) las imágenes pueden 'pestañear', o ser actualizadas entre 60 veces por segundo (ó 60 ciclos/s; o 60hz) o más; hasta 120hz.

En el ejemplo inferior, el desplazamiento del recuadro se debe a que la función requestAnimationFrame() reinicia el dibujo del recuadro y actualiza su posición según posX = posX + pixelesRecorridos;, que a su vez, la constante pixelesRecorridos tiene el valor de 1 pixel de desplazamiento hacia adelante (por cada invocación) de la función requestAnimationFrame(dibujar00);. Si ese valor crece, entonces crecerá la cantidad de pixeles que el recuadro se desplaza por cada invocación de requestAnimationFrame.

En HTML

<canvas id="areaCanvas00" 
width="600" height="400"></canvas>

En CSS

 #areaCanvas00{width: 100%;} 

En JS

const canvas01 = document.getElementById('areaCanvas00');
const ctx = canvas01.getContext('2d');
let posX = 0;
const posY = 100;
const areaElementoMovil = 80;
const pixelesRecorridos = 1;

function dibujar00() {
    ctx.clearRect(0, 0, canvas01.width, canvas01.height);
    ctx.fillStyle = 'peru';
    ctx.fillRect(posX, posY, areaElementoMovil, areaElementoMovil);
    posX = posX + pixelesRecorridos;
    if (posX > canvas01.width) {
        posX = 0 - areaElementoMovil;
    }requestAnimationFrame(dibujar00);
}requestAnimationFrame(dibujar00);

Métodos de alteración de posición y ubicación desde Canvas

Existen otros métodos para alterar la posición y ubicación de aquellos elementos dibujados en dos dimensiones. Cada método captará parámetros según cada necesidad de dibujo. Intentaré mostrar aquí la mayoría de ellos.

Métodos de dibujo de polígonos 2D en Canvas API

fillRect()

Método de dibujo desde cuatro coordenadas de posición. Estos cuatro argumentos darán la ubicación del elemento y sus dimensiones, siempre dentro del contexto del área del canvas: fillRect(posición-en-X, posición-en-Y, ancho-del-recuadro, alto-del-recuadro)

En el recuadro inferior, las coordenadas de fillRect corresponden a: (0, 0, 100, 100). El método fillStyle = 'peru'; (como ya hemos visto) le agrega color a ese elemento.

clearRect()

Método que borra todos los pixeles dentro de un área rectangular dada. Pero cuidado, ese 'borrado' es especial. Visualmente es perceptible como el color blanco. Pero lo que ocurre es que los valores RGB (red, green, blue) llegan a cero, con una opacidad (canal alfa, que no define ningún color, solo un estado de transparencia) también en cero.

En el ejemplo de abajo, las coordenadas para clearRect son (0, 0, 50, 50). En este contexto, 'limpiar' los pixeles significa también 'recortar' el elemento. Prueba de eso es lo siguiente: toque el icono de la luna (arriba, a la derecha de esta página). Al oscurecerse el color blanco solo será observable el área recortada del recuadro café.

strokeRect()

Método que agrega grosor y color al delineado externo del recuadro dibujado. Ojo con esto: al ubicar el elemento en coordenadas X y Y en cero, el límite de la esquina izquierda superior del área del canvas se sobrepondrá en la mitad del grosor de los mismos lados del área dibujada (ver ejemplo abajo). Eso se resuelve colocando el elemento más allá de las coordenadas cero en X y Y (no se muestra eso en este ejemplo).

Iniciado formas lineales con beginPath()

Método sin parámetros que, una vez invocado para cada trazo, lo inicia en forma de nueva línea, independiente del color o del grosor de un trazo anterior.

Formas lineales con método moveTo()

Si una línea esta formada por dos puntos (uno de inicio y otro final) este método da la posición (en X, horizontal; y en Y, en vertical) del primer punto de la línea.

Formas lineales con método lineTo()

Este método da la posición (en X, horizontal; y en Y, en vertical) del segundo punto de la línea.

Desconozco la causa por la que he hallado explicaciones confusas acerca de cómo es el comportamiento de estos dos métodos, especialmente desde sus argumentos-coordenadas. Espero que la siguiente explicación sea breve y eficaz.

Tanto para moveTo() como para lineTo(), los dos argumentos en paréntesis representan coordenadas en XY. En un área de canvas de 100x100px (dado en el elemento <canvas>) el desplazamiento de los puntos de inicio y final del trazo son los siguientes (ver gráfico, código y resultado final):

Gráfico que explica el comportamiento de los métodos de canvas, moveTo() y lineTo(), para el trazado de líneas. Fuente: duduromeroa.com
Comportamiento de los métodos moveTo() y lineTo() para el trazado de líneas. Elaboración: www.duduromeroa.com

El resultado en código (Javascript) se muestra de esta forma:

En Javascript

// Inicio de trazo
     ctx04.beginPath();
     // Doy estilo
     contexto2D.strokeStyle = "peru";
     contexto2D.lineWidth = 5;
     
     // Ubicación del PRIMER vértice según coordenadas XY 
     contexto2D.moveTo(0, 0);

     // Ubicación del SEGUNDO y último vértice
     contexto2D.lineTo(45, 45);
     // Dibuja la línea
     contexto2D.stroke();



Para otros métodos y combinaciones de valores, especialmente para dibujos más complejos, recomiendo la web Dibujando formas con canvas (versión en español) de la mdn web docs_. Por ahora, solo me centraré en los elementos básicos arriba mostrados para dar ejemplos de interacción desde canvas en las siguientes secciones.

Usando sprites animados e interactivos desde la API canvas en web

Un sprite (traducido al español podría significar enanito, duendecito o alma de duendecito) es una imagen digital plana que contiene docenas de secuencias de un mismo elemento visual. Esas secuencias permiten componer, desde un solo elemento, todas las respuestas visuales (en forma de poses o acciones) de uno o más objetos gráficos en pantalla.

Según B. Loguidide y M. Barton en su libro Vintage game consoles (Focal Press, 2014) el primer videojuego que aplicó sprites (posiblemente aún no era llamados así) para animar un pequeño elemento fue Atari Gran Trak 10 (1974), en EE.UU. Este juego mostraba una pantalla oscura con apenas unas líneas punteadas que asemejaban a curvas de carretera. El uso del sprite se notaba en la imagen del vehículo cuando el videojugador lo controlaba, dando la sensación visual de que esa pequeña imagen contenia volumen. En la década de 1990 la técnica de los sprites fue mucho más aplicada por las consolas de videojuego y revisada desde las matemáticas y la ciencia de la imagen computarizada, especialmente por científicos como Alan Ray Smith (1943), interesado en el potencial de los gráficos por computadora en esas décadas.

A partir de allí, el uso de sprites en videjuegos evolucionó; de una pequeña imagen monocolor a gráficas y texturas multicolores. Hoy en día los sprites todavía mantienen dos principios: agrupar múltiples secuencias de imágenes en una sola área de pixeles y ahorrar trabajo de proceso al mostrar esas imágenes con mínima nitidez a causa de un pixelado evidente, pues eso restringe positivamente la cantidad de colores que el sistema debe procesar en cada animación. Expongo dos ejemplos.

En la primera tira de manzanas la secuencia de la mordida es corta, apenas 5 fotogramas, comparado con la veintena que podría tener un sprite más complejo. La nitidez el dibujo de la manzana es aceptable, pues contiene 27 kilobytes (o 27.000 posibles valores) en contenido de color, con áreas de color limpias. Un ejemplo de esa animación (pero sin el uso del elemento canvas) puede revisarse aquí

Gráfico de ejemplo para explicar el uso de sprites sheets en animación web mediante elemento canvas. Fuente: duduromeroa.com
Conjunto de sprites, sin pixelación. Un sprite contiene secuencias de imágenes que, sumadas, dan la sensación de estar animadas. Elaboración: www.duduromeroa.com

La segunda tira de manzanas se muestra con menos pixeles. Eso tiene la función de reducir la cantidad de colores y por lo tanto, su consumo de proceso al momento de mostrarse animado. Esta tira contiene cinco veces menos valores de colores que la anterior (con una relación de 16px por lado), lo que la hace también más ligera para ser procesada cuando se muestre animada.

Gráfico de ejemplo para explicar el uso de sprites sheets en animación web mediante elemento canvas. Fuente: duduromeroa.com
Conjunto de sprites, con pixelación. Esta imagen contiene 5kb (casi 5 veces menos) en contenido de color que el sprite anterior. Elaboración: www.duduromeroa.com
Gráfico de ejemplo de sprites sheets para Nintendo, con el personaje Mario Bros. Fuente: duduromeroa.com
Conjunto de sprites para el personaje Mario Bros., para la compañía de videojuegos Nintendo, en 1982. Fuente: mariomayhem.com

Las animaciones con composiciones en sprites fueron bastante populares en videojuegos estadounidenses desde 1980, cuando las capacidades de procesamiento gráfico ya no estaban limitadas a simples cuadraditos en pantalla, como ocurrió con los gráficos de la consola Atari a inicios de 1980. Tal era su popularidad que, en la década de 1980, habían artistas digitales especialistas en diseño de sprites para crear paisajes, armas, personajes, entre otros. Hoy en día es posible generar imágenes sprites automáticamente, o incluso arte pixelado o pixel art; tanto con herramientas de pintura digital, como pixilart.com, con chats de Inteligencia Artificial, como pixelfy.ai y con generadores de sprites, como rosebud.ai

Tal era su popularidad que, en la década de 1980, habían artistas digitales especialistas en diseño de sprites para crear paisajes, armas, personajes, entre otros. Hoy en día es posible generar imágenes sprites automáticamente, o incluso arte pixelado o pixel art; tanto con herramientas de pintura digital, como pixilart.com, con chats de Inteligencia Artificial, como pixelfy.ai y con generadores de sprites, como rosebud.ai

Volviendo al tema en cuestión, con el elemento canvas en Javascript también es posible mostrar secuencias de sprites de forma interactiva, a mostrar en la próxima sección. Aquí, el método drawImage()junto con sus parámetros será clave para ejecutar animaciones a partir de secuencia de imágenes. La explicación de ese método la haré en la siguiente sección.

Gráfico de ejemplo para explicar la estructura de una imagen sprite para ser usada en animación mediante elemento canvas en HTML. Fuente: duduromeroa.com
Plantilla de 3 fotogramas. Cada fotograma será mostrado, recortado y en forma de secuencia, para simular el encendido de las luces en el gráfico del semáforo. Elaboración: www.duduromeroa.com

Siguiente: Método drawImage() para animar imágenes

Anterior: Uso de la API Canvas en web para interacción