Animaciones 2D utilizando el Canvas de HTML5
Mi objetivo es brindar una breve introducción a las animaciones en dos dimensiones utilizando canvas, así como tambien introducir algunos conceptos contextuales sobre el tema.
Si bien este artículo no posee demasiada profundidad técnica, es recomendable tener los conceptos básicos del canvas asimilados. Un buen comienzo se puede encontrar en la documentación de Mozilla (MDN).
Introducción
Con la concepción de lo que hoy en día llamamos HTML5, los browsers han ganado muchas herramientas para proveer una mejor experiencia de usuario.
El canvas, sin duda alguna, es la característica más marketinera que propone este “estándar”, permitiendo generar visualizaciones complejas de manera nativa. Existen en la web algunos ejemplos espectaculares que demuestran el gran potencial que tiene esta característica, tanto en dos dimensiones como en tres.
A diferencia de otros modos de render disponibles en el browser (DOM y SVG) el canvas no dispone de una persistencia de estado más allá del aspecto visual que presenta, lo cual implica la ausencia absoluta de referencias de que y donde dibujamos.
Librerías como three.js, heatmap.js, d3.js, entre otras, brindan grandes ejemplos de lo que se puede lograr dentro de un browser, explotando tanto las características 2d como 3d (utilizando WebGL) de este elemento para proveer soluciones a distintas problemáticas.
Soporte
El elemento <canvas> esta disponible en la gran mayoría de los browsers actuales:
- Firefox desde la versión 1.5 (parcial)
- Safari desde la versión 2
- Opera desde la versión 9
- Chrome desde la versión 4
- Internet Explorer desde la versión 9
Más información sobre el soporte en los distintos browsers se puede encontrar en varios sitios de la web, por ejemplo caniuse.com.
Alternativas
Si bien es totalmente contextual el requerimiento, el uso de canvas se puede reemplazar por animaciones CSS3, SVG o inclusive las ya clásicas animaciones DOM (scriptaculous, jQuery.fx, etc). Obviamente, cada una de estas soluciones tiene sus casos de uso recomendados y los no recomendados (así como su grado de dificultad), pero por supuesto, en ésta ocasión les hablaremos puntualmente de Canvas :).
Animaciones básicas
En una definición simple, podríamos decir que una animación, es ni más ni menos que un conjunto de cuadros (imágenes) que se suceden con un intervalo dado generando la impresión de movimiento.
Esta idea no cambia cuando hablamos de animación utilizando canvas, idea que aplicaremos en un pequeño ejemplo, en el cual desplazaremos un cuadrado por el canvas.
Para este primer ejemplo, primeramente vamos a generar nuestro HTML, que contendrá el canvas sobre el cual dibujaremos:
<canvas id="canvas" width="100" height="100"></canvas>
Para hacerlo más visible a nuestros queridos ojos con respecto al background, vamos a darle un borde y margen:
canvas { border: solid 1px #ccc; margin: 20px; }
El corazón de la animación está dado por el proceso de generar el intervalo entre las imágenes, el cual resolveremos inicialmente (y a fines de simplificar) con el uso de la función setInterval. De cualquier manera hablaremos sobre el framerate en breve.
[tipexperto titulo = “Nota”]Idealmente el renderizado debería darse siempre a la mayor velocidad posible (~60fps utilizando el método requestAnimationFrame), independientemente de los requerimientos/velocidad de la animación, sus objetos y/o sucesos.[/tipexperto]
En este primer ejemplo vamos a generarlo en 24 frames por segundo (fps de ahora en más), que resulta lo suficientemente fluido a la vista y no requiere mayor complejidad.
// Nuestro código de inicialización irá aquí setInterval(function() { // ... la magia sucede aqui }, 1000 / 24);
Luego, para dar vida a esta grandiosa animación, completamos la lógica:
// Nuestras variables var canvas, ctx, x, y; // Obtenemos una referencia al canvas canvas = document.getElementById('canvas'); // Y a su contexto 2d ctx = canvas.getContext('2d'); // Generamos las coordenadas iniciales x = 0; y = 0; // Los frames seran renderizados por este intervalo // aproximadamente a 24 frames por segundo (fps) setInterval(function() { // Limpiamos el canvas, eliminando el contenido // desde el punto (0, 0) al punto (100, 100) ctx.clearRect(0, 0, 100, 100); // Generamos nuevas coordenadas // Que basicamente representan un desplazamiento lineal x = x >= 100 ? 0 : x + 1; y = y >= 100 ? 0 : y + 1; // Y dibujamos nuestra figura ctx.fillRect(x, y, 50, 50); }, 1000 / 24);
Este ejemplo se puede encontrar funcional en este fiddle.
Utilizando imágenes
Por suerte para nosotros, canvas provee métodos para trabajar con imágenes, lo cual además de generar hermosos backgrounds e imágenes estáticas nos permite utilizar “sprites”.
En este caso (bastante general por cierto), utilizaremos un sprite que contiene las gráficas de distintos estados de movimiento de un personaje (bastante elegante por cierto).
Para dibujar las imágenes utilizaremos el método drawImage, cuya firma acepta hasta 9 (¡nueve!) argumentos. Más información sobre este horrible método se puede encontrar en el tutorial de Mozilla previamente mencionado.
<canvas id="canvas" width="100" height="100"></canvas>
El background de este ejemplo, llega a ustedes gracias a @leaverou y su útil artículo/catálogo “Checkerboard, striped & other background patterns with CSS3 gradients“.
canvas { background-color: #eee; background-image: -webkit-linear-gradient(45deg, black 25%, transparent 25%, transparent 75%, black 75%, black), -webkit-linear-gradient(45deg, black 25%, transparent 25%, transparent 75%, black 75%, black); background-image: -moz-linear-gradient(45deg, black 25%, transparent 25%, transparent 75%, black 75%, black), -moz-linear-gradient(45deg, black 25%, transparent 25%, transparent 75%, black 75%, black); background-image: linear-gradient(45deg, black 25%, transparent 25%, transparent 75%, black 75%, black), linear-gradient(45deg, black 25%, transparent 25%, transparent 75%, black 75%, black); background-size:60px 60px; background-position:0 0, 30px 30px }
El código, nuevamente es bastante sencillo, siendo básicamente una función que es llamada en cada iteración, calculando la posición y frame a dibujar:
// Nuestras variables var canvas, ctx, img, x, y, step, direction; canvas = document.getElementById('canvas'); ctx = canvas.getContext('2d'); img = document.getElementById('img'); // La posición x inicial, variara con cada paso x = 0; // La posición y inicial, quedará estática y = 25; // Numerador de paso, lo usaremos para saber que frame dibujar step = 0; // Direccion, 1 derecha, -1 izquierda direction = 1; setInterval(function() { // Borramos lo dibujado ctx.clearRect(0, 0, canvas.width, canvas.height); // Dibujamos el frame adecuado en la posicion correspondiente ctx.drawImage( // Imagen img, // Source x (step++ % 4) * 32, // Avance sobre el eje x // Source y [52, 0, 104][direction + 1], // Selecciona el frame adecuado // Source width 32, // Source height 52, // Dest x x, // Dest y y, // Dest width 32, // Dest height 52 ); // Avance, indistinto de la direccion x += 5 * direction; // Si toca un limite, cambiamos la dirección if (x >= canvas.width - 32 || x <= 0) { direction = -direction; } }, 1000 / 12); // Aproximadamente 12 frames por segundo
Este ejemplo se puede encontrar funcional en este fiddle.
En éste artículo conocimos acerca del soporte y otras alternativas a Canvas. Profundizamos un poco en el código con la manipulación de imágenes básicas y estáticas, la próxima semana veremos más acerca de la interactividad, framerate, consideraciones sobre performance, recomendaciones de lecturas y sitios para aprender más de Canvas.
Como siempre genial tu artículo amigo Aijoona, espero el de la semana que viene :{D
Saludos (:
felicitaciones @aijoona, muy buen artículo
pd: haber @maestros cuando se juegan con un highlight para el código
Gracias a ambos.
A decir verdad, no estaría nada mal el highlight del código
[…] Animaciones 2D utilizando el Canvas de HTML5 […]
Gracias por el articulo brother, ya comienzo a ver la luz con el “Canvas”.
Ahora bien, soy un novato usando el Js y mucho más el elemento canvas y me quedan un par de dudas sobre estas lineas (seguramente tontas pero seria fino si las detallas un pelin más o tal vez en el próximo tutorial) :
1) x = x >= 100 ? 0 : x + 1; <– Para que sirve el "?"
2) ctx.clearRect(0, 0, 100, 100); <– Por qué hay que limpiar el canvas al iniciar, es decir, viene lleno por defecto o es simplemente una buena practica?
P.D.: Lo del highlight estaria bien, de todas formas si se van al fiddle se lee/entiende mucho mejor. (gracias por ponerlo!).
El exp ? res1 : res2; es el gran amigo “operador ternario”, básicamente es un if-else inline.
Si tenés dudas al respecto, te invito a leer la documentación de Mozilla al respecto:
https://developer.mozilla.org/en/JavaScript/Reference/Operators/Conditional_Operator
Con respecto al clearRect, si te fijas bien, está dentro del bucle que definimos con el setInterval, con lo cual se llama en cada iteración. Lo cual no quita que la primer llamada, es decir cuando el canvas está limpio, no es necesaria.
Saludos!
Got it! Gracias.
Realmente interesante ! Muy completo !
Muy buen tutorial muchas gracias por la información solo que tengo una duda, he estado desarrollando mi propio jueguito y no entiendo esta parte del código
[52, 0, 104][direction + 1], // Selecciona el frame adecuado
que parámetros son los que estoy poniendo? muchas gracias