Canvas es una maravilla que permite hacer cosas muy chulas con JavaScript. Sin embargo, tiene un problema de momento y es que aún no han terminado de implementar un sistema cómodo para manejar las figuras una vez dibujadas, ya sea mediante la asignación de ids, hit areas, capas o cualquier otra vaina similar. Esto obliga, al menos por lo que tengo entendido, a ir redibujando el lienzo cuando es interactivo, como sucede en los juegos, lo cual termina siendo un incordio que obliga a desarrollar códigos complejos para cualquier nimiedad.
Como me sugirieron Jorge y Borja, una solución es recurrir a librerías que permitan trabajar con canvas orientado a objetos, así que me puse a investigar un poco sobre el tema. Después de ver un par de ellas, me decanté por una desarrollada por Eric Drowell, que es sencillamente perfecta. Se llama KineticJS y es a canvas lo que jQuery a JavaScript.
Permite trabajar con canvas orientado a objetos, tiene varias capas, diferencia entre eventos de ratón y pantallas táctiles, las animaciones se preparan con mucha facilidad, permite arrastrar, escalar, rotar y transformar los objetos una vez creados mediante hit areas, seleccionarlos por id, por name, prevención de eventos... y un largo etcétera. En fin, una verdadera maravilla, así que voy a dedicarle un par de entradas pues vale la pena conocerla a fondo.
Nodos, layers y stages
A la que se conozca un poco cómo funcionan los programas de diseño gráfico resultará fácil cómo funciona la estructura de Kinetik: un objeto canvas incluye un stage (capa), que tiene uno o más layers (canales) y, a su vez, en cada canal puede haber una o más shapes (figuras), que pueden estar o no agrupadas. Vamos, la estructura por capas del Photoshop o el Ilustrator para aclararnos.
Por analogía con los documentos XML y la estructura DOM, Cada uno de estos «receptáculos» se denominan nodos: el nodo principal es el canvas y luego se va recorriendo toda la estructura. Esto queda claro si lo pensamos como si fuera un esquema:
1. Stage
1.1. Layer 1
1.1.1. Figura 1
1.1.2. Grupo 1
1.1.2.1. Figura 2
1.2.2.2. Figura 3
1.2. Layer 2
1.2.1. Figura 4
... etcétera.
Proceso básico
Lo primero, claro está, es bajarse la librería, que se debe cargar en el documento HTML como cualquier otro script enlazado y a continuación el archivo donde vamos a preparar nuestro código:
<!DOCTYPE html>
<head>
<meta charset="utf-8">
<title>Kinetic JS</title>
<script type="text/javascript" src="js/kinetic-v4.4.0.min.js"></script>
<script type="text/javascript" src="js/nuestro-archivo.js"></script>
</head>
<body>
</body>
</html>
Ya en el archivo en el que vamos a incluir nuestros scripts, conviene enmarcarlos en una sentencia domReady para que se ejecuten una vez que se ha terminado de cargar el DOM (equivale al $(document).ready() de jQuery, que podríamos usar en su lugar si estamos trabajando también con esta librería).
Es decir, o esto...
$(document).ready(function() {
// el código aquí...
});
...o esto otro:
window.onload = function() {
// el código aquí...
}
En Kinetik, el canvas no se declara en el documento HTML, sino en el código JavaScript. En su lugar, se pone un div que servirá de contenedor.
<body>
<div id="lienzo"></div>
</body>
El id de ese div contenedor es que se recoge, ya sí en nuestro archivo js, para declarar el stage, que como todo en esta librería es un objeto.
var miStage = new Kinetic.Stage({
container: 'lienzo',
width: 800,
height: 600
});
Luego podemos declarar nuestro primer objeto layer mediante el método Layer() del objeto Kinetic:
var miLayer = new Kinetic.Layer();
Lo incorporamos al stage que hemos creado mediante el método add():
miStage.add(miLayer);
Y ya sobre ese layer podemos ir añadiendo todos los objetos que queramos, como un estupendo rectángulo colorao. Primero lo creamos:
var miObjeto = new Kinetic.Rect({
x: 50,
y: 55,
width: 200,
height: 150,
fill: '#900',
stroke: '000',
strokeWidth: 2
});
Lo añadimos al layer que queramos.
miLayer.add(miObjeto);
Y, por último, le indicamos que se redibuje mediante el método draw() para que muestre la figura que acabamos de incorporar:
miLayer.draw();
Sencillo, no?
Bueno, de momento vamos a dejarlo aquí.