Reto HTML5: Jugando con el Canvas
Dentro del estándar HTML5 uno de los componentes más atractivos del elemento canvas.
Este elemento nos ofrece una superficie de dibujo a la cual podemos aplicar una serie de funciones de dibujado sencillas y muy potentes. (https://www.w3.org/TR/html5/the-canvas-element.html).
Para ilustrar este tema vamos a escribir un poco de software.
¿Qué necesito para empezar?
WebMatrix. La nueva herramienta de desarrollo de Microsoft que incluye todo lo necesario para el desarrollo de sitios web. Desarrollar sitios web con HTML5 nunca ha sido tan fácil. Descargalo ya. |
|
Internet Explorer 9. Si no tienes instalado Internet Explorer 9 deberías probarlos. Puedes descargarlo desde aquí . |
|
Visual Studio. Si ya tienes VS instalado perfecto, pero por si acaso, aquí tienes el enlace de descarga de la versión gratuita express o de la Ultimate Trial. |
|
Web Standards Update for Microsoft Visual Studio 2010 SP1. Con esta extensión de Visual Studio tendrás acceso funcionalidades de soporte de HTML5 en el entorno de desarrollo. Haz clic aquí para descargarla. |
Si necesitas ayuda o tienes algún problema con cualquiera de los pasos de instalación o del reto, puedes añadir un comentario a este post, twitearnos un mensaje a @esmsdn o simplemente twitear con el hashtag #retosmsdn.
Empecemos por el principio
Lo primero es construir una página Web HMTL5 y añadir un elemento canvas.
<!DOCTYPE html />
<html >
<head>
<meta charset="utf-8" />
<title>Jugando con el canvas</title>
</head>
<body>
<canvas id="DrawingArea">
Tu navegador no soporta el elemento canvas.
</canvas>
</body>
</html>
Para verificar el soporte de canvas del navegador antes de usarlo en JavaScript vamos a usar modernizr, en este caso modernizr-2.0.6.js que descargamos desde https://www.modernizr.com/
Modernizr hace muchas cosas pero para no añadir más componentes lo vamos a utilizar sólo para usar la detección de capacidades.
...
<head>
<meta charset="utf-8" />
<title>Jugando con el canvas</title>
<script src="modernizr-2.0.6.js"></script>
<script type="text/javascript">
window.addEventListener('load', WindowLoad, true);
function WindowLoad()
{
// Comprobamos si tenemos canvas....
if (!Modernizr.canvas) {
return;
}
// seguimos a lo nuesto
}
</script>
</head>
…
Si todo va bien vamos a recuperar el objeto detrás del elemento
// seguimos a lo nuesto
var areaDeDibujo; // objeto que representa el canvas
var contexto; // contexto de dibujado para el canvas.// obtenemos el canvas
areaDeDibujo = document.getElementById("AreaDeDibujo");// obtenemos un texto de dibujo en 2 dimensiones para el canvas
contexto = areaDeDibujo.getContext("2d");
Además obtenemos un contexto de dibujado sobre el que aplicar las primitivas de dibujo que queremos.
Usando las primitivas de dibujo
La lista de las primitivas de dibujo la podemos encontrar en https://msdn.microsoft.com/es-es/library/ff975057.aspx
Lo cierto es que es bastante sencillo utilizar esas primitivas:
En este caso vamos a pintar una imagen preexistente creada con un programa de dibujo como Expression Design
Una imagen de 4x4 píxeles a la que llamamos PuntoAmarillo.png y que agregamos al directorio de nuestra página web.
Hay varias formas en las que podemos cargar esta imagen para utilizarla. Bien definiendo una etiqueta img.
<img src="PuntoAmarillo.png" id="laImagen" />
Y buscando la imagen entre los controles del DOM
var imagen = document.getElementById("laImagen");
O Bien mediante la creación del objeto en JavaScript.
var imagen = new Image();
imagen.src = "PuntoAmarillo.png";
A partir de esta imagen, lo único que necesitamos para dibujarla es invocar drawImage sobre el contexto.
contexto.drawImage(imagen, x, y);
En este caso usamos la lista más corta de parámetros de drawImage.
En https://msdn.microsoft.com/es-es/library/ff975414.aspx encontraremos la lista completa de parámetros y un buen ejemplo de cómo utilizarlos para extraer un parte de una imagen.
Como parte del reto, vamos a simular el comportamiento de objetos con una cierta propiedad, llamémosla masa, que se atraen unos a otros según una cierta ley de proporcionalidad entre las masas y de proporcionalidad inversa al cuadrado de las distancias… ¿os suena? https://es.wikipedia.org/wiki/Gravedad
Encapsulamos el comportamiento de estos objetos en una clase tal que:
function Estrella(masa, xPos, yPos, xVel, yVel) {
this.masa = masa;
this.xPos = xPos;
this.yPos = yPos;
this.xVel = xVel;
this.yVel = yVel;
this.Actualizar = function (xCampo, yCampo, gMasa) {
this.xPos += this.xVel;
this.yPos += this.yVel;
// El campo sin la masa de este objeto tiene como centro de masas
var nMasa = gMasa - masa;
var xC = (xCampo - (this.xPos * masa)) / nMasa;
var yC = (yCampo - (this.yPos * masa)) / nMasa;
// distancia entre el centro de masas y el objeto
var d = Math.sqrt(((xC - this.xPos) * (xC - this.xPos)) + ((yC - this.yPos) * (yC - this.yPos)));
// vector unitario desde el objeto al centro de masa
var u = { x: (xC - this.xPos) / d, y: (yC - this.yPos) / d };
// parte real de la acelareación...
var parteReal = G * (nMasa * this.masa) / d * d;
// aplicada sobre el vector de dirección modificando el vector velocidad
this.xVel += parteReal * u.x;
this.yVel += parteReal * u.y;
}
this.Dibujar = function (contexto, imagen) {
// renormalizamos las coordenadas
// el centro del canvas es (0,0)
// y las ordenasdas (eje y) crecen hacia arriba
var x = this.xPos + (contexto.canvas.width / 2);
var y = (contexto.canvas.height / 2) - this.yPos;
// pintamos la imagen que nos den... dónde nos toca
contexto.drawImage(imagen, x, y);
}
}
Solo nos queda pensar en el bucle de la animación…
function DibujarEscena() {
// Borramos la escena
contexto.fillStyle = '#000000';
contexto.fillRect(0, 0, areaDeDibujo.width, areaDeDibujo.height);
// Calculamos el campo
gMasa = 0;
xCampo = 0;
yCampo = 0;
for (i = 0; i < estrellas.length; i++) {
gMasa += estrellas[i].masa;
xCampo += estrellas[i].xPos * estrellas[i].masa;
yCampo += estrellas[i].yPos * estrellas[i].masa;
}
// actualizamos la posición de los objetos
for (i = 0; i < estrellas.length; i++) {
estrellas[i].Actualizar(xCampo, yCampo, gMasa);
}
// Dibujamos los objetos
for (i = 0; i < estrellas.length; i++) {
estrellas[i].Dibujar(contexto, imagen);
}
}
Para poner en marcha el dibujado podemos usar setInterval (https://msdn.microsoft.com/es-es/ms536749):
window.setInterval(DibujarEscena, 20);
Poniendo orden en todo el código nos queda:
<!DOCTYPE html />
<html>
<head>
<meta charset="utf-8" />
<title>Jugando con el canvas</title>
<script src="modernizr-2.0.6.js"></script>
<script src="Estrella.js" type="text/javascript"></script>
<script type="text/javascript">
window.addEventListener('load', WindowLoad, true);
var areaDeDibujo;
var contexto;
var imagen;
var estrellas;
var xCampo;
var yCampo;
var gMasa;
var G = 1;
function WindowLoad() {
areaDeDibujo = document.getElementById("AreaDeDibujo");
contexto = areaDeDibujo.getContext("2d");
imagen = new Image();
imagen.src = "PuntoAmarillo.png";
estrellas = [new Estrella(1, 0, 100, -1, 0),
new Estrella(1, 0, -100, 1, 0)]
EmpezarAnimacion();
}
function EmpezarAnimacion() {
if (!Modernizr.canvas) {
return;
}
window.setInterval(DibujarEscena, 20);
}
function DibujarEscena() {
// Borramos la escena
contexto.fillStyle = '#000000';
contexto.fillRect(0, 0, areaDeDibujo.width, areaDeDibujo.height);
// Calculamos el campo
gMasa = 0;
xCampo = 0;
yCampo = 0;
for (i = 0; i < estrellas.length; i++) {
gMasa += estrellas[i].masa;
xCampo += estrellas[i].xPos * estrellas[i].masa;
yCampo += estrellas[i].yPos * estrellas[i].masa;
}
// actualizamos la posición de los objetos
for (i = 0; i < estrellas.length; i++) {
estrellas[i].Actualizar(xCampo, yCampo, gMasa);
}
// Dibujamos los objetos
for (i = 0; i < estrellas.length; i++) {
estrellas[i].Dibujar(contexto, imagen);
}
}
</script>
</head>
<body>
<canvas id="AreaDeDibujo" height="640px" width="800px">
Tu navegador no soporta el elemento canvas.
</canvas>
</body>
</html>
Tu turno… ¡el Reto! – Extiende el uso de primitivas
En este caso te proponemos 3 retos:
1. Utiliza una primitiva para dibujar los objetos. En lugar de cargar una imagen, prueba diferentes primitivas para dibujar los objetos. Es más, parametriza el objeto para que permita que le pases una función que tome las coordenadas y dibuje lo que quiera.
2. Utiliza una secuencia de imágenes para animar los objetos que se pintan.
3. Haz que los objetos dejen un rastro para mostrar la ruta que han seguido.
Recuerda las descargas:
· Web Standards Update for Microsoft Visual Studio 2010 SP1
Si tenéis cualquier comentario que hacer en este post o en twitter ( @esmsdn y el hashtag #retosmsdn) estaremos encantados de con vuestro feedback.
Boris Armenta -@borisarm- Developer Evangelist