Animación de elementos en Canvas para HTML 5

Una vez conociendo los elementos y modos de dibujo dentro del canvas, lo siguiente que podemos hacer es darle vida con algo de movimiento. Para hacer esto, por cierto, no se requiere aprender algo nuevo en cuanto a características de la interfaz de CanvasRenderingContext2D, sino que vamos a utilizar un poco de fundamentos de programación y algo de javascript tradicional.

El motor para la animación

Desde la creación del cine y otros medios parecidos, se sabe que el secreto para la animación es simplemente mostrar al ojo humano una serie de imágenes que vayan cambiando ligeramente y a un ritmo constante para dar el efecto de que se mueve. Lo mismo ocurre en la animación programada. Lo que necesitamos usar es un comando que permita realizar una rutina de código cada cierto tiempo. En javascript se llama setInterval(). Esta función requiere dos parámetros. El primero es indicarle que función debe realizar, y el segundo es cada cuanto tiempo debe volver a llamar la función. Este parámetro se define en milisegundos.

Usando el archivo de inicio, tenemos una función llamada dibujar(), la cual se llama desde el evento onLoad del body. La función de dibujar crea un rectángulo rojo dentro del canvas. Lo primero que vamos a hacer llamar a una función previa a dibujar(), en la cual se colocará a setInterval() para llamar continuamente a dibujar. Realmente esta parte es dar un poco más de orden a nuestro código.

Dentro del html, cambiemos el código de la etiqueta body.

<body onLoad="animar()">

Ahora en javascript, definimos la función animar y colocamos el código de setInterval para llamar a dibujar() cada 1000 milisegundos, o sea un segundo.

function animar(){
 setInterval("dibujar()",1000); 
}

Hasta este momento, si revisamos en el navegador, parece que no hay cambios. El asunto es que le estamos pidiendo a la función dibujar() que haga exactamente lo mismo. El siguiente paso es pasar un valor dinámico.




Modificando la posición horizontal del rectángulo

Fuera de toda función, vamos a declarar una variable global para guardar un valor que luego será modificado. En este ejemplo lo que vamos a cambiar será la posición horizontal del rectángulo, de ahí que la llamaremos posicionX, y le pondremos un valor de inicio.

var posicionX = 0;

Lo siguiente es decir que el primer parámetro de fillRect(), o sea x, sea nuestra variable.

contexto.fillRect(posicionX,100,100,50);

Revisando estos cambios lo único que veremos es que después de un segundo el rectángulo se coloca pero pegado al borde del canvas. Esto es porque ahora le indicamos que se dibuje desde la c0ordenada 0, y este valor no ha sido cambiado. Para ello, debajo de la función de fillRect() pondremos.

posicionX += 10;

Ya hace algo interesante, aunque diferente a lo que se supone que debe realizar. El rectángulo parece crecer de tamaño, ser más ancho. ¿A qué se debe?

Dibujar y borrar

Recordemos que el canvas es solo un plano, y que los elementos que se colocan no están separados de este, simplemente se pintan dentro de él. De ahí que al volver a dibujar el rectángulo rojo en una nueva posición, el anterior sigue ahí. Lo que debemos hacer es borrar ese dibujo previo antes de poner el nuevo. Y para ello usaremos clearRect(). Bastaría con definir que borrara el área por donde va a animarse el rectángulo, pero en este caso le indicaremos que borre todo lo que exista en el canvas, por si más adelante deseamos cambiar la ruta. Muy importante, esta orden debe ser antes de dibujar. Si lo ponemos después, no se verá nada, ya que lo dibujado de inmediato se borraría.

//Antes de fillStyle
 contexto.clearRect(0,0,500,500);

Ahora si, el rectángulo se mueve como lo deseamos. Algo lento, pero se mueve. Si deseamos que vaya más rápido simplemente cambiamos el segundo parámetro de setInterval. En mi opinión, a 50 milésimas de segundo se ve bastante bien. En cuanto a si hacer que se repita esta función tantas veces por segundo puede demandar mucho proceso al navegador, no es un problema. Es la ventaja del canvas, de que no sean objetos por separados, sino un solo lienzo en el que se borra y dibuja. Si más adelante hiciéramos que en el canvas se dibujaran cientos o miles de dibujos cada centésimas o décima de segundo, sería más eficiente que usar javascript para mover decenas o miles de etiquetas div, por ejemplo, ya que son elementos más complejos. De todas formas, se puede hacer ajustes para optimizar este código.

Optimización del código. Manejo de variables y del intervalo

Debido a que ya teníamos algo de código y este quedó dentro de la función que se repite constantemente, cada vez que se entra a la función dibujar() se está creando una nueva variable local llamada contexto. Seguramente en este caso parece no afectar demasiado, pero lo que estamos haciendo es asignar constantemente nuevos elementos. Si realizamos esta práctica en aplicaciones más elaboradas es cuando empiezan los dolores de cabeza. Lo más recomendable es que se esté reutilizando la misma variable.

En el siguiente código, la variable contexto será declarada global y luego se le dará valor en la función de animar(), la cual corre una vez. Con esto, esa parte del código se ejecuta una sola vez.

var contexto;
var posicionX = 0;
function animar(){
 contexto = document.getElementById("elCanvas").getContext("2d");
 setInterval("dibujar()",50); 
}
function dibujar(){
 contexto.clearRect(0,0,500,500);
 contexto.fillStyle = "#ff0000";
 contexto.fillRect(posicionX,100,100,50);
 posicionX += 10;
}

Ahora supongamos que no queremos que el rectángulo se salga, sino que se detenga cuando su borde derecho toque el borde derecho del canvas. Conociendo que mide 100 pixeles de ancho y el canvas 500, lo que tenemos que poner es una condición para indicar que realice el movimiento siempre y cuando la variable sea menor a 400.

function dibujar(){
 contexto.clearRect(0,0,500,500);
 contexto.fillStyle = "#ff0000";
 contexto.fillRect(posicionX,100,100,50);
 if(posicionX < 400)
 {
 posicionX += 10;
 }
}

Parece que ya todo quedó resuelto. Sin embargo, la orden de setInterval() sigue viva, sigue invocando a dibujar() una y otra vez, de manera innecesaria. Para comprobarlo, debajo de la condición vamos a poner un llamado a la consola para demostrarlo.

function dibujar(){
 contexto.clearRect(0,0,500,500);
 contexto.fillStyle = "#ff0000";
 contexto.fillRect(posicionX,100,100,50);
 if(posicionX < 400)
 {
 posicionX += 10;
 }
 console.log("I'm alive!");
 }

Revisando en la consola del navegador podremos ver que el texto “I’m alive!” se sigue llamando aunque el rectángulo se haya quedado fijo. Estamos un poco como al principio, donde el dibujo se pone en el mismo punto.

La forma para detener a setInterval() es usando a su némesis, clearInterval(). El asunto con esta función es que necesita saber a quién detener, y hasta este momento el intervalo esta actuando de manera anónima. La solución es vincularlo a una variable. La pondremos de manera global y la llamaremos intervalo. Ya teniendo a quién detener, dentro del bloque de else se coloca clearInterval()




El código final quedaría de la siguiente manera.

<!doctype html>
<html>
<head>
<meta charset="ISO-8859-1">
<title>Canvas Base</title>
<style>
#elCanvas{
 background-color:#FC3;
 border:1px solid black; 
}
</style>
<script>
var contexto;
var posicionX = 0;
var intervalo;
function animar(){
contexto = document.getElementById("elCanvas").getContext("2d");
intervalo = setInterval("dibujar()",50); 
}
function dibujar(){
contexto.clearRect(0,0,500,500);
contexto.fillStyle = "#ff0000";
contexto.fillRect(posicionX,100,100,50);
if(posicionX < 400)
{
 posicionX += 10;
}
else
{
 clearInterval(intervalo); 
}
 console.log("I'm alive!");
}
</script>
</head>

<body onLoad="animar()">
<canvas id="elCanvas" width="500" height="500">
Tu navegador no entiende HTML 5
</canvas>
</body>
</html>

Si revisamos la consola, notaremos que el texto de “I’m alive!” deja de llamarse una vez que el rectángulo ha llegado al otro lado. El intervalo ha muerto.

¡Extras! Animación en loop y rebote

Ya conociendo esta parte, lo que sigue es divertirse. Vamos a mostrar un par de ejemplos en los que cambiando partes del código realizará otros efectos.

El primero y más sencillo es hacer que el cuadro salga por la derecha y vuelva a entrar por la izquierda. Lo único que se tiene que hacer es que la condición revise cuando se llega a 500, el ancho del canvas. Una vez hecho esto, en un bloque de else se reinicia la variable de posicionX para que empiece en -100, para que todo el rectángulo empieza a dibujarse desde fuera del área visible.

function dibujar(){
 contexto.clearRect(0,0,500,500);
 contexto.fillStyle = "#ff0000";
 contexto.fillRect(posicionX,100,100,50);
 if(posicionX < 500)
 {
 posicionX += 10;
 }
 else
 {
 posicionX = -100;
 }
 }

Otro ejemplo divertido es hacer que el cuadro cambie de dirección al llegar a alguno de los bordes. En este caso se deben hacer un par de cosas:

  1. Mantener el valor de cambio que tendrá la posición en X, ya que en algunos momentos tendrá un valor positivo y en otros negativo. Sin embargo, el valor será el mismo. Lo llamaremos velocidad y tendrá el mismo valor de cambio que hemos usado, o sea 10.
  2. Indicar dentro de la condición que el cambio debe ocurrir en dos momentos, cuando posicionX llegue a 400 (para que no se salga), y también cuando llegue a 0. En esas situaciones debe cambiar su valor al opuesto. Esto se hará multiplicando el valor de velocidad por -1, de manera que pase de negativo a positivo una y otra vez.

Este código queda de la siguiente manera.

<script>
var contexto;
var posicionX = 0;
var intervalo;
var velocidad = 10;
function animar(){
 contexto = document.getElementById("elCanvas").getContext("2d");
 intervalo = setInterval("dibujar()",50); 
}
function dibujar(){
 contexto.clearRect(0,0,500,500);
 contexto.fillStyle = "#ff0000";
 contexto.fillRect(posicionX,100,100,50);
 if(posicionX > 400 || posicionX < 0)
 {
 velocidad *= -1;
 }
 posicionX += velocidad;
}
</script>

Conclusión

Utilizando la acción de borrar y dibujar constantemente en el canvas es una forma de hacer que cambie su aspecto. A través de setInterval() se puede realizar una función constantemente, aunque no es la única. Los cambios pueden generarse a partir de diversas situaciones, como la posición del mouse, un click del usuario, al apretar una tecla. Y si estamos viendo la animación en un dispositivo móvil, podría ser a partir de más opciones, como el cambio de posición del giroscopio, o al modificarse la geolocalización.

Tarea

Realizar que en el ejemplo de rebote, el cuadro no solo se mueva por el eje X, que también lo haga en Y, para que se mueva por todo el canvas. Esto nos va a servir más adelante para realizar un juego.

Descarga el material de trabajo.

Tm

También puedes ver los video de este tutorial en Yotube:

Animación básica

Variantes de la animación

Be the first to comment

Leave a Reply

Your email address will not be published.


*