Ahora toca hablar de la clase Shape. Como habéis visto, la clase Graphics es bastante independiente del EaselJS, gracias al método draw() sólo necesita del contexto de un Canvas, elemento nativo de HTML5.
Shape en cambio, vivirá dentro de un Stage. Es una clase de EaselJS que posee en su interior, a modo de atributo, a la clase Graphics, es decir, goza de todas las capacidades de la clase Graphics y añade otras más muy interesantes. Los objetos Shape están pensados para ser usados con el Stage de CreateJS. Para crear un Shape es tan simple como:
var shape = new createjs.Shape();Con esto tendremos un Shape sin gráficos, podemos acceder a su atributo graphics para crearlo:
shape.graphics.beginFill("#f00").drawRect(100, 100, 50, 20);
Sin embargo, esto no dibujará nada en el canvas. Para ello, deberemos crear un Stage, añadir el Shape al Stage, y ordenar al Stage que renderize su contenido.
De modo que modificaremos el script para tener algo así:
var canvas = document.getElementsById('canvas'); //canvas ID
var stage = new createjs.Stage(canvas);
var shape = new createjs.Shape();
shape.graphics.beginFill("#f00").drawRect(0, 0, 50, 20); //rectángulo de 50x20
stage.addChild(shape);
stage.update();
Este código nos dará un resultado como este:
Por ahora, un resultado sencillo que podríamos conseguir con la clase Graphics (y sin usar Stage ni update). Sin embargo, con Shape podemos utilizar nuevos métodos que nos darán nuevas capacidades de manipulación gráfica.
Por ejemplo, podemos controlar su transparencia con:
shape.alpha = .5 //Valores entre 0 y 1
También podemos registrar este Shape a distintos Eventos, que se dispararán cuando se haga Click, se pase el Cursor por encima, se haga Doble Click, etc.
Por ejemplo, podemos detectar cuando se clicka en el Shape de la siguiente manera:
shape.on('click', function(){ //shape 'escuchará' el evento 'click'
alert('Click!');
});
También podemos realizar procesos de Transformación, alterando su Escala, Rotación, Posición e Inclinación. Debemos tener en cuenta que estas transformaciones precisan de un Punto de Referencia.
El punto de referencia se guarda en las variables regX y regY, que por defecto es el (0,0). Es decir, las rotaciones, escalado, posicionamiento, etc, se harán a partir de ese punto (0,0).
De manera que, si queremos que un cuadrado consiga rotar desde su centro, deberemos preocuparnos por establecer los valores correctos de su regX y su regY.
Haremos un cuadrado de 50x50 píxeles:
shape.graphics.beginFill("#3f5").drawRect(0, 0, 50, 50);
Modificaremos su punto de referencia para dejarlo en su centro:
shape.regX = 25; shape.regY = 25;
Ahora el cuadrado se 'dibuja' a partir de su centro, esto quiere decir que su 'Centro' estará en la esquina superior izquierda del canvas, no permitiéndonos ver este Shape completamente. De modo que lo moveremos con:
shape.x = canvas.width / 2; shape.y = canvas.height / 2;
Con las líneas de arriba estamos indicando la 'Posición' en el canvas que tendrá este Shape. En este caso, su posición (x,y) será el centro del canvas.
Y finalmente indicamos una rotación:
shape.rotation = 45;
Sin rotar
Rotado 45 grados
canvas = document.getElementById('canvas3');
stage = new createjs.Stage(canvas);
shape = new createjs.Shape();
//Cuadrado de 50x50
shape.graphics.beginFill("#3f5").drawRect(0, 0, 50, 50);
//Punto de Registro
shape.regX = 25;
shape.regY = 25;
//Posicionamiento
shape.x = canvas.width / 2;
shape.y = canvas.height / 2;
//Agregarlo al Escenario y renderizarlo
stage.addChild(shape);
stage.update();
Para finalizar, vamos a divertirnos un poco haciendo Transformaciones que evolucionen con el paso del tiempo, de este modo podremos conseguir una animación.
Haremos que el cuadrado Rote y se mueva de derecha a izquierda cada ciertos milisegundos. Para conseguir ejecución a intervalos, usaremos la función de JavaScript setInterval(callback, time).
Primero crearemos un simple cuadradito. Centrado verticalmente.
canvas = document.getElementById('canvas');
shape = new createjs.Shape();
shape.graphics.beginFill("#725").drawRect(0, 0, 50, 50);
shape.regX = 25;
shape.regY = 25;
shape.y = canvas.height / 2;
stage = new createjs.Stage(canvas);
stage.addChild(shape);
Para lograr una animación bastaría con las siguientes líneas:
setInterval(function(){ shape.rotation++; //aumentar su rotación shape.x += 1; //aumentar su posición x stage.update(); },50); // repetir cada 50 milisegundos
Sin embargo, esto hará que el cuadradito avance, avance y avance hasta perderse más allá del ancho del canvas. Por esta razón crearemos una pequeña lógica para mantener a nuestro cuadrado en pantalla.
Para que sea más interesante, añadiremos un par de atributos extra al Shape.
shape.velocity = 5; //lo rápido que se moverá shape.direction = 1; //1 ó -1, para hacer que vaya hacia adelante o hacia atrás
Y añadimos una pequeña lógica, para 'invertir' la dirección en la que avanza y rota el cuadradito cuando llegue más allá del ancho del canvas o cuando vaya más allá del comienzo del canvas.
//rotará y se moverá según su dirección y velocidad shape.rotation += shape.velocity * shape.direction; shape.x += shape.velocity * shape.direction; if(shape.x > canvas.width || shape.x < 0){ // Si excede algún límite shape.direction *= -1; // invertir dirección }
Y voila! Ahí lo tenemos animado. Más fácil imposible.
canvas = document.getElementById('canvas_5');
shape = new createjs.Shape();
shape.graphics.beginFill("#725").drawRect(0, 0, 50, 50);
shape.regX = 25;
shape.regY = 25;
shape.y = canvas.height / 2;
shape.velocity = 5;
shape.direction = 1;
stage = new createjs.Stage(canvas);
stage.addChild(shape);
setInterval(function(){
shape.rotation += shape.velocity * shape.direction;
shape.x += shape.velocity * shape.direction;
if(shape.x > canvas.width || shape.x < 0){
shape.direction *= -1;
}
stage.update();
},50);
Podemos notar que la animación no es demasiado fluida y esto tiene que ver con los FPS. La función setInterval() no está pensada para esto.
Por esta razón CreateJS cuenta con su propio reloj, la clase Ticker, que hará de sincronizador global ofreciendo unos intervalos más suavizados, pero esto lo veremos en el próximo capítulo ;)