Documentos de Académico
Documentos de Profesional
Documentos de Cultura
HTML5 Canvas Referencia y Ejemplos - Juan Carlos Reyes Caraballo
HTML5 Canvas Referencia y Ejemplos - Juan Carlos Reyes Caraballo
ejemplos
Juan Carlos Reyes C.
Sissi, si se pudo!
Juan Carlos Reyes C.
En este libro podrás consultar y revisar todos los aspectos técnicos del
canvas, tales como sus clases, sus métodos y sus propiedades, al igual
que examinar ejemplos prácticos en cada uno de los apartados
técnicos del canvas, con el objetivo de que puedas entender facil y
claramente para que sirven y como trabajan. No pretendemos ser una
obra para el aprendizaje de esta tecnología ni tampoco ha sido
concebida para enseñar aspectos como por ejemplo el artístico, esto es
solo una obra de consulta, que se puede usar para aprender aspectos
técnicos y teoricos, básicos o complejos, pero estamos seguros de que
puede ser, como en efecto lo es, un gran recurso de apoyo a todos los
diseñadores y desarrolladores web que hayan trabajado, trabajen o
se esten iniciando con el canvas de HTML5.
Este libro esta organizado en tres secciones, cada uno de los cuales
contiene lo siguiente:
Sección 1 - Lo básico
En esta sección comenzaremos por deinir lo que es el canvas, de
donde proviene, lo que hoy en día signiica y lo que podemos hacer
con el, luego pasaremos a deinir algunos conceptos básicos por lo que
comenzaremos deiniendo que es HTML5, su inicio, su importancia
dentro del contexto canvas y sus nuevas e impresionantes
características, hablaremos tambien de javascript y la importante labor
en el canvas, te mostraremos los aspectos básicos que hay que saber
para poder iniciar el trabajo con canvas y haremos un breve repaso
por sus principales características.
Sección 2 - La referencia
En la sección 2 vamos directo a la acción y explicaremos cada uno de
los métodos y propiedades del elemento canvas dentro del contexto
2d, en la cual estará enfocado este libro, pero también daremos un
repaso por los objetos fuera del contexto 2d y que son usados por
este, además de otros conceptos útiles de la API. deiniremos cada uno
de ellos, tratando de dar una explicación breve y fácil de entender, y
en su mayoría seguidos de un ejemplo que ira avanzando en
complejidad a medida que vayamos avanzando en el estudio de cada
uno de los complementos del canvas.
Sección 3 - Los proyectos
Una vez que hayamos estudiado todos y cada uno de los
complementos del canvas, pasaremos directamente a la acción y
mostraremos algunos proyectos profesionales que pueden ser útiles
tanto para aprender como para aplicar a tus propios proyectos e ir
construyendo tus propios patrones. En esta sección trataremos de
explicar brevemente cada uno de los conjunto de algoritmos usados
para cada una de las funciones que hagamos.
Para desarrollar los ejemplos de este libro puedes optar por introducir
manualmente el código o utilizar los archivos que acompañan al libro.
En el sitio web de juassi studios, dirígete a la sección libros y allí
encontrarás una sección con los códigos fuentes de cada libro
publicado, selecciona el tuyo y click en descargar zip. Los ejemplos
están organizados con el nombre de cada una de las propiedades y
métodos listados en este libro, ademas de una sección con
aplicaciones varias mostrada para ver la amplitud de posibilidades que
ofrece el canvas de html5, todos estos ejemplos pueden ser ejecutados
en un servidor web local, tal como apache o con tu navegador web
preferido, ya que no son necesarios en la mayoría de los ejemplos usar
un servidor.
Navegadores web
Navegadores mobiles
HTML5
Una pregunta muy común en estos tiempos es: “ ¿Cómo puedo empezar
a utilizar HTML5 si existen navegadores antiguos que no lo soportan?”
Pero la pregunta en sí se ha formulado de forma errónea. El HTML5 no
es una cosa grande como un todo, sino una colección de elementos
individuales, por consiguiente lo que sí se podrá será detectar si los
navegadores soportan cada elemento por separado.
Javascript
Crear Objetos
Un nuevo objeto se crea a través del operador new asociado con el
constructor Object. Establecer un objeto es tan fácil como lo
hacemos a continuación:
var nuevoValor = new Object();
Podría ser incluso más sencillo ( más adelante lo demostraremos),
aunque por ahora es suiciente.
Propiedades de objetos
Como los homólogos del lado del servidor, los objetos javascript
pueden contener datos y poseen métodos (en realidad, una especie
de métodos pero estaríamos adelantandonos). A diferencia de ellos,
estos elementos no son declarados previamente para un objeto; los
creamos de una forma dinámica según los necesitemos.
object [propertyNameExpression]
coche.marca
coche[‘marca’]
coche[‘m’ + ‘a’ + ‘r’ + ‘c’ + ‘a’]
var coche = {
marca : ‘Ford’,
modelo : ‘Mondeo’,
anio : 2013,
fechaCompra : new Date(2013,01,08),
propietario: {
}
};
Utilizando un literal del objeto, este fragmento crea el mismo objeto
coche que construimos anteriormente con la sentencia de asignación
de la sección precedente.
Nota destacada
Técnicamente, en JSON no se pueden expresar valores de fechas, en
principio porque javascript carece de cualquier tipo de literal de fecha.
Cuando se utiliza en un script, normalmente se utiliza el constructor
Date, como se
Atento
Cuando se utiliza la palabra clave var a alto nivel, fuera del cuerpo de
cualquier función, se trata de una notación de programación para
hacer referencia a una propiedad del objeto javascript predeinido
window. Cualquier referencia que se deine a alto nivel,
implícitamente esta deinida en la instancia window.
Esto cubre todos los aspectos sobre nuestro repaso del Object de
javascript. A continuación, se enuncian las conclusiones más
importantes que debemos extraer de este estudio:
√ Asignadas a variables.
√ Asignadas como una propiedad de un objeto.
√ Pasadas como un parámetro.
√ Devueltas como el resultado de una función.
√ Creadas utilizando literales.
Debido a que las funciones son tratadas de la misma manera que otros
objetos de este lenguaje, decimos que son objetos de primera clase.
Pero podría pensar que son diferentes de otros tipos de objetos, como
String o Number, debido a que no sólo poseen un valor (en el
caso de la instancia Function, su cuerpo) sino también un nombre.
Véamos esto en detalle.
No hay nada raro en ella y la que asigna una función a una variable
de alto nivel ( la propiedad window) no es diferente; se utiliza el
literal de una función para crear una instancia de Function y, a
continuación, se establece para la variable hacerAlgunaCosa, de
la misma forma que nuestro literal 135 tipoNumber se utilizó para
asignar una instancia Number a la variable
unNumeroMaravilloso.
(){
alert(’haces alguna cosa’);
}
window
unNumeroMaravilloso hacerAlgunaCosa
Recuerde que una variable de alto nivel en una página HTML se crea
como una propiedad de la instancia window. Por lo tanto, las siguientes
sentencias son equivalentes:
setTimeout(function(){alert(‘hola a
todos!’);},5000);
en la cual expresamos el literal de la función directamente en la lista de
parámetros y no se genera ningún nombre innecesario.
Las que hemos creado en los ejemplos que hemos visto hasta ahora
son funciones de alto nivel (que conocemos como propiedades
window de alto nivel) o bien se asignan a parámetros en una llamada
a la función. Tambien podemos establecer instancias Function a las
propiedades de objetos y aquí es donde las cosas se ponen realmente
interesantes.
La variable this
marca : ‘Ford’,
modelo : ‘Mondeo’,
anio : 2013,
fechaCompra : new Date(2013,01,08),
propietario: {
Cadena Ford
Cadena Mondeo Número 2013
Fecha
08-01-2013
Cadena Juan Reyes
Objeto
recoge las propiedades del objeto a través del cual fue invocada por
medio de this.
Lo mismo ocurre para las funciones de alto nivel. Recuerde que son
propiedades de window, por lo que su contexto es el propio objeto
window.
<head>
<title>funcion Contexto</title> <script>
o1.identifyMe= quienSoy;
alert(quienSoy()); alert(o1.identifyMe());
alert(quienSoy.call(o2));
alert(quienSoy.apply(o3));
</script>
</head>
<body>
</body>
</html>
En este ejemplo se deinen tres objetos simples, cada uno de ellos con
una propiedad handle que facilita la identiicación de cada uno dada
una referencia (líneas 5 a 7 del código). También añadimos una
propiedad handle a la instancia window, de forma que es fácilmente
localizable.
A continuación, establecemos una función de alto nivel que devuelve el
valor de la propiedad handle para el objeto que se esté utilizando
como contexto (líneas 9 y 10 del código) y asignamos la misma
instancia de la función a una propiedad del objeto o1 denominada
identifyMe, aunque es importante destacar que la función se declara
independientemente del objeto.
Para terminar, enviamos cuatro alertas, cada una de las cuales utiliza
un mecanismo diferente para invocar la misma instancia de la función.
Cuando se abre en una página, la secuencia de las cuatro alertas será
la mostrada en la imagen 1.1.
Closures
<head>
<title>Closure Example</title>
<script type=”text/javascript”
src=”javascript/jquery-1.4.js”></script>
<script>
$(function(){
var local = 1;
window.setInterval(function(){
$(‘#display’)
.append(‘<div>El dia ‘+ new Date()+’
local=’+local+’</div>’);
local++;
},3000);
});
</script>
</head>
<body>
<div id=”display”></div>
</body>
</html>
Supongamos que, debido a que el callback se va a iniciar cuando
hayan pasado tres segundos desde el momento en que se abra la
página (mucho después de que el manejador ready haya terminado
de ejecutarse), el valor de local no esta deinido durante la activación
de la función callback. Después de todo, el bloque en el que se
deine local se encuentra fuera del ámbito cuando el manejador
ready ha inalizado.
Nota
Quizás se haya dado cuenta que el closure se ha creado
implícitamente sin necesidad de una sintaxis explícita requerida en
otros lenguajes diferentes a javascript. Esto es un arma de doble filo, ya
que, por una parte, facilita
Atento
this id = ‘algunID’;
var propietario = this;
$(‘*’).each(function(){
alert(propietario.id); });
La variable local propietario, a la que se le asigna una referencia al
contexto de la función externa, forma parte del closure y se puede
acceder a la misma en la función callback. Este cambio en el código
hará que se muestre una alerta donde se visualizará la cadena
‘algun D’, tantas veces como elementos haya en el conjunto envuelto.
La referencia
El elemento <canvas>.
Height
Descripción
Tipo
Valores permitidos
Valores por defecto
</head>
<body>
<canvas id=’canvas’ width=’600’ height=’300’>
</body>
</html>
atributos
valor
getContext(contextId)
Descripción
Devuelve el contexto gráico asociado con el elemento canvas. Cada
elemento
setContext(context)
Descripción
Establece el contexto de renderizado del canvas para el objeto dado.
Lanza una excepción InvalidStateError si se han utilizado los
métodos getContext() o transferControlToProxy().
supportsContext(contextId)
toDataURL(type, quality)
Descripción
Devuelve una cadena con los datos URL que tu hayas asignado a la
propiedad
src de un elemento image. Este método acepta dos argumentos, el
primer argumento especiica el tipo MIME de la imagen, tal como
image/jpeg o image/png, este último se establece por defecto si
tu no especiicas el primer argumento. El segundo argumento, debe ser
un valor double, que va desde 0 hasta 1.0, especiica el nivel de
calidad para las imágenes jpeg. Este método devuelve los datos a una
resolución de 96ppp.
toDataURLHD(type, quality)
Descripción Devuelve una cadena con los datos URL que tu hayas
asignado a la propiedad src de un elemento image. Este método
acepta dos argumentos, el primer argumento especiica el tipo M ME de
la imagen, tal como image/jpeg o image/png, este último se
establece por defecto si tu no especiicas el primer argumento. El
segundo argumento, debe ser un valor double, que va desde 0
hasta 1.0, especiica el nivel de calidad para las imágenes jpeg. Este
método devuelve los datos a la resolución nativa del canvas.
toBlob(callback, type, args…)
Descripción
crea un blob que representa un archivo que contiene la imagen del
elemen
Descripción
Devuelve un objeto CanvasProxy que se puede utilizar para
transferir el
commit()
Descripción
Si el contexto de representación esta enlazado a un canvas, este
método muestra el fotograma actual. Este método es usado para
contextos que no están directamente ijos a un canvas especiico.
Juntamente con las propiedades anteriores, existen dos métodos que
preservan el estado del canvas, estos son:
save()
Descripción
Coloca una copia del estado actual de los gráicos dentro de la pila de
los estados
restore()
Descripción
Este método recupera la pila de los estados gráicos salvados y
restaura el valor de las propiedades del contexto, la región clipping y
la matriz de transformación.
Ejemplo de aplicación de algunos métodos de esta sección;
var canvas =
document.getElementsByTagName(‘canvas’)[0];
var proxy = canvas.transferControlToProxy());
var worker = new Worker(‘reloj2.js’);
worker.postMessage(proxy, [proxy]);
</script>
nombre del archivo - sreloj2.js
onmessage = function (event) {
var context = new CanvasRenderingContext2D();
event.data.setContext(context);
// event.data is the CanvasProxy object
setInterval(function () {
context.clearRect(0, 0, context.width,
context.height);
context.commit(); }, 1000);
};
La matriz de transformación
scale(float x, float y)
referenca referenca
Original Escala en el eje X Escala en el eje X con valores negativos imagen 2.1 -
método scale().
Ejemplo de aplicación del método scale();
nombre del archivo - scale.html
<!DOCTYPE html>
<html>
width:600px;
margin: 0px auto;
padding-top:50px;
#canvas {
background: #eaeaea;
}
</style>
</head>
<body>
<div id=”contenedor”>
<canvas id=’canvas’ width=’600’ height=’400’>
Tu navegador no soporta canvas de HTML 5
</canvas>
<script src=’scale.js’></script>
</div>
</body>
</html>
//Funciones..................................
function dibujarCuadricula(context, color,
stepx, stepy) { context.strokeStyle = color;
context.lineWidth = 0.5;
for (var i = stepx + 0.5; i <
context.canvas.width; i += stepx) {
context.beginPath();
context.moveTo(i, 0);
context.lineTo(i, context.canvas.height);
context.stroke();
}
for (var i = stepy + 0.5; i <
context.canvas.height; i += stepy) {
context.beginPath();
context.moveTo(0, i);
context.lineTo(context.canvas.width, i);
context.stroke(); }
}
<head>
<title>Ejemplo del método rotate()
</title> <style>
#contenedor{
width:600px;
margin: 0px auto;
padding-top:50px;
#canvas{
background:#eaeaea;
}
</style>
</head>
<body>
<div id=”contenedor”>
<canvas id=’canvas’ width=’600’ height=’400’>
Tu navegador no soporta canvas de HTML 5
</canvas>
<script src=’rotate.js’></script>
</div>
</body>
</html>
//Funciones..................................
function dibujarCuadricula(context, color,
stepx, stepy) { context.strokeStyle = color;
context.lineWidth = 0.5;
for (var i = stepx + 0.5; i <
context.canvas.width; i += stepx) {
context.beginPath();
context.moveTo(i,0);
context.lineTo(i,context.canvas.height);
context.stroke();
transformación. La traslación mueve el objeto desde el origen a la
localización especiicada por las coordenadas dx y dy. Ejemplo:
context translate(100, 100);
context translate()
referencia
punto de inicio punto final imagen 2.5 - método translate().
Ejemplo de aplicación del método translate();
<head>
<title>Ejemplo del método translate()
</title> <style>
#contenedor{
width:600px;
margin: 0px auto;
padding-top:50px;
#canvas {
background: #eaeaea;
}
</style>
</head>
<body>
<div id=”contenedor”>
<canvas id=’canvas’ width=’600’ height=’400’>
Tu navegador no soporta canvas de HTML 5
</canvas>
<script src=’translate.js’></script>
</div>
</body>
</html>
rectWidth,rectHeight);
//Funciones..................................
function dibujarCuadricula(context, color,
stepx, stepy) { context.strokeStyle = color;
context.lineWidth = 0.5;
for (var i = stepx + 0.5; i <
context.canvas.width; i += stepx) {
context.beginPath();
context.moveTo(i, 0);
context.lineTo(i, context.canvas.height);
context.stroke();
matriz de transformación del contexto. Es decir, que en cualquier
momento que dibujes dentro del canvas, sea esto una forma, texto o
imagen, el navegador aplica la matriz de transformación al objeto que
hayas dibujado o estés dibujando. Por defecto la matriz de
transformación es conocida como una matriz idéntica, lo que signiica
que no le hace nada al objeto que estés dibujando. Cuando llamas a
scale(), rotate() o translate() tu estas modiicando la
matriz de transformación en forma que ello afecte a todas las futuras
operaciones de dibujo.
La mayoría de las veces estos tres métodos serán suicientes cuando
tus intenciones sea manipular la matriz de transformaciones
directamente. Por ejemplo si deseas aplicar un efecto shear a objetos
que dibujastes y que no es posible hacerlo con cualesquiera de los
tres métodos principales de transformación , entonces, en ese caso,
necesitarás manipular la matriz de transformación tu mismo. El contexto
canvas provee dos métodos que manipulan directamente la matriz de
transformación: transform(), la cual aplica la transformación a la
actual matriz de transformación y setTransform(), la cual resetea
la matriz a su valor original – la matriz idéntica – y aplica la
transformación a esta mátriz idéntica. El inconveniente que podríamos
encontrarnos es que sucesivas llamadas a transform() son
acumulativas y sucesivas llamadas a setTransform() despejan la
matriz de transformación, limpiándolas en cada llamada.
Tu podrás escalar, rotar y trasladar objetos con estos dos métodos, lo
que para muchos profesionales representa dos grandes ventajas:
1. Tu puedes manejar y/o crear efectos tales como shear, que no serían
posible solo con el uso de los métodos scale(), rotate() y
translate().
2. Tu puedes combinar efectos, tales como escalar, rotar, trasladar y
shear en una llamada a transform() o setTransfrom().
El mayor inconveniente que podríamos encontrar al usar
transform() y setTransform() es que estos métodos no son
tan intuitivos y fáciles de entender como lo son scale(),
rotate() y translate().
Descripción
Aplica la transformación especiicada por los seis (6) argumentos. Las
trans
Nota Destacada!
Traslaciones, escalas y rotaciones pueden ser implementadas en
términos de esta propuesta general del método transform(). Para
traslación llamar a transform(1,0,0,1,dx, dy). Para una escala llamar a
transform(sx,0,0,sy,0,0). Para rotaciones en el sentido de las agujas del
reloj llamar a transform(cos(x), sin(x), -sin(x), cos(x),0,0). Para
Atento
var ct = Math.cos(alpha);
var st = Math.sin(alpha);
context.transform(ct, -st, st, ct,
-x*ct-y*st + x,
x*st –y*ct + y); }
width:600px;
margin: 0px auto;
padding-top:50px;
#canvas {
background: #eaeaea;
}
</style>
</head>
<body>
<divid=”contenedor”>
<canvas id=’canvas’ width=’600’ height=’400’>
Tu navegador no soporta canvas de HTML 5
</canvas>
<scriptsrc=’transform.js’></script>
</div>
</body>
</html>
//Funciones..................................
function dibujarCuadricula(context, color,
stepx, stepy) { context.strokeStyle = color;
context.lineWidth = 0.5;
for (var i = stepx + 0.5; i <
context.canvas.width; i += stepx) {
context.beginPath();
context.moveTo(i,0);
context.lineTo(i,context.canvas.height);
context.stroke();
}
for (var i = stepy + 0.5; i <
context.canvas.height; i += stepy) {
context.beginPath();
context.moveTo(0, i);
context.lineTo(context.canvas.width, i);
context.stroke();
}
}
identidad transformada, es decir, tu puedes trabajar con las
coordenadas raw del canvas, usando un código como este:
c.save();
c.setTransform(1,0,0,1,0,0);
/* usar coordenadas raw del canvas aquí */
c.restore();
width:600px;
margin: 0px auto;
padding-top:50px;
#canvas {
background: #eaeaea;
}
</style>
</head>
<body>
<div id=”contenedor”>
<canvas id=’canvas’ width=’600’ height=’400’>
Tu navegador no soporta canvas de HTML 5
</canvas>
<script src=’transformAvanzado.js’></script>
</div>
</body>
</html>
//
Funciones....................................
function dibujarTexto() {
context.strokeText(texto, 0, 0); }
// Manejadores de
eventos..................................
}
}, 1000/60);
//
Initialization...............................
context.font = tamFuente + ‘px Palatino’;
context.strokeStyle = ‘blue’;
context.shadowColor = ‘rgba(100, 100, 150,
0.9)’;
context.shadowBlur = 5;
context.textAlign = ‘center’;
context.textBaseline = ‘middle’;
origen.x = canvas.width/2; origen.y =
canvas.height/2;
context.transform(1, 0, 0, 1, origen.x,
origen.y);
dibujarTexto();
<head>
<title>Aplicación del método
setLineDash() </title>
<style>
body {
background: #eaeaea;
}
#contenedor{
width:500px;
margin:0px auto;
padding-top:50px;
}
#canvas {
}
</style> </head>
<script src=’javascript/setLineDash.js’>
</script> </body>
</html>
nombre del archivo
- setLineDash.js
contexto =
document.getElementById(“canvas”).getContext(
// Esto hace que las líneas aparecen claras y
nítidas, y
//se puede prescindir de esta
contexto.translate(0.5, 0.5);
// Dibujamos el circulo contexto.beginPath();
contexto.strokeStyle = ‘green’;
contexto.setLineDash([5]);
contexto.arc(65,65,50,0,2 * Math.PI,false);
contexto.stroke();
// Dibujamos el cuadrado
contexto.beginPath();
contexto.strokeStyle = ‘blue’;
contexto.setLineDash([5,2]);
contexto.rect(130,15,100,100);
contexto.stroke();
// Dibujamos el triangulo
contexto.beginPath();
contexto.strokeStyle = ‘black’;
contexto.setLineDash([1,2]);
contexto.moveTo(245,115);
contexto.lineTo(295,15);
contexto.lineTo(345,115);
contexto.closePath();
contexto.stroke();
Métodos de Path
Atento
Usar métodos tales como rect() o arc() es similar a dibujar con
tintas invisibles. Estos métodos crean un path invisible que tu puedes
luego hacer visible llamando a los métodos stroke() y .
beginPath()
closePath()
Descripción
Explícitamente cierra un path abierto. Este método es para abrir paths
de arcos y paths creados con líneas o curvas.
Descripción
Agrega los subpaths que vienen a representar un arco o un círculo al
actual
context.beginPath();
context.strokeStyle = “black”;
context.lineWidth = 2;
context.arc(200, 150, 60, (Math.PI/180)*0,
(Math.
PI/180)*360, false);
//full circle
context.stroke();
context.closePath();
}
function dibujarCuadricula(context, color,
stepx, stepy) { context.save();
context.strokeStyle = color;
context.lineWidth = 0.5;
canvas.height);
}
context.restore(); }
dibujarCuadricula(context, ‘lightgray’, 10,
10); drawScreen();
context.moveTo(x, y);
context.lineTo(x + w, y);
context.lineTo(x + w, y + h);
context.lineTo(x, y + h);
context.closePath();
<head>
<title>
Aplicación del método rect()
</title>
<style>
body {
background: #eaeaea; }
#contenedor{
width:800px;
margin:0px auto;
padding-top:50px;
}
#canvas {
margin-left: 20px;
margin-right: 0;
margin-bottom: 20px;
border: thin solid #aaaaaa;
cursor: crosshair;
}
</style>
</head>
<body>
<div id=”contenedor”>
<canvas id=’canvas’ width=’800’ height=’520’>
<script src=’javascript/rect.js’></script>
</body>
</html>
}else {
context.moveTo(x, y);
context.lineTo(x + w, y);
context.lineTo(x + w, y + h);
context.lineTo(x, y + h);
}
context.closePath();
}
El método fill()
Atento
odd rule). Los subpaths abiertos deben ser implícitamente cerrados
cuando ha sido rellenado (sin afectar los subpaths reales).
stroke(Path path)
Nota destacada!
Como resultado de como se define el algoritmo para trazar un path,
partes de los paths que se sobreponen en una sola operación stroke,
son tratados como si su unión fue lo dibujado.
Atento
El estilo del trazo (stroke) se ve afectada por lamatriz de transfromación
actual durante el proceso, incluso si la trayectoria deseada es el actual
path predeterminado.
}
#radios {
padding: 10px; }
</style>
</head>
<body>
</body>
</html>
// Dibujar
atributos....................................
context.font = ‘28pt Helvetica’;
context.strokeStyle = ‘green’;
//
Rectangulos..................................
//el ancho de las lineas establecidas en 4
para las formas context.lineWidth = ‘4’;
context.beginPath();
context.rect(65, 150, 100, 50);
context.stroke();
context.beginPath();
context.rect(250, 150, 100, 50);
context.beginPath();
context.rect(450, 150, 100, 50);
context.stroke();
context.beginPath();
context.arc(500, 280, 40, 0, Math.PI*3/2);
context.stroke();
context.beginPath();
Descripción Si estos no son subpaths en el path actual, este método
hace parcialmente lo mismo que moveTo(): crea un subpath en el punto
que has especiicado. Ahora bien, si estos son subpaths en el path
actual, este método agrega el punto que has especiicado a este
subpath.
<style>
#canvas {
background: #eaeaea;
}
#radios {
padding: 10px; }
</style>
</head>
<body>
//
Funciones....................................
function dibujarCuadricula(context, color,
stepx, stepy) { context.strokeStyle = color;
context.lineWidth = 0.5;
for (var i = stepx + 0.5; i <
context.canvas.width; i += stepx) {
context.beginPath();
context.moveTo(i, 0);
context.lineTo(i, context.canvas.height);
context.stroke();
}
for (var i = stepy + 0.5; i <
context.canvas.height; i += stepy) {
context.beginPath();
context.moveTo(0, i);
context.lineTo(context.canvas.width, i);
context.stroke();
}
}
//Iniciar
aplicación...................................
nombre del archivo - arcTo.html
<!DOCTYPE html>
<html>
width:600px;
margin: 0px auto;
padding-top:50px;
#canvas{
background:#eaeaea;
}
</style>
</head>
<body>
<div id=”contenedor”>
<canvas id=’canvas’ width=’600’ height=’400’>
Tu navegador no soporta canvas de HTML 5
</canvas>
<script src=’arcTo.js’></script>
</div>
</body>
</html>
context.beginPath();
context.moveTo(150, 100);
context.arcTo(500,100,500,200,10); // esquina
superior derecha
context.arcTo(500,200,100,200,30); // esquina
inferior derecha
context.arcTo(100,200,100,100,10); // esquina
inferior izquierda
context.arcTo(100,100,200,100,10); // esquina
superior izquierda context.closePath(); //
Regresar al punto de inicio
context.stroke(); // Dibuja el path
//Funcion
es...........................................
function dibujarCuadricula(context, color,
stepx, stepy) {
context.strokeStyle = color;
context.lineWidth = 0.5;
for (var i = stepx + 0.5; i <
context.canvas.width; i +=
stepx) {
context.beginPath();
context.moveTo(i,0);
context.lineTo(i,context.canvas.height);
context.stroke();
Descripción Crea un path para una curva Bézier cuadrática. Este
método acepta cuatro (4) argumentos. Los argumentos cpx y cpy
representan las coordenadas del punto de control y los argumentos x
e y representan el punto inal de la curva. Este método agrega un
segmento de curva Bézier cuadrática al subpath actual. La curva se
inicia en el punto actual y termina en las coordenadas x e y dada por
los argumentos. El punto de control (cpx, cpy) determina la forma de
la curva entre estos dos puntos.
Cuando este método inaliza devuelve el punto actual como las
coordenadas x e y.
#canvas {
position: absolute;
left: 0px;
margin-left: 20px;
margin-right: 20px;
border: thin solid rbga(0,0,0,1.0);
background:#eaeaea;
}
input {
margin-left: 15px;
}
</style> </head>
<body>
<canvas id=’canvas’ width=’400’ height=’400’>
Tu navegador no soporta canvas de HTML5
</canvas>
<script src=’quadraticBezierTo.js’></script>
</body>
</html>
{ x: canvas.width - MARGEN_FLECHA * 2, y:
canvas.height - MARGEN_FLECHA },
{ x: RADIO,
y: canvas.height/2 },
{ x: MARGEN_FLECHA,
y: canvas.height/2 - MARGEN_FLECHA },
{ x: canvas.width - MARGEN_FLECHA, y:
MARGEN_FLECHA },
{ x: canvas.width - MARGEN_FLECHA, y:
MARGEN_FLECHA*2 },
];
//
Funciones....................................
context.beginPath();
context.strokeStyle = strokeStyle;
context.lineWidth = 0.5;
context.arc(x, y, RADIO, 0, Math.PI*2,
false);
context.stroke(); }
function dibujarPuntosBezier() { var i,
strokeStyle,
for (i=0; i < puntos.length; ++i) {
-
Style);
}
}
function dibujarFlecha() {
context.strokeStyle = ‘lightgrey’;
context.moveTo(canvas.width - MARGEN_FLECHA,
MARGEN_FLECHA*2);
context.lineTo(canvas.width - MARGEN_FLECHA,
canvas.height - MARGEN_FLECHA*2);
context.quadraticCurveTo(puntos[0].x,
puntos[0].y, puntos[1].x, puntos[1].y);
context.lineTo(MARGEN_FLECHA,
canvas.height/2 + MARGEN_FLECHA);
context.quadraticCurveTo(puntos[2].x,
puntos[2].y, puntos[3].x, puntos[3].y);
context.lineTo(canvas.width -
MARGEN_FLECHA*2, MARGEN_FLECHA);
context.quadraticCurveTo(puntos[4].x,
puntos[4].y, puntos[5].x, puntos[5].y);
context.stroke(); }
stepx) {
context.beginPath();
context.moveTo(i, 0);
context.lineTo(i, context.canvas.height);
context.stroke();
}
for (var i = stepy + 0.5; i <
context.canvas.height; i += stepy) {
context.beginPath();
context.moveTo(0, i);
context.lineTo(context.canvas.width, i);
context.stroke();
}
}
// Iniciar
aplicación...................................
context.clearRect(0,0,canvas.width,canvas.heig
Descripción Agrega una curva bezier cubica al actual subpath. Este
método acepta seis (6) argumentos: cpX1 y cpY1 representan las
coordenadas del punto de control asociadas al punto inicial de las
curvas (la posición actual), cpX2 y cpY2 representan las
coordenadas del punto de control asociadas al punto inal de las
curvas y por último x e y representan las coordenadas del punto inal
de las curvas.
El punto de inicio de la curva, es el punto actual del canvas y el punto
inal está representados en las coordenadas x e y de los argumentos.
Los dos puntos de control Bézier (cpX1, cpY1) y (cpX2, cpY2)
deinen la forma de la curva.
<head>
<title>Dibujar curvas bezier</title>
<style>
body {
background: #eeeeee; font-size:14px;
}
#contenedor{
width:605px;
margin: 0px auto;
padding-top:50px;
}
.controlesFlotantes {
position: absolute;
left: 570px;
top: 170px;
width: 300px;
padding: 20px;
border: thin solid rgba(0, 0, 0, 0.3);
background-color:#eaeaea ;
color: blue;
font: 14px Arial;
-webkit-box-shadow: rgba(0, 0, 0, 0.2) 6px
6px 8px;
-moz-box-shadow: rgba(0, 0, 0, 0.2) 6px 6px
8px; box-shadow: rgba(0, 0, 0, 0.2) 6px 6px
8px; display: none;
}
.controlesFlotantes p { margin-top: 0px;
margin-bottom: 20px;
#controles {
position:relative; left: 25px;
top: 25px;
#canvas {
background: #eaeaea; cursor: pointer;
margin-left: 10px; margin-top: 10px;
border:1px solid #999;
}
</style>
</head>
<body>
<div id=”contenedor”>
<canvas id=’canvas’ width=’605’ height=’400’>
<select id=’selectEstiloLinea’>
<option value=’red’>rojo</option> <option
value=’green’>verde</option> <option
value=’blue’>azul</option> <option
value=’orange’>naranja</option>
</option> <option
value=’goldenrod’>dorado</option> <option
value=’navy’ selected>azul oscuro
</option>
<option value=’purple’>purpura</option>
</select>
Lineas guias:
<input id=’checkboxLineasGuias’
type=’checkbox’ checked/> <input
id=’botonBorrarTodo’ type=’button’
value=’Borrar todo’/> </div>
<div id=’instrucciones’
class=’controlesFlotantes’>
de control para cambiar la forma de la curva.
</p>
<input id=’botonOkInstrucciones’
type=’button’ value=’Okay’ autofocus/>
<input id=’botonNoMasInstrucciones’
type=’button’ value=’No mostrar mas las
instrucciones’/>
</div>
mostrarInstrucciones = true,
ESTILO_CUADRICULA = ‘lightgrey’,
ESPACIO_CUADRICULA = 10,
RADIO_PUNTOS_DE_CONTROL = 5,
ESTILO_LINEA_PUNTO_DE_CONTROL = ‘blue’,
ESTILO_RELLENO_PUNTO_DE_CONTROL = ‘red’,
ESTILO_LINEA_PUNTO_FINAL = ‘navy’,
ESTILO_RELLENO_PUNTO_FINAL = ‘blue’,
ESTILO_LINEAS_GUIAS = ‘rgba(0,0,230,0.4)’,
dibujarImageData,//Image data almacenada en
el evento mouse down
mousedown = {},//Localizacion del cursor para
el último evento //mouse down
bandas = {}, // Constante actualizada para el
evento mouse down
arrastrar = false, // Si es true, usuario
esta arrastrando el //cursor
//o de control
puntosDeControl=[{},{}],//localización del
punto de control //(x, y)
editar = false, // Si es true, usuario esta
editando la curva
lineasGuias = checkboxLineasGuias.checked;
//
Funciones....................................
function dibujarCuadricula(context, color,
stepx, stepy) { context.save()
context.strokeStyle = color;
context.lineWidth = 0.5;
context.clearRect(0, 0, context.canvas.width,
context.canvas.
height);
}
context.restore(); }
function windowToCanvas(x, y) {
var bbox = canvas.getBoundingClientRect();
return { x: x - bbox.left * (canvas.width /
bbox.width), y: y - bbox.top * (canvas.height
/ bbox.height) };
}
dibujarImageData = context.getImageData(0, 0,
canvas.width, canvas.height); }
context.putImageData(dibujarImageData, 0, 0);
}
//
bandas.......................................
function actualizarBandasRectangulares(loc) {
bandas.width = Math.abs(loc.x - mousedown.x);
bandas.height = Math.abs(loc.y -
mousedown.y);
function dibujarCurvasBezier() {
context.beginPath();
context.moveTo(puntosFinales[0].x,
puntosFinales[0].y);
context.bezierCurveTo(puntosDeControl[0].x,
puntosDeControl[0].y,
puntosDeControl[1].x, puntosDeControl[1].y,
puntos
Finales[1].x, puntosFinales[1].y);
context.stroke();
}
function actualizarPuntosFinControl() {
puntosFinales[0].x = bandas.left;
puntosFinales[0].y = bandas.top;
puntosFinales[1].x = bandas.left +
bandas.width; puntosFinales[1].y = bandas.top
+ bandas.height puntosDeControl[0].x =
bandas.left;
puntosDeControl[0].y = bandas.top +
bandas.height
puntosDeControl[1].x = bandas.left +
bandas.width; puntosDeControl[1].y =
bandas.top;
}
function dibujarBandas(loc) {
actualizarPuntosFinControl();
dibujarCurvasBezier();
function actualizarBandas(loc) {
actualizarBandasRectangulares(loc);
dibujarBandas(loc);
}
// Lineas
Guias........................................
function dibujarLineasGuias(x, y) {
context.save();
context.strokeStyle = ESTILO_LINEAS_GUIAS;
context.lineWidth = 0.5;
dibujarLineasGuiasVerticales(x);
dibujarLineasGuiasHorizontales(y);
context.restore();
}
function dibujarPuntoDeControl(index) {
context.beginPath();
context.arc(puntosDeControl[index].x,
puntosDeControl[index].y,
RADIO_PUNTOS_DE_CONTROL, 0, Math.PI*2,
false);
context.stroke();
}
function dibujarPuntosDeControl() {
context.save();
context.strokeStyle =
ESTILO_LINEA_PUNTO_DE_CONTROL;
dibujarPuntoDeControl(0);
dibujarPuntoDeControl(1);
context.stroke();
context.restore(); }
function dibujarPuntoFinal(index) {
context.beginPath();
context.arc(puntosFinales[index].x,
puntosFinales[index].y, RADIO_
function dibujarPuntosFinales() {
context.save();
context.strokeStyle =
ESTILO_LINEA_PUNTO_FINAL;
dibujarPuntoFinal(0); dibujarPuntoFinal(1);
context.stroke();
context.restore(); }
function dibujarPuntosControlYFinales() {
dibujarPuntosDeControl();
dibujarPuntosFinales();
}
function cursorEnPuntoFinal(loc) { var pt;
puntosFinales.forEach( function(point) {
context.beginPath();
context.arc(point.x, point.y,
RADIO_PUNTOS_DE_CONTROL, 0, Math. PI*2,
false);
if (context.isPointInPath(loc.x, loc.y)) { pt
= point;
}
});
return pt; }
function cursorEnPuntoControl(loc) { var pt;
puntosDeControl.forEach( function(point) {
context.beginPath();
context.arc(point.x, point.y,
RADIO_PUNTOS_DE_CONTROL, 0, Math.
PI*2, false);
if (context.isPointInPath(loc.x, loc.y)) { pt
= point;
}
});
return pt; }
function actualizarPuntoDeArrasytre(loc) {
puntoArrastre.x = loc.x;
puntoArrastre.y = loc.y;
}
// manejadores de eventos -
canvas..............................
canvas.onmousedown = function (e) {
e.preventDefault(); // previene el cambio del
cursor
if (!editar) {
mousedown.x = loc.x;
mousedown.y = loc.y;
actualizarBandasRectangulares(loc); arrastrar
= true;
}
else {
puntoArrastre = cursorEnPuntoControl(loc);
if (!puntoArrastre) {
puntoArrastre = cursorEnPuntoFinal(loc);
}
}
};
if(lineasGuias) {
dibujarLineasGuias(loc.x, loc.y);
}
}
if (arrastrar) {
actualizarBandas(loc);
dibujarPuntosControlYFinales();
}
else if (puntoArrastre) {
actualizarPuntoDeArrasytre(loc);
dibujarPuntosControlYFinales();
dibujarCurvasBezier();
}
};
if (!editar) {
actualizarBandas(loc);
dibujarPuntosControlYFinales();
arrastrar = false;
editar = true;
if (mostrarInstrucciones) {
instrucciones.style.display = ‘inline’; }
}
else {
if (puntoArrastre)
dibujarPuntosControlYFinales();
else
editar = false;
dibujarCurvasBezier();
} };
// Manejadores de eventos -
Control...............................
ESPACIO_CUADRICULA);
editar = false; arrastrar = false;
};
// Manejadores de eventos -
instrucciones......................
botonNoMasInstrucciones.onclick = function
(e) { instrucciones.style.display = ‘none’;
mostrarInstrucciones = false;
};
// Iniciar
aplicación...................................
context.strokeStyle =
selectEstiloLinea.value;
dibujarCuadricula(context, ESTILO_CUADRICULA,
ESPACIO_CUADRICULA, ESPACIO_CUADRICULA);
#contenedor{
width:600px;
margin: 0px auto; padding-top:50px;
#canvas {
background: #eaeaea;
}
</style>
</head>
<body>
<divid=”contenedor”>
<canvas id=’canvas’ width=’600’ height=’400’>
Tu navegador no soporta canvas de HTML 5
</canvas>
<script src=’clip.js’></script>
</div>
</body>
</html>
//
Funciones....................................
function dibujarCuadricula(context, color,
stepx, stepy) { context.strokeStyle = color;
context.lineWidth = 0.5;
for (var i = stepx + 0.5; i <
context.canvas.width; i += stepx) {
context.beginPath();
context.moveTo(i, 0);
context.lineTo(i, context.canvas.height);
context.stroke();
}
for (var i = stepy + 0.5; i <
context.canvas.height; i += stepy) {
context.beginPath();
context.moveTo(0, i);
context.lineTo(context.canvas.width, i);
context.stroke();
}
}
function dibujarTexto() { context.save();
context.font = ‘175px Arial’;
context.lineWidth = 2;
context.shadowColor = ‘rgba(100, 100, 150,
0.8)’;
context.shadowBlur = 10;
context.strokeStyle = ‘grey’;
context.strokeText(‘canvas’, 20, 250);
context.restore();
}
context.beginPath();
context.arc(canvas.width/2, canvas.height/2,
radio, 0, Math.PI*2, false); context.clip();
}
function rellenarCanvas(color) {
}
clearInterval(loop);
}, 1000);
}
function dibujarFotogramaAnimacion(radio) {
rellenarCanvas(‘#eaeaea’);
dibujarCuadricula(context, ‘lightgrey’, 10,
10); dibujarTexto();
}
function animar() {
var radio = canvas.width/2, loop;
loop = window.setInterval(function() { radio
-= canvas.width/100;
rellenarCanvas(‘#eaeaea’);
if (radio > 0) {
context.save();
dibujarFotogramaAnimacion(radio);
context.restore();
}
else {
}
}, 16); };
// Manejadores de
eventos......................................
// Iniciar
aplicación...................................
dibujarCuadricula(context, ‘lightgrey’, 10,
10); dibujarTexto();
#contenedor{
width:600px;
margin:0px auto;
padding-top:50px;
}
#canvas {
left: 0px;
margin-left: 20px;
margin-right: 20px;
border: thin solid rbga(0,0,0,1.0);
background:#eaeaea;
}
input {
margin-left: 15px;
}
</style> </head>
<body> <div id=”contenedor”>
<canvas id=’canvas’ width=’600’ height=’400’>
</body>
</html>
//Funciones
.............................................
function dibujarCuadricula(context, color,
stepx, stepy) { context.strokeStyle = color;
context.lineWidth = 0.5;
for (var i = stepx + 0.5; i <
context.canvas.width; i += stepx) {
context.beginPath();
context.moveTo(i, 0);
context.lineTo(i, context.canvas.height);
context.stroke();
}
for (var i = stepy + 0.5; i <
context.canvas.height; i += stepy) {
context.beginPath();
context.moveTo(0, i);
context.lineTo(context.canvas.width, i);
context.stroke();
}
}
//Iniciar
aplicación...................................
dibujarCuadricula(context, ‘lightgrey’, 10,
10);
context.strokeStyle = ‘blue’;
context.rect(200,100,200,200); //manejadores
de
eventos......................................
context.canvas.onmousedown = function (e) {
if (context.isPointInPath(200,150)) {
} };
scrollPathIntoView(path, element)
Atento
Descripción
Dibuja una elipse según los argumentos especíicados. Si la trayectoria
del obje
position:absolute;
left:10px;
top:1.5em;
margin:20px;
border:thin solid #aaaaaa;
}
</style>
</head>
<body>
</body>
</html>
}
for (var i = stepy + 0.5; i <
context.canvas.height; i += stepy) {
context.beginPath();
context.moveTo(0, i);
context.lineTo(context.canvas.width, i);
context.stroke();
}
context.restore();
}
context.beginPath();
context.strokeStyle = “black”;
context.lineWidth = 2;
//full circle
context.stroke(); context.closePath();
}
dibujarCuadricula(context, ‘lightgray’, 10,
10); drawScreen();
resetClip()
Métodos de Rectangulos
La API de Canvas provee tres métodos para aclarar o limpiar, dibujar y
rellenar rectángulos, respectivamente:
background: #dddddd;
}
#canvas {
position:absolute;
left:10px;
top:1.5em;
margin:20px;
border:thin solid #aaaaaa;
}
</style>
</head>
<body>
</body>
</html>
context.strokeStyle = ‘blue’;
context.strokeRect(75, 100, 200, 200);
background: #dddddd; }
#canvas {
position:absolute;
left:10px;
top:1.5em;
margin:20px;
border:thin solid #aaaaaa;
}
</style>
</head>
<body>
</body>
</html>
context.rect(0, 0, canvas.width,
canvas.height);
El resultado se mostrará mas o menos de esta manera:
<title>Crear Gradientes Radiales</title>
<style type=”text/css”>
body {
background: #dddddd;
}
#canvas {
position:absolute;
left:10px;
top:1.5em;
margin:20px;
border:thin solid #aaaaaa;
}
</style>
</head>
<body>
</body>
</html>
left:10px;
top:1.5em;
margin:20px;
border:thin solid #aaaaaa;
}
</style>
</head>
<body>
<canvas id=”canvas” width=”600” height=”400”>
Tu navegador no soporta canvas </canvas>
<script src=’linearGradient.js’></script>
</body>
</html>
context.rect(0, 0, canvas.width,
canvas.height);
El resultado se mostrará mas o menos de esta manera:
el canvas. La imagen usada en el patrón, especiicada con el primer
argumento del método, puede ser una imagen, un canvas o un
elemento de video. El segundo argumento especiica como el
navegador repite el patrón cuando lo has usado en líneas o rellenos
de formas. Valores válidos para el segundo argumento son: repeat,
repeat-x, repeat-y y no-repeat.
<head>
<title> Crear Patrones</title>
<style>
#canvas {
background: #eeeeee;
}
#radios {
padding: 10px;
}
</style>
</head>
<body>
<div id=’radios’>
<input type=’radio’ id=’repeatRadio’ name=
’patternRadio’ checked/>repeat
name=’patternRadio’/>repeat-x
<inputtype=’radio’ id=’repeatYRadio’
name=’patternRadio’/>repeat-y
<inputtype=’radio’ id=’noRepeatRadio’
name=’patternRadio’/>no repeat
</div>
<canvas id=’canvas’ width=’445’ height=’255’>
Tu navegador no soporta canvas de HTML5
</canvas>
<script src=’pattern.js’></script>
</body>
</html>
repeatYRadio =
document.getElementById(‘repeatYRadio’),
image = new Image();
//
Funciones....................................
var pattern = context.createPattern(image,
repeatString); context.clearRect(0, 0,
canvas.width, canvas.height);
} // Manejadores de
eventos......................................
la cual, la esquina superior izquierda de la imagen es dibujada.
Los argumentos dw y dh deinen el ancho y alto en la cual la imagen
debe ser colocada dentro del canvas destino. Si estos argumentos son
omitidos, la imagen será copiada en su tamaño original.
Los argumentos sx y sy deinen la esquina superior izquierda de la
región de la imagen recurso que esta siendo dibujada. Estos
argumentos son medidos en pixeles de imagen. Use especíicamente
estos argumentos si tu quieres copiar solo una parte del recurso
imagen.
Por último, los argumentos sw y sh deinen el ancho y el alto, en
pixeles de imagen, de la región del recurso imagen que esta siendo
dibujada dentro del canvas.
Sólo los primeros tres argumentos son requeridos.
El primer argumento en los tres casos anteriores es una imagen
(HTMLImageElement), pero este argumento puede ser también
otro canvas (HTMLCanvasElement) o un video
(HTMLVideoElement), lo que signiica que efectivamente puedes
tratar un canvas o un video como una imagen, lo cual abre puertas a
muchas posibilidades, tal como sotware de edición de video. De las tres
posibles combinaciones de argumentos de drawImage() listadas
anteriormente, El primero se usa para dibujar una imagen en un lugar
especiico del canvas destino; el segundo nos permite dibujar una
imagen en un lugar especiico, escalados a un ancho y alto especiicado
y el tercero se usa para dibujar una imagen completa o parte de ella,
en un lugar especiico del canvas destino a un ancho y alto especiicado
<head>
<title>Aplicación del método
drawImage</title>
<style>
body {
background:#eaeaea; }
#contenedor{
width:800px;
margin:0px auto;
padding-top:50px;
}
#guiaDeEscala {
position:relative;
vertical-align: 10px;
width: 100px;
margin-left: 80px; }
#canvas {
margin: 10px 20px 0px 20px; border: thin
solid #aaaaaa; cursor: crosshair;
padding: 0;
#controles {
margin-left: 15px; padding: 0;
}
#escalaDeSalida {
position: absolute;
width: 30px;
height: 30px;
margin-left: 10px;
vertical-align: center;
text-align: center;
color: blue;
font: 18px Arial;
text-shadow: 2px 2px 4px rgba(100, 140, 250,
0.8);
}
</style> </head>
<body>
<div id=”contenedor”>
<div id=’controles’>
<output id=’escalaDeSalida’>1.0</output>
<input id=’guiaDeEscala’ type=’range’ min=’1’
</body>
</html>
escalaDeSalida =
document.getElementById(‘escalaDeSalida’);
guiaDeEscala =
document.getElementById(‘guiaDeEscala’),
escala = guiaDeEscala.value,
escala = 1.0,
ESCALA_MINIMA = 1.0,
//
Funciones....................................
function dibujarTextoEscalado(value) {
var texto = parseFloat(value).toFixed(2); var
porcentaje = parseFloat(value -
ESCALA_MINIMA) /
escalaDeSalida.innerText = texto;
porcentaje = porcentaje < 0.35 ? 0.35 :
porcentaje;
‘em’; }
function dibujarMarcaDeAgua() {
context.save();
context.font = TAM_FUENTE + ‘px verdana’;
medidaTexto = context.measureText(lineaUno);
context.globalAlpha = 0.4;
context.translate(canvas.width/2,canvas.height
TAM_FUENTE/2);
context.strokeText(lineaUno, -
medidaTexto.width/2, 0);
medidaTexto = context.measureText(lineaDos);
context.strokeText(lineaDos, -
medidaTexto.width/2, TAM_FUENTE);
context.restore(); }
// manejadores de
eventos......................................
guiaDeEscala.onchange = function(e) { escala
= e.target.value;
if (escala < ESCALA_MINIMA) escala =
ESCALA_MINIMA;
dibujarEscalado();
dibujarTextoEscalado(escala); }
// Iniciar
aplicación...................................
context.strokeStyle = ‘blue’;
context.shadowColor = ‘rgba(50, 50, 50,
1.0)’;
context.shadowBlur = 10;
var tamLente = 150; var escala = 1.0;
Descripción
Este método acepta los mismos argumentos y trabaja de la misma
manera que
measureText(string text)
Descripción
Este método acepta solo un argumento: la cadena de texto a ser
medida.
Atento
Descent,hangingBaseline, alphabeticBaseline e
ideographicBaseline, de el objeto TextMetrics devuelto
por el método measureText(). Este objeto TextMetrics no
tiene la correspondiente propiedad height (al momento de escribir este
libro). Sin embargo, una observación a la historia de la medida del
texto, que como bien aclara la especificación canvas: Los graficos
renderizados usando y strokeText() pueden sobrepasar el
tamaño de la caja dado por el tamaño de la fuente actual y por ende
los valores devuelto por measureText() .
Esta observación desde la especificación significa que el ancho
devuelto por el método measureText() no es exacto y que a
menudo no es importante si estos valores son inexactos; sin embargo,
algunas veces esto es crucial, como por ejemplo: al editar una línea de
texto en un canvas.
<head>
<title>Aplicación de los
métodos de texto<title>
<style>
body {
background: #eaeaea;
color:#555;
}
#contenedor{
width:1100px;
margin:0px auto;
padding-top:50px; }
#contenedor_canvas{
width:600px; margin:0px auto; padding-
top:50px; }
#canvas {
padding:10px; }
#colores{
display:inline; padding:10px; }
#adicionales{
display:inline; padding:10px; }
#areaTexto{
display:inline;
padding:10px;
}
</style>
<script src=”modernizr-1.6.min.js”></script>
<script type=”text/javascript”
src=”jscolor/jscolor.js”></script> </head>
</head>
<body>
<div id=”contenedor_canvas”>
<canvas id=”canvas” width=”600” height=”300”>
<div id=”textos”>
Texto:<input id=”cajaTexto”
placeholder=”escribe lo que quieras”/><br>
Fuente: <select id=”fuenteUsada”>
<option value=”palatino”>palatino</option>
<option value=”serif”>serif</option>
<option value=”sans-serif”>sans-serif
</option>
<option value=”cursive”>cursive</option>
<option value=”fantasy”>fantasy</option>
<option value=”monospace”>monospace</option>
</select>
<br>
Grosor:
<select id=”grosorFuente”>
<option value=”normal”>normal</option>
<option value=”bold”>bold</option> <option
value=”bolder”>bolder</option> <option
value=”lighter”>lighter</option> </select>
<br>
Estilo:
<select id=”estiloFuente”>
<option value=”normal”>normal</option>
<option value=”italic”>italic</option>
<option value=”oblique”>oblique</option>
</select>
<br>
Tamaño: <input type=”range”
id=”tamañoTexto”
min=”0” max=”200” step=”1” value=”50”/>
<br>
</div>
<div id=”colores”>
<option value=”stroke”>Delinear</option>
<option value=”both”>Ambos</option> </select>
<br>
Alineación vertical <select
id=”alineacionVertical”>
<option value=”middle”>middle</option>
<option value=”top”>top</option> <option
value=”hanging”>hanging</option> <option
value=”alphabetic”>alphabetic
</option>
<option value=”ideographic”>ideographic
</option>
<option value=”bottom”>bottom</option>
</select>
<br>
Alineación horizontal <select
id=”alineacionHorizontal”>
<option value=”center”>center</option>
<option value=”start”>start</option> <option
value=”end”>end</option> <option
value=”left”>left</option> <option
value=”right”>right</option>
</select>
<br>
<br>
</div>
<div id=”adicionales”>
min=”-100”
max=”100”
step=”1”
value=”1”/>
<br>
<br>
<br>
Color sombra: <input class=”color”
id=”colorSombra” value=”707070”/>
<br>
<br>
Alto del canvas:
<input type=”range” id=”altoDelCanvas”
min=”0”
max=”1000”
step=”1”
value=”300”/>
<br>
<br>
Estiloalto canvas:
<input type=”range”
id=”estiloDeAltoDelCanvas” min=”0”
max=”1000”
step=”1”
value=”300”/>
<br>
<br>
</div>
<div id=”areaTexto”>
</div>
var sombreadoY = 1;
var desenfoqueSombra = 1;
var colorSombra = “#707070”;
var alineacionVertical = “middle”; var
alineacionHorizontal = “center”;
if (!soportarCanvas()) { return;
}
var canvas =
document.getElementById(“canvas”); var
context = canvas.getContext(“2d”);
var formElement =
document.getElementById(“cajaTexto”);
formElement.addEventListener(“keyup”,
cambiarCajaTexto, false);
formElement =
document.getElementById(“RellenarODelinear”);
formElement.addEventListener(“change”,
cambiarRellenarODelinear, false);
formElement =
document.getElementById(“tamañoTexto”);
formElement.addEventListener(“change”,
cambiarTamanoTexto, false);
formElement =
document.getElementById(“colorRellenoTexto1”)
formElement.addEventListener(“change”,
cambiarColorRellenoTexto, false);
formElement =
document.getElementById(“fuenteUsada”);
formElement.addEventListener(“change”,
cambiarFuenteUsada, false);
formElement =
document.getElementById(“alineacionVertical”)
formElement.addEventListener(“change”,
cambiarAlineacionVertical, false);
formElement =
document.getElementById(“alineacionHorizontal
formElement.addEventListener(“change”,
cambiarAlineacionHorizontal, false);
formElement =
document.getElementById(“grosorFuente”);
formElement.addEventListener(“change”,
cambiarGrosorFuente, false);
formElement =
document.getElementById(“estiloFuente”);
formElement.addEventListener(“change”,
cambiarEstiloFuente, false);
formElement.addEventListener(“change”,
formElement =
document.getElementById(“sombreadoY”);
formElement.addEventListener(“change”,
cambiarSombreadoY, false);
formElement =
document.getElementById(“desenfoqueSombra”);
formElement.addEventListener(“change”,
cambiarDesenfoqueSombra, false);
formElement =
document.getElementById(“colorSombra”);
formElement.addEventListener(“change”,
cambiarColorSombra, false);
formElement =
document.getElementById(“transparenciaTexto”)
formElement.addEventListener(“change”,
cambiarTransparenciaTexto, false);
formElement =
document.getElementById(“colorRellenoTexto2”)
formElement.addEventListener(“change”,
cambiarColorRellenoTexto2, false);
formElement =
document.getElementById(“tipoRelleno”);
formElement.addEventListener(“change”,
cambiarTipoRelleno, false);
formElement =
document.getElementById(“anchoDelCanvas”);
formElement.addEventListener(“change”,
cambiarAnchoDelCanvas, false);
formElement =
document.getElementById(“altoDelCanvas”);
formElement.addEventListener(“change”,
cambiarAltoDelCanvas, false);
formElement = document.getElementById(
“estiloDeAnchoDelCanvas”);
formElement.addEventListener(“change”,
cambiarTamanoCanvas, false);
formElement = document.getElementById(
“estiloDeAltoDelCanvas”);
formElement.addEventListener(“change”,
cambiarTamanoCanvas, false);
formElement =
document.getElementById(“crearImageData”);
formElement.addEventListener(“click”,
crearImageDataImpreso, false);
patrones.src = “texture.jpg”;
dibujarEnCanvas();
function dibujarEnCanvas() { //Fondos
context.globalAlpha = 1;
context.shadowColor = “#707070”;
context.shadowBlur = 0;
//Box
context.strokeStyle = “#444”;
context.strokeRect(0, 0, canvas.width,
canvas.height);
//Textos
context.textBaseline = alineacionVertical;
context.textAlign = alineacionHorizontal;
context.font = grosorFuente + “ “ +
estiloFuente + “ “ +
vartempColor;
if (tipoRelleno == “colorFill”) {
tempColor = colorRellenoTexto;
} else if (tipoRelleno == “linearGradient”) {
var gradient = context.createLinearGradient(
gradient.addColorStop(0,colorRellenoTexto);
gradient.addColorStop(.6,colorRellenoTexto2);
tempColor = gradient;
1);
gradient.addColorStop(0,colorRellenoTexto);
gradient.addColorStop(.6,colorRellenoTexto2);
tempColor = gradient;
} else {
tempColor = colorRellenoTexto;
}
switch(RellenarODelinear) {
break;
case “stroke”:
context.strokeStyle = tempColor;
context.strokeText ( mensajeBienvenida,
function cambiarCajaTexto(e) {
var target = e.target;
mensajeBienvenida = target.value;
dibujarEnCanvas();
}
var target = e.target;
dibujarEnCanvas(); }
}
function cambiarTransparenciaTexto(e) { var
target = e.target;
transparenciaTexto = (target.value);
dibujarEnCanvas();
}
function cambiarTamanoCanvas(e) {
function crearImageDataImpreso(e) {
var imageDataDisplay =
document.getElementById( “imageDataDisplay”);
imageDataDisplay.value = canvas.toDataURL();
window.open(imageDataDisplay.value,
”canavsImage”,”left=0,top=0,width=” +
canvas.width + “,height=” + canvas.height
+”,toolbar=0,resizable=0”);
}
}
function iniciarAplicacion() {
aplicacionTextos();
}
function eventWindowLoaded() {
var patrones = new Image();
patrones.src = “texture.jpg”;
patrones.onload = cargarRecursosAplicacion;
}
function cargarRecursosAplicacion() {
aplicacionTextos();
}
Atento
(400 x 400) pixeles de dispositivo. Tu puedes sacar muchos pixeles de
dispositivo con las propiedades width y height de tu objeto
imagedata.
canvas
y imagedata
Atento
el despliegue de los gradientes o la aplicación de las sombras. Esto es
lo contrario de drawImage(), la cual si es afectada por todas estas
cosas.
Atento
<head>
<title>
Aplicaciónde los métodos
getImageData(), putImageData() y
createIamgeData()
</title>
<style>
body {
background: #eaeaea; }
#contenedor{
width:800px;
margin:0px auto;
padding-top:50px;
}
#canvas {
margin-left: 20px;
margin-right: 0;
margin-bottom: 20px;
border: thin solid #aaaaaa;
cursor: crosshair;
}
#controles {
position:relative;
top:60px;
left:20px;
margin: 20px 0px 20px 20px; }
</style> </head>
<body>
<div id=”contenedor”>
<div id=’controles’>
canvas.height),
mousedown = {},
bandasRectangulares = {}, arrastrar = false;
//
Funciones....................................
function windowToCanvas(canvas, x, y) {
var rectanguloCanvas =
canvas.getBoundingClientRect();
return { x: x - rectanguloCanvas.left, y: y -
rectanguloCanvas.top };
}
function copiarPixelesCanvas() { var i=0;
// copia los componentes rojo, verde y azul
del primer pixel
copiaImageData.data[i+1] =
imageData.data[i+1]; // Rojo
copiaImageData.data[i+2] =
imageData.data[i+2]; // Verde
copiaImageData.data[i+3] =
imageData.data[i+3]; // Azul
}
}
function capturarPixelesCanvas() {
imageData = context.getImageData(0, 0,
canvas.width, canvas.
height);
copiarPixelesCanvas();
}
function restaurarPixelBandas() {
var anchoDispositivoSobreCSSPixeles =
imageData.width / canvas.
width,
altoDispositivoSobreCssPixeles =
imageData.height / canvas.height;
context.putImageData(copiaImageData, 0, 0,
(bandasRectangulares.left +
context.lineWidth), (bandasRectangulares.top
+ context.lineWidth),
(bandasRectangulares.width -
2*context.lineWidth) *
anchoDispositivoSobreCSSPixeles,
(bandasRectangulares.height -
2*context.lineWidth) *
altoDispositivoSobreCssPixeles);
}
bandasRectangulares.left = Math.min(x,
mousedown.x); bandasRectangulares.top =
Math.min(y, mousedown.y);
bandasRectangulares.width = Math.abs(x -
mousedown.x), bandasRectangulares.height =
Math.abs(y - mousedown.y);
}
function dibujarBanda() {
var anchoDispositivoSobreCSSPixeles =
imageData.width / canvas.
width,
altoDispositivoSobreCssPixeles =
imageData.height / canvas.height;
context.strokeRect(bandasRectangulares.left +
context.lineWidth, bandasRectangulares.top +
context.lineWidth, bandasRectangulares.width
- 2*context.lineWidth,
bandasRectangulares.height -
2*context.lineWidth);
bandasRectangulares.left = mousedown.x;
bandasRectangulares.top = mousedown.y;
bandasRectangulares.width = 0;
bandasRectangulares.height = 0;
arrastrar = true;
capturarPixelesCanvas(); }
function ajustarBanda(x, y) {
if (bandasRectangulares.width >
2*context.lineWidth &&
bandasRectangulares.height >
2*context.lineWidth) {
restaurarPixelBandas(); }
}
if (bandasRectangulares.width >
2*context.lineWidth &&
bandasRectangulares.height >
2*context.lineWidth) { dibujarBanda();
}
};
context.putImageData(imageData, 0, 0);
// Draw the canvas back into itself, scaling
along the way
context.drawImage(canvas,
bandasRectangulares.left +
context.lineWidth*2, bandasRectangulares.top
+ context.lineWidth*2,
bandasRectangulares.width -
4*context.lineWidth,
bandasRectangulares.height -
4*context.lineWidth,
0, 0, canvas.width, canvas.height);
arrastrar = false;
}
// Manejadores de
eventos......................................
canvas.onmousedown = function (e) {
e.preventDefault();
inicioBanda(loc.x, loc.y); };
canvas.onmousemove = function (e) { var loc;
if (arrastrar) {
ajustarBanda(loc.x, loc.y); }
};
canvas.onmouseup = function (e) {
imagen 2.27 - aplicación del método imageData().
Descripción
Este método puede tomar solo un set de argumentos:
<head>
<title>Ejemplo de los métodos de
ImageDataHD</title> <style>
body {
background: #eaeaea;
}
#contenedor{
width:800px;
margin:0px auto;
padding-top:50px;
}
#canvas {
}
</style>
</head>
<body onload=”init()”>
<div id=”contenedor”>
<canvas id=’canvas’ width=’800’ height=’400’>
Tu navegador no soporta canvas de HTML5
</canvas>
</div>
<script src=’javascript/getImageDataHD.js’>
</script> </body>
</html>
image.onload = demo;
image.src = “images/slider-img2.jpg”;
}
function demo() {
var canvas =
document.getElementsByTagName(‘canvas’)[0];
var context = canvas.getContext(‘2d’);
// detección de bordes
for (var y = 1; y < h-1; y += 1) {
for (var x = 1; x < w-1; x += 1) {
-inputData[i - 4] + 8*input
Data[i] - inputData[i + 4] +
-inputData[i + w*4 - 4] input
Data[i + w*4] - inputData[i + w*4 + 4];
}
outputData[(y*w + x)*4 + 3] = 255; // alpha
}
}
// poner los datos de la imagen de nuevo
después de //la manipulación
context.putImageDataHD(output, 0, 0);
}
drawSystemFocusRing(path, element)
Nota destacada!
Atento
drawCustomFocusRing(path, element)
La región Clipping
width:600px;
margin:0px auto;
padding-top:50px;
#canvas {
background: #eaeaea;
}
</style>
</head>
<body>
<div id=”contenedor”>
<canvas id=’canvas’ width=’600’ height=’400’>
Tu navegador no soporta canvas de HTML 5
</canvas>
<script src=’regionClipping.js’></script>
</div>
</body>
</html>
stepx) {
context.beginPath();
context.moveTo(i, 0);
context.lineTo(i, context.canvas.height);
context.stroke();
}
for (var i = stepy + 0.5; i <
context.canvas.height; i += stepy) {
context.beginPath();
context.moveTo(0, i);
context.lineTo(context.canvas.width, i);
context.stroke();
}
}
function drawScreen() {
context.save();
context.beginPath();
false);
//circulo full
context.stroke(); context.closePath();
context.restore();
fillStyle
Descripción
Tipo
Valor por defecto
font
Descripción
Tipo
Valor por defecto
Atento
Configurar las propiedades de la fuente.
propiedad font-style
font-variant
font-weight
valores permitidos
Tres valores son permitidos: normal, italic y oblique Dos
valores son permitidos: normal y small-caps Determina el grosor
del carácter de una fuente: normal, bold, bolder (una fuente
mas resaltada que la fuente base), lighter (una fuente menos
resaltada que la fuente base), 100, 200, 300,…,900. una fuente de 400
es normal, 700 es bold
propiedad font-size
line-height
font-family
valores permitidos
Valores para el tamaño de la fuente: xx-small, x-small,
medium, large, x-large, xx-larger, smaller, larger,
length, %.
El navegador siempre lleva esta propiedad a su valor por defecto, la
cual es normal. Si tu coniguras esta propiedad, el navegador ignorará
tu coniguración.
Dos tipos de nombres de familias de fuentes son permitidas: family-
name, tal como helvética, verdana, palatino, etc. Y nombres
de generic-family: serif, sans-serif, monospace, cursive
y fantasy. Tu puedes usar family-name, generic-family o ambos para
el componente font-family de la fuente.
<head>
<title>La propiedad font y sus
valores</title> <style>
body {
background: #eaeaea;
}
#contenedor{
width:500px;
margin:0px auto;
padding-top:50px;
}
#canvas {
}
</style> </head>
<script src=’javascript/font.js’></script>
</body>
</html>
FUENTES_A = [
‘normal 2em palatino’,’bolder 2em palatino’,
‘lighter 2em palatino’, ‘italic 2em
palatino’, ‘oblique small-caps 30px
palatino’,’bold 18pt palatino’, ‘xx-large
palatino’, ‘italic xx-large palatino’,
‘oblique 1.5em lucida console’, ‘x-large
fantasy’, ‘italic 28px monaco’, ‘italic large
copperplate’, ‘36px century’, ‘28px tahoma’,
‘28px impact’, ‘1.7em verdana’
],
DELTA_Y = 40, TOP_Y = 50, y = 40;
function dibujarFondo() {
var DISTANCIA_Y = 12,
i = context.canvas.height;
context.strokeStyle = ‘rgba(0,0,0,0.5)’;
context.lineWidth = 0.5;
context.save(); context.restore();
}
context.save();
context.strokeStyle = ‘rgb(0,0,0)’;
context.lineWidth = 1;
context.beginPath();
context.moveTo(35,0);
context.lineTo(35,context.canvas.height);
context.stroke(); context.restore(); }
dibujarFondo();
FUENTES_A.forEach( function (fuentes) {
context.font = fuentes;
});
globalAlpha
Descripción
Tipo
Valor por defecto
Atento
Nota destacada!
globalComposite-Operation
Resultado Valor
source-atop
source-in
Descripción
Renderiza A en la propiedad top de B solo donde B no es
transparente
Renderiza solo A y solo donde B no es transparente
source-out Renderiza solo A y solo donde B es transparente
source-over
destination-atop
destination-in
width:600px;
margin: 0px auto; padding-top:50px;
#canvas {
background: #eaeaea;
}
</style>
</head>
<body>
<divid=”contenedor”>
<canvas id=’canvas’ width=’600’ height=’400’>
Tu navegador no soporta canvas de HTML 5
</canvas>
<script src=’composicionCanvas.js’></script>
</div>
</body>
</html>
//Funciones..................................
function dibujarCuadricula(context, color,
stepx, stepy) { context.strokeStyle = color;
context.lineWidth = 0.5;
for (var i = stepx + 0.5; i <
context.canvas.width; i += stepx) {
context.beginPath();
context.moveTo(i, 0);
context.lineTo(i,context.canvas.height);
context.stroke();
}
for (var i = stepy + 0.5; i <
context.canvas.height; i += stepy) {
context.beginPath();
puedes especiicar algunos de los siguientes tres valores: butt,
round y square. String.
butt
Atento
butt round square
imagen 2.31 - diferentes valores para las terminaciones de las
lineas
lineCap
lineWidth
Descripción
Tipo
Valor por defecto
Determina el ancho, en pixel de pantalla o screen, de lo que tu dibujas
en el elemento canvas. El valor debe ser un double no negativo y no
ininito. Numbers
1.0
lineJoin
Descripción
Tipo
Valor por defecto
Especiica como son unidas las líneas cuando se encuentran los puntos
inales. Tu puedes especiicar algunos de los siguientes tres valores:
bevel, round y miter.
String
miter
Atento
ta de las dos lineas con una linea recta. miter, la cual es el valor por
defecto para esta propiedad, es lo mismo que bevel con la diferencia
que miter agrega un triangulo extra al cuadrado de las esquinas.
Finalmente, un valor round para esta propiedad resulta de crear un
arco en las dos esquinas.
miterLimit
Descripción
Tipo
Valor por defecto
Especiica como son unidas las líneas cuando se encuentran los puntos
inales. Tu puedes especiicar algunos de los siguientes tres valores:
bevel, round y miter.
String
miter
shadowBlur
Descripción
Tipo
Valor por defecto
Numbers
0.0
shadowColor
Descripción
Tipo
Valor por defecto
Valores permitidos
#F444
#44FF44
rgb(60,60,255)
rgb(100%,25%,100%) rgba(0,0,0,0)
hsl(60, 100%, 50%) hsla(60,100%,50%,0.5) magenta
Comentario
Color RGB Hexadecimal: red (rojo)
shadowOffsetX
Descripción
Tipo
Valor por defecto
Determina la impresión horizontal, en pixeles de pantalla, para
sombrados. Numbers
0.0
shadowOffsetY
Descripción
Tipo
Valor por defecto
Determina la impresión vertical, en pixeles de pantalla, para
sombreados. Numbers
0.0
Atento
Las sombras
Descripción
Tipo
Valor por defecto
Especiica estilo usado para el dibujado de las líneas. Este valor puede
ser un color, gradiente o patrón. Los valores válidos son: una cadena
que contiene un color CSS, un canvasGradient Object y un
canvasPattern Object. cualquiera
#000000
Atento
textAlign
Tipo String
Valor por defecto start
La propiedad textAlign
El valor por defecto para la propiedad textAlign es start y cuando el
navegador muestra el texto desde la izquierda a la derecha, significa
que el atributo dir del elemento canvas es ltr, left es lo mismo
que
Atento
textBaseLine
Tipo String
Valor por defecto alphabetic
La propiedad textBaseline
El valor por defecto para la propiedad textBaseline es alphabetic,
la cual es usado para lenguajes base latin, ideographic es usado
para lenguajes tales como japonés y chino, mientras que hanging es
Atento
<script src=’javascript/posicionTextos.js’>
</script> </body> </html>
//
Funciones....................................
function dibujarCuadricula(context, color,
stepx, stepy) { context.save();
context.strokeStyle = color;
context.lineWidth = 0.5;
height);
context.beginPath();
context.moveTo(x, y); context.lineTo(x + 728,
y); context.stroke();
}
//
Initialization...............................
context.font = ‘normal bold 18px palatino’;
dibujarCuadricula(context, ‘#ccc’, 10, 10);
dibujarTextos(‘posicion: ‘ +
valoresTextAlign[align] + ‘/’ +
métodos y strokeText(). Valores válidos son: ltr, rtl e
inherit
Tipo String
Valor por defecto inherit
La propiedad direction
El valor por defecto para la propiedad direction es inherit, lo cual
sigue la direccionalidad del elemento canvas o documento en su caso,
ltr es usado para indicar al canvas que deseamos que los textos se
dibujen
Atento
lineDashOffset
Descripción
Tipo
Valor por defecto
Esta coniguración puede ser usada para estipular cuán lejos dentro de
la secuencia de la linea dash se dibuja al inicio de la misma. es decir, si
coniguras una linea dash según esta matriz de números ([5,5,2,2]) y
coniguras la propiedad lineDashOfset igual a 10, entonces, el primer
trazo que es dibujado será de 2 pixeles de tamaño seguido de un
espacio de 2 pixeles, para luego repetir el ciclo indicado en la matriz
de números , una y otra vez, hasta que inalice la linea a la cual se le
aplica el método dash.
Number
0
<head>
</title>
<style>
body {
background: #eaeaea; }
#contenedor{
width:500px; margin:0px auto; padding-
top:50px;
}
#canvas {
}
</style> </head>
<body>
<div id=”contenedor”>
<canvas id=’canvas’ width=’500’ height=’300’>
</div>
<script src=’javascript/dash.js’></script>
</body>
</html>
// El circulo
contexto.setLineDash([5]);
contexto.mozDash = [5];
contexto.beginPath();
contexto.arc(55,215,40,0,2 * Math.PI,false);
contexto.stroke();
// El cuadrado
contexto.setLineDash([1,2]); contexto.mozDash
= [5];
contexto.beginPath();
contexto.strokeStyle = ‘red’;
contexto.rect(130,175,80,80);
contexto.stroke();
// la linea curva
contexto.setLineDash([5]);
contexto.mozDash = [5];
contexto.beginPath();
contexto.strokeStyle = ‘blue’;
contexto.moveTo(255,175);
contexto.quadraticCurveTo(295,325,345,175);
contexto.stroke();
// La forma irregular
contexto.setLineDash([5,5,2,2]);
contexto.mozDash = [5];
contexto.beginPath();
contexto.strokeStyle = ‘green’;
contexto.moveTo(415,175);
contexto.lineTo(385,215);
contexto.lineTo(405,255);
contexto.lineTo(455,255);
contexto.lineTo(435,225);
contexto.lineTo(455,175);
contexto.closePath();
contexto.stroke();
};
/** */
function incrementarDash () {
if (typeof(interval_reference) != ‘number’) {
interval_reference = setTimeout(foo =
function () {
contexto.canvas.width =
contexto.canvas.width;
// Dibujar las lineas/formas window.onload();
setTimeout(foo, 50);
}, 50);
location.href = ‘#introduction’; }
}
Los objetos dentros del canvas
textMetrics
Descripción
Atributos
width, actualBoundingBoxLeft,
actualBoundingBoxRight,
fontBoundingBoxAscent,
fontBoundingBoxDescent,
actualBoundingBoxAscent,
actualBoundingBoxDescent, emHeightAscent,
emHeightDescent, hangingBaseline,
alphabeticBaseline, ideographicBaseline
Direccion horizontal X
width
Descripción
Representa el ancho del texto dado. viene expresado en pixeles CSS
actualBoundingBoxLeft
Descripción
actualBoundingBoxLeft y actualBoundingBoxRight
la suma de estos valores puede ser más ancho que el ancho de la
linea de caja (anchura), en particular con las fuentes inclinadas donde
los caracteres sobresalen de su anchura normal.
Atento
actualBoundingBoxRight
Direccion vertical Y
fontBoundingBoxAscent
fontBoundingBoxDescent
actualBoundingBoxAscent
Descripción
actualBoundingBoxDescent
emHeightAscent
emHeightDescent
hangingBaseline
alphabeticBaseline
ideographicBaseline
Nota destacada!
Los gráficos usando los métodos y strokeText() pueden
sobresalir de la caja propuesta por el tamaño de la fuente (el tamaño
del cuadro em) y la anchura devuelta por el método mea
Atento
sureText() (el ancho del texto). Los autores pueden utilizar los
valores del cuadro delimitador descritos anteriormente si esto es un
problema.
El Objeto canvasGradient
Descripción
Especiica colores ijos dentro de un gradiente. Si tu especíicas 2 o mas
para
El Objeto ImageData
height
Descripción
Devuelve el componente de altura real de los datos en el objeto
ImageData,
Descripción
Devuelve el número teórico de pixeles en los datos del objeto
ImageData
data
El Objeto Path
Descripción
Junto con las regiones Hit, los objetos Path son una de las adiciones
más signif
icativas de la especiicación canvas. Actualmente el canvas tiene solo
un path que se puede manipular, delinear y/o rellenar. Una vez que
hayamos creado un n uevo path, el primer path se pierde. Los objetos
paths le permiten conservar los paths y reproducirlos, ajustarlos y
ponerlos a prueba mediante el método isPointInPath(). Esta última
característica, la prueba de posicionamiento del método isPointInPath(),
hara que se pruebe la detección de colisiones en el canvas de una
forma más rapida y sencilla. Actualmente si usted desea probar si un
par de coordenadas están dentro de un cuadrado, tu tienes que
probar que esas coordenadas esten dentro de los límites X/Y/W/H del
cuadrado. Con el método isPointInPath(), se realiza con sólo una
simple llamada a la función y pasar los parametros para comoprobar el
resultado (que será true o false). También tiene el potencial de eliminar
las porciones de código, en partícular cuando se trata de formas mucho
más complejas.
Al momento de escribir este libro, ningún navegador soporta aún las
funciones del objeto path y el objeto en si mismo.
moveTo(float x, float y)
closePath()
Descripción
Explícitamente cierra un path abierto. Este método es para abrir paths
de arcos y paths creados con líneas o curvas.
lineTo(float x, float y)
Descripción
Crea un path para una curva Bézier cuadrática. Este método acepta
cuatro (4)
Descripción
Agrega una curva bezier cubica al actual subpath. Este método acepta
seis (6)
arcTo(float x0, float y0, float x1, float y1, float radius)
Descripción Este método acepta cinco (5) argumentos: los dos primeros
representan las coordenadas del punto P1, los dos siguientes
representan las cordenadas del punto P2 y el último argumento
representa el radio del círculo que deine el arco. Este método agrega
una línea recta y un arco al actual subpath y describe un arco que lo
hace particularmente útil para dar esquineas redondeadas a los
poligonos. El arco que es agregado al path es una porción de un
círculo con el radio especiicado. El arco tiene un punto que es
tangente a la línea desde la posición actual a P1 y un punto que es
tangente a la línea desde P1 a P2. El arco comienza y termina en esos
dos puntos tangentes y es dibujado en la posición que conecta estos
dos puntos con el arco mas pequeño. Despues agrega el arco al path,
este método agrega una línea recta desde el punto actual al inicio del
punto del arco. Despues de llamar a este método, el punto actual es el
último punto del arco, la cual reside entre los puntos P1 y P2.
Descripción
Este método agrega un rectángulo al path, es un subpath de si mismo y
no
esta conectado a otro subpath en el path, es decir, el subpath es
implícitamente cerrado y siempre en dirección al sentido de las agujas
del reloj. Este método devuelve la posición actual en sus coordenadas
x e y.
Acepta 4 argumentos, x e y representan las coordenadas de la
esquina superior izquierda del rectángulo, mientras que width y
height representan las dimensiones del ancho y alto del rectángulo.
Un llamado a este método es equivalente a una secuencia de llamado
de los siguientes métodos:
styles.moveTo(x, y);
styles.lineTo(x + w, y);
styles.lineTo(x + w, y + h);
styles.lineTo(x, y + h);
styles.closePath();
El Objeto DrawingStyle
Descripción
Todos los estilos de linea (line Width, caps, join y patrones dash) y
estilos de
textos (fonts) son cubiertos por este objeto. En esta sección se deine el
constructor usado para obtener un objeto DrawingStyle. Este objeto es
usado por los métodos en objetos path para controlar como los textos y
los trazos son rasterizados y delineados.
Descripción
Especiica como el buscador dibuja los puntos inales de una línea. Tu
puedes especiicar algunos de los siguientes tres valores: butt,
round y square.
butt round square imagen 2.31 - diferentes valores para las
terminaciones de las lineas
lineJoin
Descripción
Especiica como son unidas las líneas cuando se encuentran los puntos
inales. Tu puedes especiicar algunos de los siguientes tres valores:
bevel, round y miter.
miterLimit
lineDashOffset
font
Descripción
Especiica la fuente usada por el objeto DrawingStyle. La sintaxis es la
misma
propiedad font-style
font-variant
font-weight
font-size
line-height
font-family
valores permitidos
Tres valores son permitidos: normal, italic y oblique Dos
valores son permitidos: normal y small-caps Determina el grosor
del carácter de una fuente: normal, bold, bolder (una fuente
mas resaltada que la fuente base), lighter (una fuente menos
resaltada que la fuente base), 100, 200, 300,…,900. una fuente de 400
es normal, 700 es bold Valores para el tamaño de la fuente: xx-
small, x-small, medium, large, x-large, xx-larger,
smaller, larger, length, %.
El navegador siempre lleva esta propiedad a su valor por defecto, la
cual es normal. Si tu coniguras esta propiedad, el navegador ignorará
tu coniguración.
Dos tipos de nombres de familias de fuentes son permitidas: family-
name, tal como helvética, verdana, palatino, etc. Y nombres
de generic-family: serif, sans-serif, monospace, cursive
y fantasy. Tu puedes usar family-name, generic-family o ambos para
el componente font-family de la fuente.
textAlign
textBaseLine
Descripción
Determina la colocación vertical del texto dibujado. Los valores válidos
para esta propiedad son: top, hanging, middle, alphabetic,
ideographic y bottom. El valor por defecto es alphabetic.
direction
Descripción
Determina la dirección de como se dibujarán los textos en el canvas.
Valores válidos son: ltr, rtl e inherit. El valor por defecto es
inherit.
getLineDash(segments)
La región Hit
Y opcionalmente por
√ Una cadena no vacía que representa un identiicador para distinguir
dicha región de las demás. √ Una referencia a otra región que actúa
como el padre de esta.
√ Un número de regiones que tiene a este como su padre, conocido
como la cantidad de hijos de las regiones Hit.
addHitRegion(options)
La región Hit puede ser usada para una gran variedad de propósitos!
Atento
click en un canvas y enviar automáticamente un formulario via un
elemento “ button” .
Con una etiqueta (label), pueden hacer que sea más fácil para las
diferentes regiones del canvas tener diferentes cursores, el user agent
automáticamente cambiaría entre ellos.
removeHitRegion(options)
<head>
<title>Aplicación de La Region
Hit</title> <style>
body {
background: #eaeaea;
}
#contenedor{
width:500px; margin:0px auto; padding-
top:50px; }
#canvas {
}
</style> </head>
<script src=’javascript/regionHit.js’>
</script> </body>
</html>
Descripción
Para probar si un punto P esta dentro de un path, utilizando la regla
‘nonzeron
imagen 2.37
- aplicación de la regla nonzero
EvenOdd Rule
Descripción
El valor “ EvenOdd” indica la regla par-impar, en el que un punto se
considerará
Adicional al canvas
ctx.beginPath();
ctx.arc(75, 75, 75, 0, Math.PI*2, true);
ctx.arc(75, 75, 25, 0, Math.PI*2, true);
Un potente escenario
<head>
<title>
Tutorial juego de arcade </title>
<style>
body {
background: #eaeaea; }
#contenedor{
width:400px;
margin:0px auto;
padding-top:20px;
}
#canvas {
margin-left: 20px;
margin-right: 0;
margin-bottom: 20px;
border: thin solid #aaaaaa; cursor:
crosshair; }
</style>
</head>
<body>
<div id=”contenedor”>
<canvas id=’canvas’ width=’400’ height=’600’>
<script src=’defensaEspacial.js’></script>
</body>
</html>
canvasApp();
}
function canvasApp(){
var canvas =
document.getElementById(‘canvas’); if
(!canvas || !canvas.getContext) {
return;
}
var context = canvas.getContext(‘2d’);
if (!context) { return;
}
crearDibujo();
function crearDibujo() {
context.font = ‘30px sans-serif’;
context.textBaseline = ‘top’;
}
}
No vamos a asumir que todo el que lea este tutorial conoce o entiende
el clásico juego de arcade de Atari® ‘Asteroids’. Asteroids fue diseñado
por Ed Logg y Lile Rains, este fué lanzado por Atari® en 1979. El juego
enfrenta a una pequeña nave espacial de forma triangular
bidimensional vectorizada (jugador) contra un grupo de asteroides
que se va multiplicando a medida que avanza y que a su vez necesitan
ser destruidos o esquivados por el jugador. De vez en cuando aparece
un platillo volador que intentará destruir la nave del jugador con sus
misiles.
Todos los objetos del juego se mueven (empujar, rotar y/o lotar)
libremente por toda la pantalla de juego, lo que representa una
porción del plano espacial. Cuando uno de estos objetos sale de la
pantalla, este vuelve a aparecer por el extremo contrario, así se da la
impresión de un espacio ininito.
Que necesitaremos
Los paths nos ofrecen una manera muy simple pero de gran alcance
para imitar el aspecto vectorial del clásico juego de asteroides.
Podríamos utilizar las imágenes de mapa de bits para este propósito,
pero en esta sección nos vamos a centrar en la creación de nuestro
juego en el código sin activos externos. Vamos a comenzar a realizar
los dos fotogramas de animación necesarios para nuestra nave
espacial del jugador.
context.beginPath(); context.moveTo(16,0);
context.lineTo(30,19); context.lineTo(16,14);
context.moveTo(16,14); context.lineTo(0,19);
context.lineTo(16,0); context.stroke();
context.closePath();
context.stroke();
Animación en el canvas
crearDibujo();
window.setTimeout(gameLoop, intervaloTiempo);
}
canvasApp();
}
function canvasApp(){
var canvas =
document.getElementById(‘canvas’); if
(!canvas || !canvas.getContext) {
return;
}
var context = canvas.getContext(‘2d’);
if (!context) {
return;
}
crearDibujo();
//variables de nivel del objeto canvasApp
function crearDibujo() {
//actualizar estado de la nave espacial
estadoNave++;
if (estadoNave >1) {
estadoNave=0;
}
context.font = ‘30px sans-serif’;
context.textBaseline = ‘top’;
//dibujar la nave espacial estatica (jugador)
context.strokeStyle = ‘red’;
context.beginPath();
context.moveTo(16,0);
context.lineTo(30,19);
context.lineTo(16,14);
context.moveTo(16,14);
context.lineTo(0,19);
context.lineTo(16,0);
//dibujar la nave espacial de avance
(jugador) if (estadoNave==1) {
context.moveTo(16,16); context.lineTo(17,20);
context.moveTo(16,16); context.lineTo(16,22);
context.moveTo(17,16); context.lineTo(16,22);
}
context.stroke();
context.closePath();
}
const PROM_FOTOGRAMA = 30;
var intervaloTiempo = 1000/PROM_FOTOGRAMA;
temporizador();
function temporizador(){
crearDibujo();
window.setTimeout(temporizador,intervaloTiempo
}
}
Aplicando transformaciones a los gráficos del juego
canvasApp();
}
function canvasApp(){
var canvas =
document.getElementById(‘canvas’); if
(!canvas || !canvas.getContext) {
return;
}
var context = canvas.getContext(‘2d’);
if (!context) {
return;
}
crearDibujo();
//variables de nivel del objeto canvasApp var
rotacion = 0;
var x = 50;
var y = 50;
function crearDibujo() {
//transformación
var anguloEnRadianes = rotacion * Math.PI /
180; context.save(); //salvar el actual
estado de la pila // reestablecer la
identidad
context.setTransform(1,0,0,1,0,0);
//Trasladar el canvas original al centro del
jugador context.translate(x,y);
context.rotate(anguloEnRadianes);
//dibujar la nave espacial estatica (jugador)
context.strokeStyle = ‘red’;
context.beginPath(); context.moveTo(16,0);
context.lineTo(30,19); context.lineTo(16,14);
context.moveTo(16,14); context.lineTo(0,19);
context.lineTo(16,0);
context.stroke();
context.closePath();
//restaurar el contexto
//antiguo estado emergente a la pantalla
context.restore();
//agregar la rotación
rotacion++;
function temporizador(){
crearDibujo();
window.setTimeout(temporizador,
intervaloTiempo);
}
}
Como puede ver, la nave del jugador gira en sentido horario un grado
a la vez. Como hemos mencionado anteriormente, debemos convertir
los grados a radianes para las transformaciones del método
context rotate(), que usan radianes para los cálculos. En la siguiente
parte de nuestro tutorial, veremos algunas transformaciones mas
complejas que usaremos en nuestro juego básico Defensa Espacial.
Transformaciones grafica del juego
Nota destacada!
Las variables width y height representan los atributos ancho y alto de
nuestro nave espacial (jugador) dibujada. Vamos a crear estos
atributos en elas siguientes lineas.
Atento
Este no es el único cambio que tenemos que hacer, también tenemos
que dibujar nuestra nave espacial como si pensamos que es el punto
central. Para realizar esto, vamos a restar la mitad del ancho de cada
atributo x en nuestra secuencia de trazos de dibujo, y restar la mitad
de la altura de cada atributo y:
Como puede ver, puede ser un poco confuso tratando de dibujar las
coordenadas de esta manera. También es ligeramente menos intenso
para el procesador que utilizar constantes. En ese caso, podríamos
simplemente codiicar los valores necesarios. Recuerde que los
atributos de anchura y altura de nuestra nave espacial son ambos 30 y
20. La versión codiicada sería algo como esto:
var canvas =
document.getElementById(‘canvas’);
if (!canvas || !canvas.getContext) {
return;
}
var context = canvas.getContext(‘2d’); if
(!context) {
return;
}
crearDibujo();
//variables de nivel del objeto canvasApp var
rotacion = 0;
var x = 50;
var y = 50;
var width=30;
var height=20;
function crearDibujo() {
//transformación
var anguloEnRadianes = rotacion * Math.PI /
180; context.save(); //salvar el actual
estado de la pila
context.setTransform(1,0,0,1,0,0);//reestable
la identidad //Trasladar el canvas original
al centro del jugador
context.translate(x+.5*width,y+.5*height);
context.rotate(anguloEnRadianes);
context.beginPath();
context.moveTo(16-.5*width,0-.5*height);
context.lineTo(30-.5*width,19-.5*height);
context.lineTo(16-.5*width,14-.5*height);
context.moveTo(16-.5*width,14-.5*height);
context.lineTo(0-.5*width,19-.5*height);
context.lineTo(16-.5*width,0-.5*height);
context.stroke();
context.closePath();
//restaurar el contexto context.restore();
//antiguo estado emergente a la pantalla
//agregar la rotación
rotacion++;
}
const PROM_FOTOGRAMA = 45;
var intervaloTiempo = 1000/PROM_FOTOGRAMA;
temporizador();
function temporizador(){
crearDibujo();
window.setTimeout(temporizador,
intervaloTiempo);
}
}
canvasApp();
}
function canvasApp(){
var canvas =
document.getElementById(‘canvas’); if
(!canvas || !canvas.getContext) {
return;
}
var context = canvas.getContext(‘2d’);
if (!context) {
return;
}
crearDibujo();
//variables de nivel del objeto canvasApp var
rotacion = 0;
var x = 50;
var y = 50;
var width=30;
var height=20;
var transparencia=0;
context.globalAlpha = 1;
function crearDibujo() {
context.globalAlpha = 1;
context.font = ‘20px sans-serif’;
context.textBaseline = ‘top’;
180);
context.globalAlpha = transparencia;
//transformación
var anguloEnRadianes = rotacion * Math.PI /
180; context.save(); //salvar el actual
estado de la pila
context.setTransform(1,0,0,1,0,0);//reestable
la identidad
//Trasladar el canvas original al centro del
jugador
context.translate(x+.5*width,y+.5*height);
context.rotate(anguloEnRadianes);
context.beginPath();
context.moveTo(16-.5*width,0-.5*height);
context.lineTo(30-.5*width,19-.5*height);
context.lineTo(16-.5*width,14-.5*height);
context.moveTo(16-.5*width,14-.5*height);
context.lineTo(0-.5*width,19-.5*height);
context.lineTo(16-.5*width,0-.5*height);
context.stroke();
context.closePath();
//restaurar el contexto
context.restore(); //antiguo estado emergente
a la pantalla
transparencia=0; }
}
function temporizador(){
crearDibujo();
window.setTimeout(temporizador,
intervaloTiempo);
}
}
x = x+moverX; y = y+moverY;
Esto es un código algo feo, pero funciona muy bien si queremos que
nuestra nave del jugador este apuntando hacia arriba antes de aplicar
las transformaciones de rotación. Un método mejor es dejar a la
variable anguloEnRadianes tal como esta, pero dibujando la
nave del jugador apuntando hacia la dirección del ángulo 0 (a la
derecha). La siguiente imagen muestra cómo íbamos a sacar esto.
imagen 3.4
- Trazado que representa la nave espacial apuntando a un angulo
0
El código para dibujar la nave se modiicaría de la siguiente manera:
document.onkeydown = function(e){
e=e?e:window.event;
//ConsoleLog.log(e.keyCode + “down”);
listaTeclasPres[e.keyCode] = true;
}
document.onkeyup = function(e){
e = e?e:window.event;
//ConsoleLog.log(e.keyCode + “up”);
listaTeclasPres[e.keyCode] = false; };
if ( listaTeclasPres[38]==true){
//thrust
var anguloEnRadianes = jugador.rotacion * Math PI / 180; avanceX =
Math.cos(anguloEnRadianes);
avanceY = Math sin(anguloEnRadianes);
moverX = moverX+aceleracionAvance*avanceX; moverY =
moverY+aceleracionAvance*avanceY;
}
if ( listaTeclasPres[37]==true) {
//rotar en sentido contrario a las agujas del reloj rotacion-
=velocidadDeRotacion;
if ( listaTeclasPres[39]==true) {
//rotar en el sentido de las agujas del reloj
rotacion+=velocidadDeRotacion;
}
Una vez más vamos a escribir todo el archivo javascript completo
debido a que se han sucedido cambios muy importantes y es una
buena practica chequear que hasta ahora estemos haciendo bien los
deberes.
canvasApp();
}
function canvasApp(){
var canvas =
document.getElementById(‘canvas’); if
(!canvas || !canvas.getContext) {
return;
}
var context = canvas.getContext(‘2d’);
if (!context) { return;
}
var avanceY=0;
var moverY=0;
var width=30;
var height=20;
var velocidadDeRotacion=5; //grados que gira
la nave var aceleracionAvance=.03;
var listaTeclasPres=[];
function crearDibujo() {
if (listaTeclasPres[38]==true){
//avance
var anguloEnRadianes = rotacion * Math.PI /
180; avanceY=Math.sin(anguloEnRadianes);
moverY=moverY+aceleracionAvance*avanceY;
}
if (listaTeclasPres[37]==true) {
//rotar en direccion contraria al sentido
//de las agujas del reloj
rotacion-=velocidadDeRotacion;
}
if (listaTeclasPres[39]==true) {
//rotar en direccion al sentido de las agujas
del reloj rotacion+=velocidadDeRotacion;
}
y=y+moverY;
context.font = ‘20px sans-serif’;
context.textBaseline = ‘top’;
//transformación
var anguloEnRadianes = rotacion * Math.PI /
180; context.save(); //salvar el actual
estado de la pila
context.setTransform(1,0,0,1,0,0);
context.beginPath(); context.moveTo(-10,-15);
context.lineTo(15,0); context.lineTo(-10,15);
context.lineTo(0,0); context.moveTo(0,0);
context.lineTo(-10,-15);
context.moveTo(-10,-15);
context.stroke();
context.closePath();
//restaurar el contexto
context.restore();//antiguo estado emergente
a la pantalla }
const PROM_FOTOGRAMA = 45;
var intervaloTiempo = 1000/PROM_FOTOGRAMA;
temporizador();
function temporizador(){
crearDibujo();
window.setTimeout(temporizador,
intervaloTiempo);
}
document.onkeydown=function(e){
e=e?e:window.event;
//ConsoleLog.log(e.keyCode + “down”);
listaTeclasPres[e.keyCode]=true;
}
document.onkeyup=function(e){
//document.body.onkeyup=function(e){
e=e?e:window.event;
//ConsoleLog.log(e.keyCode + “up”);
listaTeclasPres[e.keyCode]=false;
};
}
var moverXNuevo =
moverX+aceleracionAvance*avanceX; var
moverYNuevo =
moverY+aceleracionAvance*avanceY;
La velocidad actual de nuestra nave es la raíz cuadrada de
moverXNuevo * 2 + moverYNuevo * 2
var velocidadActual = Math.sqrt
((moverXNuevo*moverXNuevo) +
(moverYNuevo*moverYNuevo));
Si la variable velocidadActual es menor que la variable velocidadMax,
coniguramos los valores moverX y moverY:
Ahora que hemos conseguido mojarnos los pies (por así decirlo),
gracias a que ya tenemos la primera impresión de lo que serán los
gráicos de juego, las transformaciones y la física básica que vamos a
usar en nuestro juego, vamos a ver como podemos estructurar un
framework simple que sirva de base para todos los juegos que
querramos crear en un canvas de HTML5. Empezaremos por crear un
grupo estático usando para ellos las constantes, seguidamente
introduciremos nuestra función temporizador de intervalos a nuestra
estructura, para inalmente crear un objeto simple reusable que muestre
la cantidad de fotogramas actuales que hay en nuestro juego para
saber que esta funcionando. Dicho esto, es hora de ponernos manos a
la obra.
• ESTADO_TITULO_DEL_JUEGO
• ESTADO_NUEVO_JUEGO
• ESTADO_NUEVO_NIVEL
• ESTADO_JUGADOR_INICIAL
• ESTADO_NIVEL_DEL_JUEGO
• ESTADO_JUGADOR_MUERTO
• ESTADO_JUEGO_TERMINADO
var estadoActualDeJuego = 0;
var funcionActualEstadoDeJuego = null;
Vamos a crear una función llamada cambioEstadoApp() que
será llamada sólo cuando queremos cambiar a un nuevo estado:
function cambioEstadoApp(nuevoEstado) {
estadoActualDeJuego = nuevoEstado;
switch (estadoActualDeJuego) {
case ESTADO_TITULO_DEL_JUEGO:
funcionActualEstadoDeJuego =
estadoTituloJuego; break;
case ESTADO_NIVEL_DEL_JUEGO:
funcionActualEstadoDeJuego =
estadoNivelJuegoApp; break;
case ESTADO_JUEGO_TERMINADO:
funcionActualEstadoDeJuego =
estadoJuegoTerminado; break;
}
}
El llamado a la funcion temporizador() inicia la aplicación mediante la
activación de la iteración de la función iniciarJuego(), mediante el uso
del método setTimeout(). Vamos a llamar a la función iniciarJuego()
repetidamente en este método setTimeout(). la función iniciarJuego()
llamará a la variable de referencia
funcionActualEstadoDeJuego en cada fotograma. Esto nos
permite cambiar fácilmente la función llamada por iniciarJuego() sobre
la base de los cambios de estado de la aplicación:
temporizador();
function temporizador() {
iniciarJuego();
window.setTimeout(temporizador,
intervaloTiempo); }
function iniciarJuego(){
funcionActualEstadoDeJuego();
}
canvasApp();
}
function canvasApp(){
var canvas =
document.getElementById(‘canvas’);
if (!canvas || !canvas.getContext) {
return;
}
if (!context) { return;
}
//estados de la aplicación
var estadoActualDeJuego=0;
var funcionActualEstadoDeJuego=null;
function cambioEstadoApp(newState) {
estadoActualDeJuego=newState;
switch (estadoActualDeJuego) {
case ESTADO_TITULO_DEL_JUEGO:
funcionActualEstadoDeJuego =
estadoTituloJuego; break;
case ESTADO_NIVEL_DEL_JUEGO:
funcionActualEstadoDeJuego =
estadoNivelJuegoApp; break;
case ESTADO_JUEGO_TERMINADO:
funcionActualEstadoDeJuego=estadoJuegoTerminad
break;
}
}
function estadoTituloJuego() {
ConsoleLog.log(“estadoTituloApp”); // dibujar
el fondo y los textos
context.font = ‘20px sans-serif’;
context.textBaseline = ‘top’;
}
function estadoNivelJuegoApp() {
ConsoleLog.log(“estadoNivelJuegoApp”); }
function estadoJuegoTerminadoApp() {
ConsoleLog.log(“estadoJuegoTerminadoApp”); }
function iniciarJuego(){
funcionActualEstadoDeJuego();
}
function temporizador() {
iniciarJuego();
window.setTimeout(temporizador,intervaloTiempo
}
}
//***** objecto prototypes *****
}
console_log=function(message) {
console.log(message);
}
}
//agregar la función clase/estatica para la
clase por asignación
ConsoleLog.log=console_log;
Nota destacada!
hemos añadido el objeto ConsoleLog para usar esta utilidad para
crear mensajes de depuración útiles en la ventana de registro
JavaScript del navegador. Esto se añadió para los navegadores que
chocaban cuando ninguna consola
Atento
estaba encendida. Sin embargo, este es un fenómeno poco frecuente
en la mayoría de los navegadores que soportan Canvas.
El ciclo actualizar/renderizar
}
function canvasApp(){
var canvas =
document.getElementById(‘canvas’);
if (!canvas || !canvas.getContext) {
return;
}
var context = canvas.getContext(‘2d’);
if (!context) {
return;
}
//variables de nivel del objeto canvasApp
var rotacion=0;
var x=50;
var y=50;
var avanceY=0;
var moverY=0;
var width=30;
var height=20;
var velocidadDeRotacion=5; //grados que gira
la nave var aceleracionAvance=.03;
var listaTeclasPres=[];
function estadoNivelJuegoApp() {
chequearTeclas();
actualizar();
renderizar();
}
function chequearTeclas() {
//chequeamos las teclas pulsadas
if (listaTeclasPres[38]==true){
//avance
var anguloEnRadianes = rotacion * Math.PI /
180;
avanceY=Math.sin(anguloEnRadianes);
moverY=moverY+aceleracionAvance*avanceY;
}
if (listaTeclasPres[37]==true) {
//rotar en contra del sentido de las agujas
del reloj
rotacion-=velocidadDeRotacion;
}
if (listaTeclasPres[39]==true) {
y=y+moverY;
}
function renderizar() {
//transformación
var anguloEnRadianes = rotacion * Math.PI /
180; context.save(); //salvar el actual
estado de la pila
context.setTransform(1,0,0,1,0,0);//reestable
la identidad
context.beginPath(); context.moveTo(-10,-15);
context.lineTo(15,0); context.lineTo(-10,15);
context.lineTo(0,0); context.moveTo(0,0);
context.lineTo(-10,-15);
context.moveTo(-10,-15);
context.stroke();
context.closePath();
//restaurar el contexto
context.restore(); //antiguo estado emergente
a la pantalla }
function iniciarJuego() {
estadoNivelJuegoApp();
}
const PROM_FOTOGRAMA = 45;
var intervaloTiempo = 1000/PROM_FOTOGRAMA;
temporizador();
e=e?e:window.event;
//ConsoleLog.log(e.keyCode + “down”);
listaTeclasPres[e.keyCode]=true;
document.onkeyup=function(e){
//document.body.onkeyup=function(e){ e=e?
e:window.event;
//ConsoleLog.log(e.keyCode + “up”);
listaTeclasPres[e.keyCode]=false;
};
}
canvasApp();
}
function canvasApp(){
var canvas =
document.getElementById(‘canvas’); if
(!canvas || !canvas.getContext) {
return;
}
var context = canvas.getContext(‘2d’);
if (!context) {
return;
}
//variables de nivel del objeto canvasApp
var rotacion=0;
var x=50;
var y=50;
var avanceY=0;
var moverY=0;
var width=30;
var height=20;
var velocidadDeRotacion=5; //grados que gira
la nave var aceleracionAvance=.03; var
listaTeclasPres=[];
function estadoNivelJuegoApp() {
chequearTeclas();
actualizar();
renderizar();
}
function chequearTeclas() {
//chequeamos las teclas pulsadas
if(listaTeclasPres[38]==true){
//avance
var anguloEnRadianes = rotacion * Math.PI /
180;
avanceY=Math.sin(anguloEnRadianes);
moverY=moverY+aceleracionAvance*avanceY;
}
if (listaTeclasPres[37]==true) {
//rotar en contra de las agujas del reloj
rotacion-=velocidadDeRotacion;
}
if (listaTeclasPres[39]==true) {
//rotar en direccion a las agujas del reloj
rotacion+=velocidadDeRotacion;
}
}
function actualizar() {
y=y+moverY;
contadorDeFotogramas.cuentaFotogramas(); }
function renderizar() {
// dibujar el fondo y el texto
context.font = ‘20px sans-serif’;
context.textBaseline = ‘top’;
contadorDeFotogramas.ultimoContadorDeFotograma
20, 570);
//transformación
var anguloEnRadianes = rotacion * Math.PI /
180; context.save(); //salvar el actual
estado de la pila
context.setTransform(1,0,0,1,0,0);//reestable
la identidad
context.beginPath(); context.moveTo(-10,-15);
context.lineTo(15,0); context.lineTo(-10,15);
context.lineTo(0,0); context.moveTo(0,0);
context.lineTo(-10,-15);
context.moveTo(-10,-15);
context.stroke();
context.closePath();
//restaurar el contexto
context.restore(); //antiguo estado emergente
a la pantalla }
function iniciarJuego() {
estadoNivelJuegoApp();
}
contadorDeFotogramas = new
ContadorDeFotogramas(); const PROM_FOTOGRAMA
= 45;
var intervaloTiempo = 1000/PROM_FOTOGRAMA;
temporizador();
function temporizador(){
iniciarJuego();
window.setTimeout(temporizador,
intervaloTiempo); }
document.onkeydown=function(e){
e=e?e:window.event;
//ConsoleLog.log(e.keyCode + “down”);
listaTeclasPres[e.keyCode]=true;
}
document.onkeyup=function(e){
//document.body.onkeyup=function(e){
e=e?e:window.event;
//ConsoleLog.log(e.keyCode + “up”);
listaTeclasPres[e.keyCode]=false;
};
}
console.log(message);
}
}
//agregar la función clase/estatica para la
clase por asignación
ConsoleLog.log=console_log;
estadoTituloJuego()
muestra los textos del titulo en la pantalla de inicio del juego y espera
que la barra de espacio sea pulsada antes de iniciar el nuevo juego.
function estadoTituloJuego() { if
(titulosIniciales !=true){ rellenarFondo();
140); titulosIniciales=true;
}else{
//esperando click en la barra de espacio if
(listaTeclasPres[32]==true){
ConsoleLog.log(“espacio presionado”);
cambioEstadoApp(ESTADO_NUEVO_JUEGO);
titulosIniciales=false;
}
}
}
estadoNuevoJuego()
conigura todos los valores predeterminados para un nuevo juego.
Todas las matrices que almacenan objetos de visualización se reinician
- el nivel de juego se resetea a 0 y la puntuación del juego vuelve a 0.
function estadoNuevoJuego(){
ConsoleLog.log(“estadoNuevoJuego”);
nivel=0;
puntuacion=0;
navesJugador=3;
jugador.velocidadMax=5;
jugador.width=30;
jugador.height=20;
jugador.widthMedio=15;
jugador.heightMedio=10;
jugador.velocidadDeRotacion=5; //cuantos
grados gira la nave
jugador.aceleracionAvance= .05;
jugador.retrasoMisilFotograma = 5;
jugador.avance = false;
rellenarFondo();
renderizarTableroPuntuacion();
cambioEstadoApp(ESTADO_NUEVO_NIVEL);
estadoNuevoNivel()
aumenta el valor del nivel en 1 y conigura los valores de “ mando del
juego” para controlar el nivel de diicultad de nuestro juego.
function estadoNuevoNivel(){
piedras=[];
platillos=[];
misilesDelJugador=[];
particulas=[];
misilesDelPlatillo=[];
nivel++;
nivelAjusteVelocidadMaxPiedra = nivel * .25;
if (nivelAjusteVelocidadMaxPiedra > 3){
nivelAjusteVelocidadMaxPiedra = 3; }
nivelPromedioDisparoPlatillo = 20 + 3 *
nivel; if (nivelPromedioDisparoPlatillo < 50)
{ nivelPromedioDisparoPlatillo = 50;
}
nivelVelocidadMisilPlatillo = 1 + .2*nivel;
if (nivelVelocidadMisilPlatillo > 4){
nivelVelocidadMisilPlatillo = 4;
}
//crear el nivel de las piedras
for (var contarNuevaPiedra = 0;
contarNuevaPiedra < nivel+3;
contarNuevaPiedra++){
var nuevaPiedra={};
nuevaPiedra.scale=1; nuevaPiedra.width=50;
nuevaPiedra.height=50;
nuevaPiedra.widthMedio=25;
nuevaPiedra.heightMedio=25;
//ConsoleLog.log(“nuevaPiedra.y=” +
nuevaPiedra.y); nuevaPiedra.dx=
(Math.random()*2)+nivelAjusteVelocidadMaxPiedr
if (Math.random() < .5){
nuevaPiedra.dx *= -1;
}
nuevaPiedra.dy=
(Math.random()*2)+nivelAjusteVelocidadMaxPiedr
if (Math.random()<.5){
nuevaPiedra.dy*=-1;
}
//velocidad de rotación y dirección
nuevaPiedra.rotacionInc=(Math.random()*5)+1;
if (Math.random()<.5){
nuevaPiedra.rotacionInc *= -1;
}
nuevaPiedra.valorPuntuacion =
puntosPiedrasGrandes; nuevaPiedra.rotacion =
0;
piedras.push(nuevaPiedra);
//ConsoleLog.log(“piedras creadas
rotacionInc=” + //nuevaPiedra.rotacionInc);
}
resetearJugador();
cambioEstadoApp(ESTADO_JUGADOR_INICIAL);
estadoJugadorInicial()
los gráicos correspondientes al jugador desaparecen en un alpha con
valores de 0 a 1. Cuando se ha completado, el juego iniciará el nivel.
function estadoJugadorInicial(){
rellenarFondo();
renderizarTableroPuntuacion();
if (jugador.alpha < 1){
jugador.alpha += .02;
context.globalAlpha = jugador.alpha; }else{
cambioEstadoApp(ESTADO_NIVEL_DEL_JUEGO); }
renderizarNaveJugador(jugador.x, jugador.y,
270, 1); context.globalAlpha=1;
actualizarPiedras();
renderizarPiedras();
}
estadoNivelDelJuego()
controla el nivel en la cual se desarrolla el juego. Esta función llama a
las funciones actualizar() y renderizar(), así como a las funciones que
evaluan los eventos y entradas del teclado para el control de la nave
del jugador.
function estadoNivelDelJuego(){
chequearTeclas();
actualizar();
renderizar();
chequearColisiones();
chequearParaNaveExtra();
chequearFinDeNivel();
contadorDeFotogramas.cuentaFotogramas();
estadoJugadorMuerto()
inicia una explosión en el lugar donde la nave del jugador ha
colisionado con una roca, un platillo volador o un misil del platillo
volador. Cuando la explosión termine (todas las partículas de la
explosión han agotado sus valores de vida individuales), da paso al
estado ESTADO_JUGADOR_ NICIAL.
function estadoJugadorMuerto(){
if (particulas.length > 0 ||
misilesDelJugador.length > 0) {
rellenarFondo();
renderizarTableroPuntuacion();
actualizarPiedras();
actualizarPlatillos();
actualizarParticulas();
actualizarMisilesDelPlatillo();
actualizarMisilesDelJugador();
renderizarPiedras();
renderizarPlatillos();
renderizarParticulas();
renderizarMisilesDelPlatillo();
renderizarMisilesJugador();
contadorDeFotogramas.cuentaFotogramas();
}else{
navesJugador--; if (navesJugador < 1) {
cambioEstadoApp(ESTADO_JUEGO_TERMINADO);
}else{
resetearJugador();
cambioEstadoApp(ESTADO_JUGADOR_INICIAL); }
}
}
estadoJuegoTerminado()
ConsoleLog.log(“espacio presionado”);
cambioEstadoApp(ESTADO_TITULO_DEL_JUEGO);
} }
}
Funciones de la aplicación
Tal cual lo dijimos con anterioridad, hay una cantidad de funciones que
son llamadas por estas funciones estadales que no han sido
declaradas, y tampoco sabemos cual es su funcionamiento y utilidad.
En la siguiente sección vamos a deinir, de la misma forma que lo hemos
hecho en la sección anterior con las funciones de estado de la
aplicación, cada una de estas funciones complementarias necesarias
para el funcionamiento de nuestra aplicación.
resetearJugador()
resetea la nave del jugador y lo traslada al centro de la pantalla de
juego preparandolo para que continue jugando.
chequearParaNaveExtra()
Comprueba si al jugador debe o no otorgarsele una nueva nave
adicional. Consulte más adelante en la sección ‘concesión de naves
adicionales a los jugadores’ para obtener más información acerca de
este algoritmo.
function chequearParaNaveExtra() {
{ navesJugador++; naveExtraGanada++;
}
}
chequearFinDeNivel()
Comprueba si todas las piedras han sido destruidas en un
determinado nivel y, si es así, inaliza el nivel actual y da comienzo a un
nuevo nivel. Consulta mas adelante la sección ‘nivel y in de juego’ para
obtener mas información sobre este algoritmo.
function chequearFinDeNivel(){
if (piedras.length==0) {
cambioEstadoApp(ESTADO_NUEVO_NIVEL); }
}
rellenarFondo()
Rellena el canvas con el color del fondo en cada uno de los
fotogramas.
function rellenarFondo() {
// dibujar el fondo y los textos
}
Conigura la base del estilo del texto antes de que este sea dibujado
en la pantalla del juego.
context.font = ‘15px sans-serif’;
context.textBaseline = ‘top’;
}
RenderizarTableroPuntuacion()
Esta función es llamada en cada uno de los fotogramas de la
aplicación. Su función es mostrar el tablero con los indicadores que
interesan al jugador, como la puntuación actualizada, el número de
naves disponibles y por último la cantidad de fotogramas por segundo
(FPS) actuales de la aplicación.
function renderizarTableroPuntuacion() {
renderizarNaveJugador(200,10,270,0.5);
- Fotogramas, 330,10);
}
chequearTeclas()
Comprueba la matriz listaTeclasPres y luego modiica el
comportamiento de la nave del jugador basada en los valores que
resulten en true como resultado de dicha comprobación.
function chequearTeclas() {
//chequear las teclas pulsadas
if (listaTeclasPres[38]==true){
//avance
var angulosEnRadianes = jugador.rotacion *
Math.PI / 180; jugador.avanceX =
Math.cos(angulosEnRadianes); jugador.avanceY
= Math.sin(angulosEnRadianes);
}
jugador.avance = true;
if (listaTeclasPres[37]==true) {
//rotar direccion contraria a las agujas del
reloj jugador.rotacion -=
jugador.velocidadDeRotacion;
if (listaTeclasPres[39]==true) {
//rotar direccion a las agujas del reloj
jugador.rotacion +=
jugador.velocidadDeRotacion;
if (listaTeclasPres[32]==true) {
//ConsoleLog.log(“jugador.cuentaMisilesPorFoto
= //“ + jugador.cuentaMisilesPorFotograma);
//ConsoleLog.log(“jugador.retrasoMisilFotogram
= //“ + jugador.retrasoMisilFotograma);
if (jugador.cuentaMisilesPorFotograma >
jugador.retrasoMisilFotograma){
misilDisparadoPorJugador();
jugador.cuentaMisilesPorFotograma = 0;
}
}
}
actualizar()
Esta función es llamada desde el ESTADO_NIVEL_DEL_JUEGO,
quien a su vez llama a cada una de las funciones actualizar()
para cada matriz de objeto mostrado en la pantalla de juego. funcion
actualizar() por cada objeto mostrado de forma individual
Cada una de las funciones que iguran a continuación actualiza a cada
uno de los objetos mostrados en la pantalla de juego. Estas funciones
(excepto actualizarJugador()) se repetirá a través de la
matriz respectiva de los objetos asociados al tipo de objeto mostrado y
actualiza los valores x e y con los valores dx y dy. La función
actualizarPlatillo() contiene la logica necesaria para
comprobar si se debe o no crear otro platillo, a la vez que chequea si
actualmente existe un platillo en la pantalla de juego, para comprobar
si el mismo debe o no disparar un misil al jugador.
• actualizarJugador()
• actualizarMisilesDelJugador()
• actualizarPiedras()
• actualizarPlatillos()
• uactualizarMisilesDelPlatillo()
• actualizarParticulas()
function actualizar() {
actualizarJugador();
actualizarMisilesDelJugador();
actualizarPiedras();
actualizarPlatillos();
actualizarMisilesDelPlatillo();
actualizarParticulas();
renderizar()
Esta función es llamada desde el ESTADO_NIVEL_DEL_JUEGO,
quien a su vez llama a la función renderizar() para cada matriz
de objeto mostrado en la pantalla de juego.
funcion actualizar() por cada objeto mostrado de forma individual
Al igual que las funciones actualizar() cada una de las
funciones que iguran a continuación, renderizan a cada uno de los
diferentes objetos mostrados en la pantalla de juego. Nuevamente con
la excepción de la función renderizarJugador() (porque solo
existe una nave del jugador), cada una de esas funciones pasará un
bucle a traves de las matrices de los objetos asociados a su tipo, y los
dibujará en la pantalla de juego. Como vimos en la elaboración de la
nave del jugador al principio de este tutorial, dibujaremos cada objeto
mediante el movimiento y el traslado del canvas al punto en la cual
queremos dibujar nuestros objetos lógicos, luego transformaremos
nuestros objetos (si es necesario) y dibujaremos los trazos en la
pantalla del juego.
• renderizarNaveJugador()
• renderizarMisilesJugador()
• renderizarPiedras()
• renderizarPlatillos()
• renderizarMisilesDelPlatillo()
• renderizarParticulas()
function renderizar() {
rellenarFondo();
renderizarTableroPuntuacion();
renderizarNaveJugador(jugador.x,jugador.y,juga
renderizarMisilesJugador();
renderizarPiedras();
renderizarPlatillos();
renderizarMisilesDelPlatillo();
renderizarParticulas();
chequearColisiones()
Pasa un bucle a traves de cada uno de los diferentes objetos
mostrados en pantalla y chequea si ha colisionado o no con otros
objetos mostrados en la pantalla de juego. Para una información mas
detallada sobre este tema, lea la sección ‘aplicación de detección de
colisiones’ en las paginas siguientes de este tutorial. A continuación el
código de esta función, no se sorprenda si es un poco largo, en
realidad es muy sencillo, pero muy potente.
function chequearColisiones() {
//Pasar un bucle a traves de las piedras y
luego a los misilies. //Siempre habrá piedras
y una nave,pero no siempre habrá misiles var
piedrasTemporales={};
var cantidadPiedras = piedras.length-1;
var misilesJugadorTemp = {};
var cantidadMisilesJugador =
misilesDelJugador.length-1; var
cantidadPlatillos = platillos.length-1;
var platillosTemp = {};
var cantidadMisilesPlatillo =
misilesDelPlatillo.length-1;
piedras:for(var
cuentaPiedras=cantidadPiedras; cuentaPiedras
>= 0; cuentaPiedras--){
piedrasTemporales = piedras[cuentaPiedras];
if
(colisionCuadroDelimitador(piedrasTemporales,
misilesJugadorTemp)){
//ConsoleLog.log(“colision con piedra”);
crearExplosion(piedrasTemporales.x +
piedrasTemporales.widthMedio,
piedrasTemporales.y +
piedrasTemporales.heightMedio, 10);
if (piedrasTemporales.scale < 3) {
dividirPiedras(piedrasTemporales.scale+1,
piedrasTemporales.x,
piedrasTemporales.y);
}
agregarAPuntuacion(piedrasTemporales.valorPun
misilesDelJugador.splice(cuentaMisilesJugador
misilesJugadorTemp = null;
piedras.splice(cuentaPiedras,1);
piedrasTemporales=null;
break piedras; break misiles; }
}
platillos:for (var cuentaPlatillos =
cantidadPlatillos; cuentaPlatillos >= 0;
cuentaPlatillos--){
platillosTemp=platillos[cuentaPlatillos];
if
(colisionCuadroDelimitador(piedrasTemporales,p
{ ConsoleLog.log(“colision con roca”);
crearExplosion(platillosTemp.x +
platillosTemp.widthMedio,
platillosTemp.y + platillosTemp.heightMedio,
10); crearExplosion(piedrasTemporales.x +
piedrasTemporales.widthMedio,
piedrasTemporales.y +
piedrasTemporales.heightMedio, 10);
if (piedrasTemporales.scale < 3) {
dividirPiedras(piedrasTemporales.scale+1,
piedras
Temporales.x, piedrasTemporales.y); }
platillos.splice(cuentaPlatillos, 1);
platillosTemp = null;
piedras.splice(cuentaPiedras, 1);
piedrasTemporales = null;
break piedras;
break platillos;
}
}
//misiles del platillo contra las piedras
//esto se hace aquí, así que no tenemos que
recorrer las rocas de //nuevo ya que
probablemente la matriz seria mayor
misilesPlatillos:for (var
cuentaMisilesPlatillo =
cantidadMisilesPlatillo;
cuentaMisilesPlatillo >= 0;
cuentaMisilesPlatillo--){
misilesPlatilloTemp =
misilesDelPlatillo[cuentaMisilesPlatillo];
if
(colisionCuadroDelimitador(piedrasTemporales,
misilesPlatilloTemp)){
ConsoleLog.log(“colision con piedra”);
crearExplosion(piedrasTemporales.x +
piedrasTemporales.widthMedio,
piedrasTemporales.y +
piedrasTemporales.heightMedio, 10);
if (piedrasTemporales.scale < 3) {
dividirPiedras(piedrasTemporales.scale + 1,
piedrasTemporales.x, piedrasTemporales.y); }
misilesDelPlatillo.splice(cuentaPlatillos,
1); misilesPlatilloTemp=null;
piedras.splice(cuentaPiedras, 1);
piedrasTemporales=null;
break piedras;
break misilesPlatillos;
}
}
//chequear jugador contra las rocas
if
(colisionCuadroDelimitador(piedrasTemporales,
jugador)){
ConsoleLog.log(“colision jugador”);
crearExplosion(piedrasTemporales.x +
piedrasTemporales.widthMedio,
piedrasTemporales.heightMedio, 10);
agregarAPuntuacion(piedrasTemporales.valorPun
if (piedrasTemporales.scale < 3) {
dividirPiedras(piedrasTemporales.scale + 1,
piedrasTemporales.x, piedrasTemporales.y);
}
piedras.splice(cuentaPiedras, 1);
piedrasTemporales = null;
jugadorMuerto(); }
}
misiles:for(var
cuentaMisilesJugador=cantidadMisilesJugador;
cuentaMisilesJugador>=0;cuentaMisilesJugador-
-){
misilesJugadorTemp=misilesDelJugador[cuentaMi
if (colisionCuadroDelimitador(platillosTemp,
misilesJugadorTemp)){
ConsoleLog.log(“colision con piedras”);
crearExplosion(platillosTemp.x +
platillosTemp.widthMedio,
platillosTemp.y + platillosTemp.heightMedio,
10);
agregarAPuntuacion(platillosTemp.valorPuntuaci
misilesDelJugador.splice(cuentaMisilesJugador
misilesJugadorTemp = null;
platillos.splice(cuentaPlatillos, 1);
platillosTemp = null;
break platillos; break misiles; }
}
platillos.splice(cuentaPiedras, 1);
platillosTemp=null;
jugadorMuerto(); }
}
//misiles del platillo contra jugador
cantidadMisilesPlatillo =
misilesDelPlatillo.length-1;
misilesPlatillos:for (var
cuentaMisilesPlatillo =
cantidadMisilesPlatillo;
cuentaMisilesPlatillo >= 0;
cuentaMisilesPlatillo--){
misilesPlatilloTemp =
misilesDelPlatillo[cuentaMisilesPlatillo];
if (colisionCuadroDelimitador(jugador,
misilesPlatilloTemp)){ ConsoleLog.log(“misil
del platillo impacta al jugador”);
jugadorMuerto();
misilesDelPlatillo.splice(cuentaPlatillos,
1); misilesPlatilloTemp = null;
break misilesPlatillos; }
}
}
misilDisparadoPorJugador()
Crea un objeto misilJugador en el centro de la nave del jugador y
disparado en la misma dirección de avance de la nave.
function misilDisparadoPorJugador(){
ConsoleLog.log(“jugador dispara misil”);
var nuevoMisilJugador={};
nuevoMisilJugador.dx=5*Math.cos(Math.PI*
(jugador.rotacion)/180);
nuevoMisilJugador.dy=5*Math.sin(Math.PI*
(jugador.rotacion)/180);
nuevoMisilJugador.x=jugador.x+jugador.widthMed
nuevoMisilJugador.y=jugador.y+jugador.heightM
nuevoMisilJugador.life=60;
nuevoMisilJugador.lifeCtr=0;
nuevoMisilJugador.width=2;
nuevoMisilJugador.height=2;
misilesDelJugador.push(nuevoMisilJugador);
misilDisparadoPorPlatillo()
Crea un objeto misilPlatillo en el centro del platillo y es disparado en la
dirección actual de la nave del jugador.
function misilDisparadoPorPlatillo(platillo)
{
var nuevoMisilPlatillo = {};
nuevoMisilPlatillo.x = platillo.x+.5 *
platillo.width; nuevoMisilPlatillo.y =
platillo.y+.5 * platillo.height;
nuevoMisilPlatillo.width=2;
nuevoMisilPlatillo.height=2;
nuevoMisilPlatillo.speed =
platillo.missileSpeed;
Math.cos(Math.PI*(grados)/180);
nuevoMisilPlatillo.dy = platillo.missileSpeed
*
Math.sin(Math.PI*(grados)/180);
nuevoMisilPlatillo.life=160;
nuevoMisilPlatillo.lifeCtr=0;
misilesDelPlatillo.push(nuevoMisilPlatillo);
}
jugadorMuerto()
crea una explosión para la nave, cuando ésta es alcanzada por un
misil del platillo o es impactada por el platillo o por una piedra,
llamando a la función crearExplosión(), cambiando de esta
manera el estado de la aplicación a ESTADO_JUGADOR_MUERTO.
function jugadorMuerto() {
ConsoleLog.log(“jugador muerto”);
crearExplosion(jugador.x+jugador.widthMedio,
jugador.y+jugador.heightMedio,50);
cambioEstadoApp(ESTADO_JUGADOR_MUERTO);
}
crearExplosion()
Esta función crea una explosión y para ello acepta tres argumentos, los
primeros indican las coordenadas de la explosión y el tercer
argumento representa la cantidad de particulas desprendidas por la
explosión.
function crearExplosion(x,y,num) {
//create 10 particles
for (var
contarParticulas=0;contarParticulas<num;contar
{
nuevaParticula.dx*=-1;
}
nuevaParticula.dy=Math.random()*3;
if (Math.random()<.5){
nuevaParticula.dy*=-1;
}
nuevaParticula.lifeCtr=0;
nuevaParticula.x=x;
nuevaParticula.y=y;
ConsoleLog.log(“nuevaParticula.life=” +
nuevaParticula.life);
particulas.push(nuevaParticula);
}
}
colisionCuadroDelimitador()
Determina si la caja rectangular que abarca la anchura y la altura de
un objeto coincide con el cuadro delimitador de otro objeto. Se
necesita de dos objetos de visualización lógicas como parámetros y
devuelve true si se las areas de los objetos coinciden y falso si no
coinciden. Vea la sección “ Aplicación de detección de colisiones” en
las siguientes páginas de este tutorial para obtener más información
acerca de esta función.
function colisionCuadroDelimitador(objeto1,
objeto2) { var izquierda1 = objeto1.x;
var izquierda2 = objeto2.x;
var derecha1 = objeto1.x + objeto1.width; var
derecha2 = objeto2.x + objeto2.width; var
superior1 = objeto1.y;
var superior2 = objeto2.y;
var inferior1 = objeto1.y + objeto1.height;
var inferior2 = objeto2.y + objeto2.height;
if (inferior1 < superior2)
return(false);
if (superior1 > inferior2)
return(false);
return(true); }
dividirPiedras()
cuando las piedras grandes son impactadas por misiles del jugador,
misiles del platillo o simplemente colicionan con el platillo o la nave del
jugador, estas se dividen en dos piedras medianas, que a su vez, si les
pasa lo mismo que a las piedras grandes, se dividen en dos piedras
pequeñas. Acepta como argumentos la escala y las coordenadas x e y
donde se creara la división.
function dividirPiedras(scale,x,y){
for (var
nuevasPiedrastr=0;nuevasPiedrastr<2;nuevasPied
{ var nuevaPiedra={};
//ConsoleLog.log(“split rock”);
if (scale==2){
nuevaPiedra.valorPuntuacion=puntosPiedrasMedia
nuevaPiedra.width=25;
nuevaPiedra.height=25;
nuevaPiedra.widthMedio=12.5;
nuevaPiedra.heightMedio=12.5;
}else {
nuevaPiedra.valorPuntuacion=puntosPiedrasPequ
nuevaPiedra.width=16;
nuevaPiedra.height=16;
nuevaPiedra.widthMedio=8;
nuevaPiedra.heightMedio=8;
nuevaPiedra.scale=scale;
nuevaPiedra.x=x;
nuevaPiedra.y=y;
nuevaPiedra.dx=Math.random()*3; if
(Math.random()<.5){
nuevaPiedra.dx*=-1;
}
nuevaPiedra.dy=Math.random()*3;
if (Math.random()<.5){
nuevaPiedra.dy*=-1;
}
nuevaPiedra.rotacionInc=(Math.random()*5)+1;
if (Math.random()<.5){
nuevaPiedra.rotacionInc*=-1;
}
nuevaPiedra.rotacion=0;
ConsoleLog.log(“nueva escala de roca”+
(nuevaPiedra.scale));
piedras.push(nuevaPiedra);
}
}
agregarAPuntuacion()
function agregarAPuntuacion(valor){
puntuacion+=valor;
}
//titulos en pantalla
var titulosIniciales=false;
//valores de puntuación
var puntosPiedrasGrandes=50; var
puntosPiedrasMedianas=75; var
puntosPiedrasPequenas=100; var
puntosPlatillos=300;
var nivelAjusteVelocidadMaxPiedra=1;
var nivelPlatilloMax = 1;
//este será multiplicado por el nivel y la
máxima puede ser en sí var
nivelPromedioAparicionPlatillo=25;
var nivelVelocidadPlatillo=1;
var nivelRetrasoDisparoPlatillo=300;
var nivelPromedioDisparoPlatillo=30;
var nivelVelocidadMisilPlatillo=1;
El objeto jugador
jugador.velocidadMax=5;
jugador.width=30;
jugador.height=20;
jugador.widthMedio=15;
jugador.heightMedio=10;
jugador.velocidadDeRotacion=5; //cuantos
grados gira la nave
jugador.aceleracionAvance= .05;
jugador.retrasoMisilFotograma = 5;
jugador.avance = false;
El objeto prototype
El uso de un objetde la clase prototype similar a
ContadorDeFotogramas puede ser implementado fácilmente por varios
de los tipos de objetos mostrados. Sin embargo, estos objetos nos
permitiría separar el código para actu
Atento
alizar y dibujar de las funciones comunes actuales y luego poner ese
código dentro de un objeto prototype individual. Hemos incluido un
prototype Piedra al final de este tutorial como ejemplo de lo que
estamos aqui discutiendo.
Usted se dará cuenta que los platillos y las piedras están dibujados
con puntos en la misma manera que esta dibujada la nave del jugador.
Piedras
nuevaPiedra.scale = 1;
nuevaPiedra.width = 50;
nuevaPiedra.height = 50;
nuevaPiedra.widthMedio = 25;
nuevaPiedra.heightMedio = 25;
nuevaPiedra.x
nuevaPiedra.y
nuevaPiedra.dx
nuevaPiedra.dy
nuevaPiedra.valorPuntuacion =
puntosPiedrasMedianas; nuevaPiedra.rotacion =
0;
Platillos
A diferencia del juego Asteroids® de Atari®, que cuenta con grandes y
pequeños platillos, vamos a tener un solo tamaño en nuestro juego
Defensa Espacial. Se almacena en la matriz de platillos.
imagen 3.4
- Trazado que representa el platillo
Los variables atributos del objeto platillo son muy similares a los
atributos de un objeto piedra, aunque sin el atributo scale de la piedra.
Los platillos tampoco tienen rotación como atributo, por lo que siempre
estará conigurada en 0. El platillo también contiene variables que se
actualizan en cada nuevo nivel para hacer el juego más desaiante
para el jugador. Estas son las variables que serán discutidas con más
detalle en la sección “ Mandos de nivel” en las próximas páginas:
nuevoPlatillo.disparosPromedios=nivelPromedioD
nuevoPlatillo.retrasoDisparo=nivelRetrasoDispa
nuevoPlatillo.cantRetrasoDisparo=0;
nuevoPlatillo.velocidadMisil=nivelVelocidadMi
Misiles
Tanto los misiles del jugador como los del platillo, estarán diseñados en
un bloque de 2x2 pixeles. Ellos serán almacenados en las matrices
misilesDelJugador y misilesDelPlatillo, respectivamente. Los objetos
son en sí muy simples. Ellos contienen suicientes atributos para
moverse a través de la pantalla de juego y para calcular los valores de
vida.
nuevoMisilJugador.dx=5*Math.cos(Math.PI*
(jugador.rotacion)/180);
nuevoMisilJugador.dy=5*Math.sin(Math.PI*
(jugador.rotacion)/180);
nuevoMisilJugador.x=jugador.x+jugador.widthMed
nuevoMisilJugador.y=jugador.y+jugador.heightM
nuevoMisilJugador.life=60;
nuevoMisilJugador.lifeCtr=0;
nuevoMisilJugador.width=2;
nuevoMisilJugador.height=2;
nuevoMisilPlatillo.x = platillo.x + .5 *
platillo.width;
nuevoMisilPlatillo.y = platillo.y + .5 *
platillo.height; nuevoMisilPlatillo.width=2;
nuevoMisilPlatillo.height=2;
nuevoMisilPlatillo.speed =
platillo.velocidadMisil;
nuevoMisilPlatillo.dx =
platillo.velocidadMisil * Math.cos(Math.PI*
(grados)/180);
nuevoMisilPlatillo.dy =
platillo.velocidadMisil * Math.sin(Math.PI*
(grados)/180);
nuevoMisilPlatillo.life=160;
nuevoMisilPlatillo.lifeCtr=0;
Explosiones y partículas
Al igual que los misiles del jugador, los objetos partículas son bastante
simples. Estos también contienen información suiciente para moverlos
por la pantalla y calcular su tiempo de vida en cada fotograma:
nuevaParticula.dx=Math.random()*3;
nuevaParticula.dy=Math.random()*3;
nuevaParticula.lifeCtr=0; nuevaParticula.x=x;
nuevaParticula.y=y;
Mandos de nivel
level ++;
function estadoJugadorMuerto(){
if (particulas.length > 0 ||
misilesDelJugador.length > 0) {
rellenarFondo();
renderizarTableroPuntuacion();
actualizarPiedras();
actualizarPlatillos();
actualizarParticulas();
actualizarMisilesDelPlatillo();
actualizarMisilesDelJugador();
renderizarPiedras();
renderizarPlatillos();
renderizarParticulas();
renderizarMisilesDelPlatillo();
renderizarMisilesJugador();
contadorDeFotogramas.cuentaFotogramas();
}else{ navesJugador--;
if (navesJugador < 1) {
cambioEstadoApp(ESTADO_JUEGO_TERMINADO);
}else{
resetearJugador();
cambioEstadoApp(ESTADO_JUGADOR_INICIAL);
}
}
}
function chequearParaNaveExtra() {
navesJugador++; naveExtraGanada++; }
}
function colisionCuadroDelimitador(objeto1,
objeto2) { var izquierda1 = objeto1.x;
var izquierda2 = objeto2.x;
var derecha1 = objeto1.x + objeto1.width; var
derecha2 = objeto2.x + objeto2.width; var
superior1 = objeto1.y;
var superior2 = objeto2.y;
var inferior1 = objeto1.y + objeto1.height;
var inferior2 = objeto2.y + objeto2.height;
return(true);
}
Tendremos que recorrer cada uno de los distintos tipos de objetos que
deben ser comprobados uno contra el otro. Pero no queremos
comprobar un objeto que fue destruido previamente contra otros
objetos. Para asegurarse de que hacemos la menor cantidad de
controles de colisión necesarias, hemos implementado una rutina que
utiliza la etiqueta y la sentencia break.
Esta es la lógica de nuestra rutina para detectar colisiones:
1. Creamos una etiqueta piedras: luego pasamos un bucle a través de
la matriz piedras. 2. Creamos una etiqueta misiles: La etiqueta debe
estar en el interior de la iteración piedras, y recorrer la matriz
misilesDelJugador.
3. Hacer un cuadro delimitador de detección de colisiones entre la
última piedra y el último misil. Tenga en cuenta que el ciclo que
comienza en el inal de cada matriz para que podamos eliminar los
elementos (cuando se producen colisiones) de la matriz sin afectar a
miembros de la matriz que no han sido chequeados todavía.
4. Si una piedra y un misil chocan, sacarlos desde sus respectivas
matrices, y luego llamar a la sentencia break en las rocas y en los
misiles. Tenemos que aplicar la sentencia break de nuevo al
siguiente elemento en una matriz para cualquier tipo de objeto que se
elimina.
5. Continuar recorriendo la matriz misiles hasta que todos hayan sido
veriicados contra la piedra actual (a menos que ya se le haya aplicado
la sentencia break cuando fue disparado y removido en una colisión
piedras/misiles).
6. Comprobar cada platillo, cada misil del platillo, y el jugador contra
cada una de las piedras. El jugador no necesita una etiqueta, porque
sólo hay una instancia jugador.
Los platillos y misilesDelPlatillo seguirán la misma
lógica que los misiles. Si se produce una colisión entre un platillo y
una piedra, aplicamos la sentencia break de nuevo a sus respectivas
etiquetas después eliminamos los objetos de sus respectivas matrices.
7. Después de haber chequeado las piedras contra todos los otros
objetos del juego, chequeamos los misilesDelJugador contra
los platillos, usando la misma lógica básica del ciclo de etiquetas,
comenzando el ciclo por el último elemento de las matrices y
aplicandoles la sentencia break nuevamente a las etiquetas cuando
hayan sido eliminados objetos de la pantalla de juego.
8. Chequear los misilesDelPlatillo contra el jugador de la
misma manera.
Con los años se ha encontrado que esta es una manera muy efectiva
de comprobar múltiples matrices de objetos, unos contra otros. Es cierto
que no es la única forma de hacerlo. Ahora bien, si usted no se siente
cómodo trabajando con las etiquetas en los bucles, puede usar un
método como el siguiente:
<head>
<title>
Defensa Espacial </title>
<style>
body {
background: #eaeaea;
}
#contenedor{
width:400px;
margin:0px auto;
padding-top:20px;
}
#canvas {
margin-left: 20px;
margin-right: 0;
margin-bottom: 20px; border: thin solid
#aaaaaa; cursor: crosshair;
}
</style>
</head>
<body>
<div id=”contenedor”>
<canvas id=’canvas’ width=’400’ height=’600’>
canvasApp();
}
function canvasApp(){
var canvas =
document.getElementById(‘canvas’); if
(!canvas || !canvas.getContext) {
return;
}
var context = canvas.getContext(‘2d’);
if (!context) { return;
}
//estados de la aplicación
const ESTADO_TITULO_DEL_JUEGO = 0;
const ESTADO_NUEVO_JUEGO = 1; const
ESTADO_NUEVO_NIVEL = 2; const
ESTADO_JUGADOR_INICIAL = 3; const
ESTADO_NIVEL_DEL_JUEGO = 4; const
ESTADO_JUGADOR_MUERTO = 5; const
ESTADO_JUEGO_TERMINADO = 6; var
estadoActualDeJuego = 0;
var funcionActualEstadoDeJuego=null;
//titulos en pantalla
var titulosIniciales=false;
//objetos del juego
//entorno de juego
var puntuacion=0;
var nivel=0;
var puntosParaNaveExtra=10000; var
naveExtraGanada=0;
var navesJugador=3;
var nivelAjusteVelocidadMaxPiedra=1;
var nivelPlatilloMax = 1;
var nivelPromedioAparicionPlatillo=25;
var nivelVelocidadPlatillo=1;
var nivelRetrasoDisparoPlatillo=300;
var nivelPromedioDisparoPlatillo=30;
var nivelVelocidadMisilPlatillo=1;
function iniciarJuego(){
funcionActualEstadoDeJuego();
}
function cambioEstadoApp(nuevoEstado)
{ estadoActualDeJuego = nuevoEstado;
switch (estadoActualDeJuego) { break;
caseESTADO_TITULO_DEL_JUEGO:
funcionActualEstadoDeJuego=
estadoTituloJuego; break;
case ESTADO_NUEVO_JUEGO:
funcionActualEstadoDeJuego =
estadoNuevoJuego; break;
case ESTADO_NUEVO_NIVEL:
funcionActualEstadoDeJuego =
estadoNuevoNivel; break;
caseESTADO_JUGADOR_INICIAL:
funcionActualEstadoDeJuego =
estadoJugadorInicial; break;
case ESTADO_NIVEL_DEL_JUEGO:
funcionActualEstadoDeJuego =
estadoNivelDelJuego; break;
case ESTADO_JUGADOR_MUERTO:
funcionActualEstadoDeJuego =
estadoJugadorMuerto;
case ESTADO_JUEGO_TERMINADO:
funcionActualEstadoDeJuego =
estadoJuegoTerminado; break;
}
}
function estadoTituloJuego() { if
(titulosIniciales !=true){
rellenarFondo();
titulosIniciales=true; }else{
//esperando click en la barra de
espacio
if (listaTeclasPres[32]==true){
ConsoleLog.log(“espacio presionado”);
cambioEstadoApp(ESTADO_NUEVO_JUEGO);
titulosIniciales=false;
}
}
}
function estadoNuevoJuego(){
ConsoleLog.log(“estadoNuevoJuego”);
nivel=0;
puntuacion=0;
navesJugador=3;
jugador.velocidadMax=5;
jugador.width=30;
jugador.height=20;
jugador.widthMedio=15;
jugador.heightMedio=10;
jugador.velocidadDeRotacion=5;
//cuantos grados gira la nave
jugador.aceleracionAvance= .05;
jugador.retrasoMisilFotograma = 5;
jugador.avance = false;
rellenarFondo();
renderizarTableroPuntuacion();
cambioEstadoApp(ESTADO_NUEVO_NIVEL);
nivelAjusteVelocidadMaxPiedra = 3; }
if (nivelPlatilloMax > 5){
nivelPlatilloMax = 5;
}
nivelPromedioAparicionPlatillo = 10 +
3 * nivel;
if (nivelPromedioAparicionPlatillo >
35){
nivelPromedioAparicionPlatillo=35;
}
nivelVelocidadPlatillo = 1 + .5 *
nivel;
if (nivelVelocidadPlatillo > 5){
nivelVelocidadPlatillo = 5;
}
nivelRetrasoDisparoPlatillo = 120 - 10
* nivel;
if (nivelRetrasoDisparoPlatillo < 20)
{ nivelRetrasoDisparoPlatillo = 20;
}
nivelPromedioDisparoPlatillo = 20 + 3
* nivel; if
(nivelPromedioDisparoPlatillo < 50) {
nivelPromedioDisparoPlatillo = 50; }
nivelVelocidadMisilPlatillo = 1 +
.2*nivel;
if (nivelVelocidadMisilPlatillo > 4){
nivelVelocidadMisilPlatillo = 4;
}
//crear el nivel de las piedras
for (var contarNuevaPiedra = 0;
contarNuevaPiedra < nivel+3;
contarNuevaPiedra++){
varnuevaPiedra={};
nuevaPiedra.scale=1;
//scale
//1=large
//2=medium
//3=small
//estos se pueden usar como, el
divisor para el nuevo tamaño //50/1=50
//50/2=25
//50/3=16
nuevaPiedra.width=50;
nuevaPiedra.height=50;
nuevaPiedra.widthMedio=25;
nuevaPiedra.heightMedio=25;
//ConsoleLog.log(“nuevaPiedra.y=” +
nuevaPiedra.y); nuevaPiedra.dx=
(Math.random()*2)+nivelAjusteVelocidadM
if (Math.random() < .5){
nuevaPiedra.dx *= -1;
}
nuevaPiedra.dy=
(Math.random()*2)+nivelAjusteVelocidadM
if (Math.random()<.5){
nuevaPiedra.dy*=-1;
}
//velocidad de rotación y dirección
nuevaPiedra.rotacionInc=
(Math.random()*5)+1;
if (Math.random()<.5){
nuevaPiedra.rotacionInc *= -1;
}
nuevaPiedra.valorPuntuacion =
puntosPiedrasGrandes;
nuevaPiedra.rotacion = 0;
piedras.push(nuevaPiedra);
//ConsoleLog.log(“piedras creadas
rotacionInc=” +
nuevaPiedra.rotacionInc);
}
resetearJugador();
cambioEstadoApp(ESTADO_JUGADOR_INICIAL)
}
function estadoJugadorInicial(){
rellenarFondo();
renderizarTableroPuntuacion();
if (jugador.alpha < 1){
jugador.alpha += .02;
context.globalAlpha = jugador.alpha;
}else{
cambioEstadoApp(ESTADO_NIVEL_DEL_JUEGO)
}
renderizarNaveJugador(jugador.x,
jugador.y, 270, 1);
context.globalAlpha=1;
actualizarPiedras();
renderizarPiedras();
} chequearFinDeNivel();
contadorDeFotogramas.cuentaFotogramas()
function estadoNivelDelJuego(){
chequearTeclas();
actualizar();
renderizar();
chequearColisiones();
chequearParaNaveExtra();
function resetearJugador() {
jugador.rotacion = 270; jugador.x = .5
* xMax; jugador.y = .5 * yMax;
jugador.avanceY = 0;
jugador.moverY = 0;
jugador.alpha = 0;
jugador.cuentaMisilesPorFotograma=0;
}
function chequearParaNaveExtra() {
navesJugador++; naveExtraGanada++; }
}
function chequearFinDeNivel(){
if (piedras.length==0) {
cambioEstadoApp(ESTADO_NUEVO_NIVEL); }
}
function estadoJugadorMuerto(){
if (particulas.length > 0 ||
misilesDelJugador.length > 0) {
rellenarFondo();
renderizarTableroPuntuacion();
actualizarPiedras();
actualizarPlatillos();
actualizarParticulas();
actualizarMisilesDelPlatillo();
actualizarMisilesDelJugador();
renderizarPiedras();
renderizarPlatillos();
renderizarParticulas();
renderizarMisilesDelPlatillo();
renderizarMisilesJugador();
contadorDeFotogramas.cuentaFotogramas()
}else{ navesJugador--;
if (navesJugador < 1) {
cambioEstadoApp(ESTADO_JUEGO_TERMINADO)
}else{
resetearJugador();
cambioEstadoApp(ESTADO_JUGADOR_INICIAL)
} }
function estadoJuegoTerminado() {
ConsoleLog.log(“Estado juego
terminado”);
rellenarFondo();
renderizarTableroPuntuacion();
ConsoleLog.log(“espacio presionado”);
cambioEstadoApp(ESTADO_TITULO_DEL_JUEGO
} }
}
function rellenarFondo() {
// dibujar el fondo y los textos
}
context.font = ‘15px sans-serif’;
context.textBaseline = ‘top’;
}
function renderizarTableroPuntuacion()
{
renderizarNaveJugador(200,10,270,0.5);
contadorDeFotogramas.ultimoContadorDeFo
}
function chequearTeclas() { //chequear
las teclas pulsadas
if (listaTeclasPres[38]==true){
//avance
var angulosEnRadianes =
jugador.rotacion * Math.PI / 180;
jugador.avanceY =
Math.sin(angulosEnRadianes);
var moverYNuevo = jugador.moverY +
jugador.aceleracionAvance *
jugador.avanceY;
(moverYNuevo*moverYNuevo));
if (velocidadActual <
jugador.velocidadMax) {
jugador.moverY = moverYNuevo; }
jugador.avance = true;
}else{
jugador.avance = false;
}
if (listaTeclasPres[37]==true) {
//rotar direccion contraria a las
agujas del reloj jugador.rotacion -=
jugador.velocidadDeRotacion;
if (listaTeclasPres[39]==true) {
//rotar direccion a las agujas del
reloj jugador.rotacion +=
jugador.velocidadDeRotacion;
if (listaTeclasPres[32]==true) {
//ConsoleLog.log(“jugador.cuentaMisiles
= “ +
//jugador.cuentaMisilesPorFotograma);
//ConsoleLog.log(“jugador.retrasoMisilF
= “ +
//jugador.retrasoMisilFotograma);
if (jugador.cuentaMisilesPorFotograma
> jugador.retrasoMisilFotograma){
misilDisparadoPorJugador();
jugador.cuentaMisilesPorFotograma = 0;
}
}
}
function actualizar() {
actualizarJugador();
actualizarMisilesDelJugador();
actualizarPiedras();
actualizarPlatillos();
actualizarMisilesDelPlatillo();
actualizarParticulas();
function renderizar() {
rellenarFondo();
renderizarTableroPuntuacion();
renderizarNaveJugador(jugador.x,jugador
renderizarMisilesJugador();
renderizarPiedras();
renderizarPlatillos();
renderizarMisilesDelPlatillo();
renderizarParticulas();
function actualizarJugador() {
jugador.cuentaMisilesPorFotograma++;
jugador.y+=jugador.moverY;
function actualizarMisilesDelJugador()
{
var misilesJugadorTemp={};
var
cantMisilesJugador=misilesDelJugador.le
1;
ConsoleLog.log(“actualizar cantidad de
misiles =” + cantMisilesJugador); for
(var
contarMisilesJugador=cantMisilesJugador
contarMisilesJugador--){
misilesJugadorTemp.lifeCtr++;
if (misilesJugadorTemp.lifeCtr >
misilesJugadorTemp.life){
ConsoleLog.log(“removermisiles del
jugador”);
misilesDelJugador.splice(contarMisilesJ
misilesJugadorTemp=null;
}
}
}
var piedrasTemp={};
var cantidadPiedras=piedras.length-1;
ConsoleLog.log(“actualizar cantidad de
piedras =” + cantidadPiedras);
piedrasTemp.x=xMin-piedrasTemp.width;
}else if (piedrasTemp.x<xMin-
piedrasTemp.width){
piedrasTemp.x=xMax;
}
if (piedrasTemp.y > yMax) {
piedrasTemp.y=yMin-piedrasTemp.width;
}else if (piedrasTemp.y<yMin-
piedrasTemp.width){
piedrasTemp.y=yMax;
}
ConsoleLog.log(“actualizar piedras “+
contarPiedras); }
}
function actualizarPlatillos() {
//lo primero es chequear para saber si
queremos un platillo
if (platillos.length<
nivelPlatilloMax){
<= nivelPromedioAparicionPlatillo){
ConsoleLog.log(“crear platillo”); var
nuevoPlatillo={};
nuevoPlatillo.width=28;
nuevoPlatillo.height=13;
nuevoPlatillo.heightMedio=6.5;
nuevoPlatillo.widthMedio=14;
nuevoPlatillo.valorPuntuacion=puntosPla
nuevoPlatillo.disparosPromedios =
nivelPromedioDisparoPlatillo;
nuevoPlatillo.retrasoDisparo=
nivelRetrasoDisparoPlatillo;
nuevoPlatillo.cuentaDisparosRetrasos=0;
nuevoPlatillo.velocidadMisil =
nivelVelocidadMisilPlatillo;
nuevoPlatillo.dy=(Math.random()*2);
nuevoPlatillo.dy*=-1; }
//seleccionar entre los extremos
derecho e izquierdo para empezar
//comenzar en la derecha e ir a la
izquierda nuevoPlatillo.x=450;
nuevoPlatillo.dx=-1*nivelVelocidadPlati
nuevoPlatillo.velocidadMisil=
nivelVelocidadMisilPlatillo;
nuevoPlatillo.retrasoDisparo=
nivelRetrasoDisparoPlatillo;
nuevoPlatillo.disparosPromedios=
nivelPromedioDisparoPlatillo;
platillos.push(nuevoPlatillo); }
}
cantidadPlatillos);
for (var
contarPlatillos=cantidadPlatillos;conta
contarPlatillos--){
platilloTemp =
platillos[contarPlatillos];
platilloTemp.disparosPromedios &&
platilloTemp.cuentaDisparosRetrasos >
platilloTemp.retrasoDisparo ){
misilDisparadoPorPlatillo(platilloTemp)
platilloTemp.cuentaDisparosRetrasos=
0;
}
var remover = false;
platilloTemp.x += platilloTemp.dx;
platilloTemp.y += platilloTemp.dy;
if (remover==true) {
//remover el platillos
ConsoleLog.log(“platillo removido”);
platillos.splice(contarPlatillos,1);
platilloTemp=null;
}
}
}
function
actualizarMisilesDelPlatillo() {
var misilesDelPlatilloTemp={};
var cantidadMisilesDelPlatillo =
misilesDelPlatillo.length-1; for (var
contarMisilesDelPlatillo =
cantidadMisilesDelPlatillo;
contarMisilesDelPlatillo >= 0;
contarMisilesDelPlatillo--){
}
misilesDelPlatilloTemp.lifeCtr++;
if (misilesDelPlatilloTemp.lifeCtr >
misilesDelPlatilloTemp.life){
//remover
misilesDelPlatillo.splice(contarMisiles
1); misilesDelPlatilloTemp=null;
}
}
}
var remover = false;
particulasTemp =
particulas[contarParticulas];
particulasTemp.x += particulasTemp.dx;
particulasTemp.y += particulasTemp.dy;
function actualizarParticulas() {
var particulasTemp={};
var cantidadParticulas =
particulas.length-1;
ConsoleLog.log(“particulas =” +
cantidadParticulas);
for (var contarParticulas =
cantidadParticulas; contarParticulas
>= 0;
contarParticulas--){
particulasTemp.lifeCtr++;
ConsoleLog.log(“particulas.lifeCtr=” +
particulasTemp.lifeCtr);
try{ if(particulasTemp.lifeCtr >
particulasTemp.life){ remover=true;
remover=true;
}
}
catch(err){
ConsoleLog.log (“error en
particulas”);
ConsoleLog.log(“particulas:” +
contarParticulas);
}
if (remover) {
particulas.splice(contarParticulas,1);
particulasTemp=null;
}
} }
function
renderizarNaveJugador(x,y,rotacion,
scale) {
//transformación
var angulosEnRadianes = rotacion *
Math.PI / 180; context.save();
//salvar el estado actual en la pila
context.setTransform(1,0,0,1,0,0); //
resetear la identidad
//mover el canvas original al centro
del jugador
context.translate(x +
jugador.widthMedio,
y+jugador.heightMedio);
context.rotate(angulosEnRadianes);
context.scale(scale,scale);
//dibujarNave
context.beginPath();
context.moveTo(-10,-15);
context.lineTo(15,0);
context.lineTo(-10,15);
context.lineTo(0,0);
context.closePath();
//restaurar el contexto
context.restore(); //estado anterior a
la pantalla }
function renderizarMisilesJugador() {
var misilesJugadorTemp = {};
var cantMisilesJugador =
misilesDelJugador.length-1;
ConsoleLog.log(“renderizar
cantMisilesJugador=” +
cantMisilesJugador); for (var
contarMisilesJugador =
cantMisilesJugador;
contarMisilesJugador >= 0;
contarMisilesJugador--){
//ConsoleLog.log(“dibujar misil del
jugador “ +
//contarMisilesJugador);
misilesJugadorTemp =
misilesDelJugador[contarMisilesJugador]
context.save(); //salvar el estado
actual en la pila
context.setTransform(1,0,0,1,0,0); //
resetear la identidad
context.stroke();
context.closePath();
context.restore(); //recuperar viejo
estado de la pantalla
}
}
contarPiedras--){
piedrasTemp = piedras[contarPiedras];
var angulosEnRadianes =
piedrasTemp.rotacion * Math.PI / 180;
ConsoleLog.log(“renderizar rotacion de
las piedras” +
function renderizarPiedras() {
var piedrasTemp = {};
var cantPiedras=piedras.length-1;
for (var contarPiedras = cantPiedras;
contarPiedras >= 0;
(piedrasTemp.rotacion));
context.save(); //salvar el estado
actual en la pila
context.setTransform(1,0,0,1,0,0); //
resetear la identidad
context.beginPath();
context.moveTo(-
(piedrasTemp.widthMedio-1),
(piedrasTemp.heightMedio-1));
context.lineTo((piedrasTemp.widthMedio-
1), (piedrasTemp.heightMedio-1));
context.lineTo((piedrasTemp.widthMedio-
1), (piedrasTemp.heightMedio-1));
context.lineTo(-
(piedrasTemp.widthMedio-1),
(piedrasTemp.heightMedio-1));
context.lineTo(-
(piedrasTemp.widthMedio-1),
(piedrasTemp.heightMedio-1));
context.stroke();
context.closePath();
context.restore(); //restaurar el
estado antiguo en la pantalla }
}
function renderizarPlatillos() {
varplatilloTemp={};
var cantidadPlatillos =
platillos.length-1;
for (var contarPlatillos =
cantidadPlatillos; contarPlatillos >=
0;
contarPlatillos--){
ConsoleLog.log(“platillos: “ +
contarPlatillos); platilloTemp =
platillos[contarPlatillos];
context.moveTo(4,0);
context.lineTo(9,0);
context.lineTo(12,3);
context.lineTo(13,3);
context.moveTo(13,4);
context.lineTo(10,7);
context.lineTo(3,7);
context.lineTo(1,5);
context.lineTo(12,5);
context.moveTo(0,4);
context.lineTo(0,3);
context.lineTo(13,3);
context.moveTo(5,1);
context.lineTo(5,2);
context.moveTo(8,1);
context.lineTo(8,2);
context.moveTo(2,2);
context.lineTo(4,0);
context.stroke();
context.closePath();
context.restore(); //recuperar el
viejo estado de la pantalla }
}
function
renderizarMisilesDelPlatillo() {
var misilesDelPlatilloTemp = {};
var cantMisilesPlatillo =
misilesDelPlatillo.length-1;
//ConsoleLog.log(“misilesDelPlatillo =
“ + misilesDelPlatillo.length); for
(var contarMisilesPlatillo =
cantMisilesPlatillo;
contarMisilesPlatillo >= 0;
contarMisilesPlatillo--){
//ConsoleLog.log(“dibujar misiles del
platillo “ + cantMisilesPlatillo);
misilesDelPlatilloTemp=
misilesDelPlatillo[contarMisilesPlatill
context.save(); //salvar el estado
actual en la pila
context.setTransform(1,0,0,1,0,0); //
resetear la identidad
context.beginPath();
context.lineTo(-1,1);
context.lineTo(-1,-1);
context.stroke();
context.closePath();
context.restore(); //restaurar el
estado anterior en la pantalla
} }
function renderizarParticulas() {
var particulasTemp={};
var
cantidadParticulas=particulas.length-
1;
for (var contarParticula =
cantidadParticulas; contarParticula >=
0;
contarParticula--){
particulasTemp =
particulas[contarParticula];
context.save(); //save current state
in stack
context.setTransform(1,0,0,1,0,0); //
reset to identity
context.beginPath();
context.moveTo(0,0);
context.lineTo(1,1);
context.stroke();
context.closePath();
context.restore(); //pop old state on
to screen
}
}
function chequearColisiones() {
//Pasar un bucle a traves de las
piedras y luego a los misilies. Siem
var piedrasTemporales={};
var cantidadPiedras = piedras.length-
1;
var misilesJugadorTemp = {};
var cantidadMisilesJugador =
misilesDelJugador.length-1; var
cantidadPlatillos = platillos.length-
1;
var platillosTemp = {};
var cantidadMisilesPlatillo =
misilesDelPlatillo.length-1;
piedrasTemporales =
piedras[cuentaPiedras];
if
(colisionCuadroDelimitador(piedrasTempo
misilesJugadorTemp)){
//ConsoleLog.log(“colision con
piedra”);
crearExplosion(piedrasTemporales.x +
piedrasTemporales.widthMedio,
piedrasTemporales.y
+ piedrasTemporales.heightMedio, 10);
if (piedrasTemporales.scale < 3) {
dividirPiedras(piedrasTemporales.scale+
piedrasTemporales.x,
piedrasTemporales.y);
}
agregarAPuntuacion(piedrasTemporales.va
misilesDelJugador.splice(cuentaMisilesJ
misilesJugadorTemp = null;
piedras.splice(cuentaPiedras, 1);
piedrasTemporales=null;
break piedras; break misiles; }
}
if
(colisionCuadroDelimitador(piedrasTempo
platillosTemp)){
ConsoleLog.log(“colision con roca”);
crearExplosion(platillosTemp.x +
platillosTemp.widthMedio,
platillosTemp.y +
platillosTemp.heightMedio, 10);
crearExplosion(piedrasTemporales.x +
piedrasTemporales.widthMedio,piedrasTem
+ piedrasTemporales.heightMedio, 10);
if (piedrasTemporales.scale < 3) {
dividirPiedras(piedrasTemporales.scale+
piedrasTemporales.x,
piedrasTemporales.y);
}
platillos.splice(cuentaPlatillos, 1);
platillosTemp = null;
break piedras;
break platillos;
piedras.splice(cuentaPiedras, 1);
piedrasTemporales = null;
}
}
//misiles del platillo contra las
piedras
//esto se hace aquí, así que no
tenemos que recorrer las rocas //de
nuevo ya que probablemente la matriz
seria mayor misilesPlatillos:for (var
cuentaMisilesPlatillo =
cantidadMisilesPlatillo;
cuentaMisilesPlatillo >= 0;
cuentaMisilesPlatillo--){
misilesPlatilloTemp =
misilesDelPlatillo[cuentaMisilesPlatill
if
(colisionCuadroDelimitador(piedrasTempo
misilesPlatilloTemp)){
ConsoleLog.log(“colision con piedra”);
crearExplosion(piedrasTemporales.x +
piedrasTemporales.widthMedio,piedrasTem
+ piedrasTemporales.heightMedio, 10);
if (piedrasTemporales.scale < 3) {
dividirPiedras(piedrasTemporales.scale
+ 1,
piedrasTemporales.x,piedrasTemporales.y
}
misilesDelPlatillo.splice(cuentaPlatill
1); misilesPlatilloTemp=null;
piedras.splice(cuentaPiedras, 1);
piedrasTemporales=null;
breakpiedras;
break misilesPlatillos; }
}
//chequear jugador contra las rocas
if(colisionCuadroDelimitador(piedrasTem
jugador)){ ConsoleLog.log(“colision
jugador”);
crearExplosion(piedrasTemporales.x +
piedrasTemporales.widthMedio,
piedrasTemporales.heightMedio, 10);
agregarAPuntuacion(piedrasTemporales.va
if (piedrasTemporales.scale < 3) {
dividirPiedras(piedrasTemporales.scale+
1,
piedrasTemporales.x,piedrasTemporales.y
}
piedras.splice(cuentaPiedras, 1);
piedrasTemporales = null;
//Ahora compruebe jugador contra
platillos y luego platillos contra los
//misiles de jugadores y por último
jugador contra misiles platillo
cantidadMisilesJugador =
misilesDelJugador.length-1;
cantidadPlatillos = platillos.length-
1;
platillos:for (var cuentaPlatillos =
cantidadPlatillos;
cuentaPlatillos >= 0; cuentaPlatillos-
-){
platillosTemp=platillos[cuentaPlatillos
jugadorMuerto(); }
misiles:for (var
cuentaMisilesJugador=cantidadMisilesJug
cuentaMisilesJugador>=0;cuentaMisilesJu
-){
misilesJugadorTemp=misilesDelJugador[cu
if
(colisionCuadroDelimitador(platillosTem
misilesJugadorTemp)){
ConsoleLog.log(“colisioncon piedras”);
crearExplosion(platillosTemp.x +
platillosTemp.widthMedio,
platillosTemp.y +
platillosTemp.heightMedio, 10);
agregarAPuntuacion(platillosTemp.valorP
misilesDelJugador.splice(cuentaMisilesJ
misilesJugadorTemp = null;
platillos.splice(cuentaPlatillos,1);
platillosTemp = null;
break platillos; break misiles; }
}
platillos.splice(cuentaPiedras,1);
platillosTemp=null;
jugadorMuerto(); }
}
//misiles del platillo contra jugador
cantidadMisilesPlatillo =
misilesDelPlatillo.length-1;
misilesPlatillos:for (var
cuentaMisilesPlatillo =
cantidadMisilesPlatillo;
cuentaMisilesPlatillo >= 0;
cuentaMisilesPlatillo--){
jugadorMuerto();
misilesDelPlatillo.splice(cuentaPlatill
1); misilesPlatilloTemp = null;
misilesPlatilloTemp =
misilesDelPlatillo[cuentaMisilesPlatill
if (colisionCuadroDelimitador(jugador,
misilesPlatilloTemp)){
ConsoleLog.log(“misil del platillo
impacta al jugador”);
break misilesPlatillos; }
}
}
function misilDisparadoPorJugador(){
ConsoleLog.log(“jugador dispara
misil”);
var nuevoMisilJugador={};
nuevoMisilJugador.dx=5*Math.cos(Math.PI
(jugador.rotacion)/180);
nuevoMisilJugador.dy=5*Math.sin(Math.PI
(jugador.rotacion)/180);
nuevoMisilJugador.x=jugador.x+jugador.w
nuevoMisilJugador.y=jugador.y+jugador.h
nuevoMisilJugador.life=60;
nuevoMisilJugador.lifeCtr=0;
nuevoMisilJugador.width=2;
nuevoMisilJugador.height=2;
misilesDelJugador.push(nuevoMisilJugado
function
misilDisparadoPorPlatillo(platillo) {
var nuevoMisilPlatillo = {};
nuevoMisilPlatillo.x = platillo.x + .5
* platillo.width; nuevoMisilPlatillo.y
= platillo.y + .5 * platillo.height;
nuevoMisilPlatillo.width=2;
nuevoMisilPlatillo.height=2;
nuevoMisilPlatillo.speed =
platillo.velocidadMisil;
ConsoleLog.log(“disparo del
platillo”); //fuego al jugador desde
pequeño platillo
var grados = 360 * radianes / (2 *
Math.PI); nuevoMisilPlatillo.dx =
platillo.velocidadMisil *
Math.cos(Math.PI*(grados)/180);
nuevoMisilPlatillo.dy =
platillo.velocidadMisil *
Math.sin(Math.PI*(grados)/180);
nuevoMisilPlatillo.life=160;
nuevoMisilPlatillo.lifeCtr=0;
misilesDelPlatillo.push(nuevoMisilPlati
}
function crearExplosion(x,y,num) {
//crear 10 particulas
for (var
contarParticulas=0;contarParticulas<num
{ var nuevaParticula=new Object();
nuevaParticula.dx=Math.random()*3;
if (Math.random()<.5){
nuevaParticula.dx*=-1;
}
nuevaParticula.dy=Math.random()*3;
if (Math.random()<.5){
nuevaParticula.dy*=-1;
}
function jugadorMuerto() {
ConsoleLog.log(“jugador muerto”);
crearExplosion(jugador.x+jugador.widthM
jugador.y+jugador.heightMedio,50);
cambioEstadoApp(ESTADO_JUGADOR_MUERTO);
nuevaParticula.lifeCtr=0;
nuevaParticula.x=x;
nuevaParticula.y=y;
ConsoleLog.log(“nuevaParticula.life=”
+ nuevaParticula.life);
particulas.push(nuevaParticula);
}
}
function
colisionCuadroDelimitador(objeto1,
objeto2) {
return(true);
}
function dividirPiedras(scale,x,y){
for (var
contarNuevasPiedras=0;contarNuevasPiedr
{ var nuevaPiedra={};
ConsoleLog.log(“dividir piedras”);
if (scale==2){
nuevaPiedra.valorPuntuacion=puntosPiedr
nuevaPiedra.width=25;
nuevaPiedra.height=25;
nuevaPiedra.widthMedio=12.5;
nuevaPiedra.heightMedio=12.5;
}else {
nuevaPiedra.valorPuntuacion=puntosPiedr
nuevaPiedra.width=16;
nuevaPiedra.height=16;
nuevaPiedra.widthMedio=8;
nuevaPiedra.heightMedio=8;
nuevaPiedra.scale=scale;
nuevaPiedra.x=x;
nuevaPiedra.y=y;
nuevaPiedra.dx=Math.random()*3;
if(Math.random()<.5){
nuevaPiedra.dx*=-1;
}
nuevaPiedra.dy=Math.random()*3;
if(Math.random()<.5){
nuevaPiedra.dy*=-1;
}
nuevaPiedra.rotacionInc=
(Math.random()*5)+1;
if (Math.random()<.5){
nuevaPiedra.rotacionInc*=-1;
}
nuevaPiedra.rotacion=0;
ConsoleLog.log(“nueva escala de roca”+
(nuevaPiedra.scale));
piedras.push(nuevaPiedra);
}
}
function agregarAPuntuacion(valor){
puntuacion+=valor;
}
document.onkeydown=function(e){
e=e?e:window.event;
ConsoleLog.log(e.keyCode + “down”);
listaTeclasPres[e.keyCode]=true;
}
//*** inicio de la aplicación
cambioEstadoApp(ESTADO_TITULO_DEL_JUEGO
document.onkeyup=function(e){
//document.body.onkeyup=function(e){
e=e?e:window.event;
ConsoleLog.log(e.keyCode + “up”);
listaTeclasPres[e.keyCode]=false;
};
function temporizador(){
iniciarJuego();
window.setTimeout(temporizador,
intervaloTiempo);
}
}
//***** objectos prototype *****
}
console_log=function(message) {
console.log(message);
}
}
//agregar la función clase/estatica
para la clase por asignación
ConsoleLog.log=console_log;
Bomomo(www.bomomo.com)
Canvas Cycle(www.effectgames.com/demos/canvascycle)
Descripción
El Canvas Cycle App muestra una serie de escenas en movimiento,
como el agua y la nieve. Es una buena manera de obtener
perspectivas rápida de la variedad de fondos que pueden ser creados
usando canvas.
imagen 4.2 - aplicación canvas cycle
Chrome Experiments(www.chromeexperiments.com)
Descripción
Google patrocina un sitio web que contiene una colección de algunos
de los mejores
Grow a face(www.growaface.com)
Descripción
¿Por qué alguien querría hacer crecer una cara? Hay un montón de
razones: tener un poco de diversión, obtener algunas ideas para los
gráicos, y obtener ayuda para crear caras que podría utilizar en sus
propias aplicaciones.
imagen 4.4 - aplicación divertida acerca de rostros y sus
cambios
Pocket Full of
Canvas(www.nihilogic.dk/labs/pocket_full_of_canvas/#pres
Audacity(http://audacity.sourceforge.net)
Descripción
Hasta que todos los navegadores sean compatibles con todo los tipo
de archivos de
audio, usted debe, por regla general, incluir varias versiones de sus
archivos de audio en las aplicaciones que construya. Audacity es una
herramienta de sotware libre, de código abierto multiplataforma para
convertir un archivo de audio en varios formatos, incluyendo MP3, OGG
y WAV.
imagen 4.9 - Audacity, aplicación muy útil para convertir formato
de audio.
Can I Use(http://www.caniuse.com)
EaselJS(www.createjs.com/#!/EaselJS)
Descripción
EaselJS es una serie de bibliotecas de JavaScript que se pueden
utilizar para ayudar a simpliicar el desarrollo de Canvas en JavaScript.
Electrotank(www.electrotank.com)
Descripción
Electrotank proporciona una serie de productos de sotware que
apoyan el desarrollo de juegos sociales multijugador de escritorio y
dispositivos móviles.
Firebug(http://getfirebug.com/)
Gamepad API(https://wiki.mozilla.org/GamepadAPI)
HTML5 Test(http://html5test.com)
Kuler(https://kuler.adobe.com/)
Descripción El sitio web de Kuler es una herramienta de Adobe para el
desarrollo de paletas de colores y experimentar con combinaciones de
colores.
WebGL(http://www.khronos.org/webgl/)
Descripción
HTML5 Canvas no soporta actualmente un contexto integrado, 3D.
WebGL es una plataforma-cruzada web estándar de rayaltee-free para
una API de gráicos 3D. WebGL está creciendo como el estándar
indicativo para el 3D Canvas.
Apéndice A
Este apendice provee información básica sobre el uso de color en la
web. La referencia no solo cubre los formatos de colores deinidos,
nombres y valores para la especiicación X(HTML) y CSS, pero presenta
los nombres de colores menos evidentes estandarizados pero de uso
común.
Colores (X)HTML
Nota destacada!
Los nombres y los valores de los colores no son sensitivos a las
mayusculas o minusculas, es decir un color red y RED son
equivalentes, asi como lo son #FF0000 y #ff0000.
Atento
tabla A!
- Nombre de colores y sus equivalencias hexadecimales
estandard HTML 4.0
imagen B.1
- Hoja de mosaicos nave.png
A continuación mostraremos un segundo conjunto de cuadros para la
nave con las turbinas encendidas listas para avanzar. Usaremos esta
imagen para representar la nave del jugador cuando el jugador
presiona la tecla hacia arriba del teclado, lo que traducimos en nuestro
juego como avanzar o acelerar.
imagen B.2
- Hoja de mosaicos nave2.png que representa la nave acelerada
Para representar las piedras que el jugador debe esquivar o destruir
tenemos los siguientes tres conjuntos de imagenes de hojas de
mosaicos, las cuales cada una describe el tamaño de roca, dado que
tenemos tres tamaños, ellas son grandes, medianas y pequeñas.
function renderizarNaveJugador(x,y,rotacion,
scale) { //transformacion
context.save(); //salvar actual estado en la
pila context.globalAlpha =
parseFloat(jugador.alpha); var
anguloEnRadianes = rotacion * Math.PI / 180;
if (jugador.avance){
context.drawImage(mosaicoNave2, origenX,
origenY, 32,32, jugador.x,jugador.y,32,32);
}else{
context.drawImage(mosaicoNave, origenX,
origenY, 32,32, jugador.x,jugador.y,32,32);
}
//restaurar contexto
context.restore(); //pop old state on to
screen
context.globalAlpha = 1;
}
function estadoNuevoJuego(){
ConsoleLog.log(“estadoNuevoJuego”);
nivel = 0; puntuacion = 0;
navesJugador = 3;
jugador.velocidadMax = 5;
jugador.width = 32;
jugador.height = 32;
jugador.widthMedio = 16;
jugador.heightMedio = 16;
jugador.hitWidth = 24;
jugador.hitHeight = 24;
jugador.velocidadRotacion = 10; //cuantos
grados gira la nave jugador.aceleracion =
.05;
jugador.missileFrameDelay = 5;
jugador.avance = false;
jugador.alpha = 1;
jugador.rotacion = 0;
jugador.x = 0;
jugador.y = 0;
rellenarFondo();
renderizarTableroPuntuacion();
cambioEstadoApp(ESTADO_NUEVO_NIVEL);
Todos los demas objetos del juego tienen sus nuevos atributos
hitWidth y hitHeight. Vamos a modiicar la función
colisionCuadroDelimitador() de nuestro juego para usar
estos nuevos valores para todas las comprobaciones de colision.
function colisionCuadroDelimitador(objeto1,
objeto2) {
seguidamente, vamos a ver como podemos usar esta misma idea para
renderizar el resto de los objetos del juego con la nueva hoja de
mosaicos.
function renderizarPlatillos() {
var platilloTemp = {};
var cantidadPlatillos = platillos.length-1;
for (var contarPlatillos =
cantidadPlatillos;contarPlatillos>=0;
contarPlatillos--){
//ConsoleLog.log(“platillos: “ +
contarPlatillos); platilloTemp =
platillos[contarPlatillos];
function renderizarPiedras() {
var piedrasTemp = {};
var cantPiedras = piedras.length-1;
switch(piedrasTemp.scale){ case 1:
piedrasTemp.width;
piedrasTemp.height;
context.drawImage(mosaicoPiedrasGrandes,
origenX, origenY,
piedrasTemp.width,piedrasTemp.height,piedrasT
piedrasTemp.y,piedrasTemp.width,piedrasTemp.h
break;
case 2:
piedrasTemp.width;
piedrasTemp.height;
context.drawImage(mosaicoPiedrasMedianas,
origenX,
origenY,piedrasTemp.width,piedrasTemp.height,
piedrasTemp.x,piedrasTemp.y,piedrasTemp.width
piedrasTemp.height);
break;
case 3:
piedrasTemp.width;
piedrasTemp.height;
context.drawImage(mosaicoPiedrasPeque,
origenX,
origenY,piedrasTemp.width,piedrasTemp.height,
piedrasTemp.x,piedrasTemp.y,piedrasTemp.width
piedrasTemp.height);
break;
}
context.restore(); //recuperar viejo estado a
la pantalla
} }
piedrasTemp.cuentaAnimacion++;
if (piedrasTemp.cuentaAnimacion >
piedrasTemp.retrasoAnimacion){
piedrasTemp.cuentaAnimacion = 0;
piedrasTemp.rotacion +=
piedrasTemp.rotacionInc; if
(piedrasTemp.rotacion > 4){
piedrasTemp.rotacion = 0; }else if
(piedrasTemp.rotacion <0){
piedrasTemp.rotacion = 4; }
}
Ambos los misiles del jugador y los misiles del platillo son renderizados
de identica manera. Para cada uno de ellos, simplemente
necesitaremos saber el ID del mosaico que destinamos para este
propósito, esta imagen del mosaico representa la imagen de lo que
queremos mostrar. Para los misiles del jugador, el ID asignado al
mosaico es 1; para los misiles del platillo el ID asignado al mosaico es
el 0.
function renderizarMisilesJugador() {
var misilesJugadorTemp = {};
var
cantMisilesJugador=misilesDelJugador.length-
1;
//ConsoleLog.log(“render cantMisilesJugador=”
+
//cantMisilesJugador);
for (var
contarMisilesJugador=cantMisilesJugador;
contarMisilesJugador>=0;contarMisilesJugador-
-){ //ConsoleLog.log(“dibujar misiles del
jugador “ + //contarMisilesJugador)
misilesJugadorTemp =
misilesDelJugador[contarMisilesJugador];
context.save(); //salvar el estado actual en
la pila
context.drawImage(mosaicoParticulas, origenX,
origenY,
misilesJugadorTemp.width,misilesJugadorTemp.h
misilesJugadorTemp.x,misilesJugadorTemp.y,
misilesJugadorTemp.width,misilesJugadorTemp.h
function renderizarMisilesDelPlatillo() {
var misilesDelPlatilloTemp = {};
var cantMisilesPlatillo =
misilesDelPlatillo.length-1;
//ConsoleLog.log(“misilesDelPlatillo= “ +
misilesDelPlatillo.length)
for (var
contarMisilesPlatillo=cantMisilesPlatillo;
contarMisilesPlatillo>=0;contarMisilesPlatillo
-){ //ConsoleLog.log(“dibujar misiles del
platillo “ + //contarMisilesPlatillo)
misilesDelPlatilloTemp =
misilesDelPlatillo[contarMisilesPlatillo];
context.save(); //salvar el estado actual en
la pila
context.drawImage(mosaicoParticulas, origenX,
origenY,
misilesDelPlatilloTemp.width,misilesDelPlatill
misilesDelPlatilloTemp.x,misilesDelPlatilloTem
misilesDelPlatilloTemp.width,misilesDelPlatill
Las partículas usarán los mismos cuatro mosaicos del archivo parts png
que renderiza a los misiles. El juego Defensa Espacial en el capítulo 3
usaba solo una sencilla partícula blanca para animar las explosiones.
Reemplazaremos la función crearExplosión() desde ese juego
previo con una nueva que pueda usar un color de partícula diferente
para cada uno de los diferentes tipos de explosión, de esta forma, las
piedras, platillos y la nave del jugador pueden todos ellos tener un
único color que diferencie sus explosiones.
nuevaParticula.dx *= -1;
}
nuevaParticula.dy = Math.random()*3;
if (Math.random()<.5){
nuevaParticula.dy *= -1;
}
nuevaParticula.lifeCtr = 0;
nuevaParticula.x = x;
nuevaParticula.width = 2;
nuevaParticula.height = 2;
nuevaParticula.y = y;
nuevaParticula.tipo = tipo;
//ConsoleLog.log(“nuevaParticula.vida=” +
//nuevaParticula.vida);
particulas.push(nuevaParticula);
}
}
}
platillos: tipo = 0
piedras grandes: tipo = 1 piedras medianas: tipo = 2 piedras
pequeñas: tipo = 3 jugador: tipo = 4
function renderizarParticulas() {
;contarParticulas--){
particulasTemp =
particulas[contarParticulas]; context.save();
//salvar el estado actual en la pila var
mosaico;
//console.log(“partes tipo=” +
particulasTemp.tipo)
switch(particulasTemp.tipo){
case 0: // platillo
mosaico = 0;
break;
case 1: //piedras grandes
mosaico = 2;
break;
case 2: //piedras medianas
mosaico = 3;
break;
case 3: //piedras pequeñas
mosaico = 0;
break;
case 4: //jugador
mosaico = 1;
break;
}
context.drawImage(mosaicoParticulas, origenX,
origenY,
particulasTemp.width,particulasTemp.height,par
particulasTemp.y,particulasTemp.width,particul
crearExplosion(piedrasTemp.x+piedrasTemp.width
+
piedrasTemp.heightMedio,10,piedrasTemp.scale)
Pasamos el atributo piedrasTemp.scale como último parámetro
porque en las piedras estamos usando el parámetro scale como el
parámetro tipo.
Para el platillo:
crearExplosion(platillosTemp.x+platillosTemp.w
platillosTemp.y + platillosTemp.heightMedio,
10, 0);
Para el platillo y el jugador, pasamos como último parámetro un número
literal dentro de la función crearExplosion(). En el caso del
platillo, pasamos un 0. Para la nave del jugador pasaremos un 4:
crearExplosion(jugador.x +
jugador.widthMedio, jugador.y +
jugador.heightMedio, 50, 4);
Note que la función crearExplosion() llamada para el jugador
esta en la función jugadorMuerto(), la cual es llamada desde
chequearColisiones().
Agregar Sonido
var sonidoExplosion;
var sonidoExplosion2;
var sonidoExplosion3;
var sonidoDisparo;
var sonidoDisparo2;
var sonidoDisparo3;
var sonidoDisparoPlatillo; var
sonidoDisparoPlatillo2; var
sonidoDisparoPlatillo3;
Usaremos una función para cargar todos esos elementos del juego
que hemos agregado en este apéndice, mientras que nuestro estado
de la aplicación esperará en un estado inerte. Agregamos este código
a nuestro juego a través de la función estadoInicial()
Nota destacada!
Los sonidos no trabajan de la misma forma en todos los navegadores
web. En este juego, estamos cargando todas las imagenes y sonidos
del juego. Para Internet Explorer 9 y 10, esta precarga, algunas veces,
no funciona.
Atento
Tu puedes cambiar el número de elementos a precargar de 16 a 7,
para probar el código del juego sin sonido en navegadores Internet
Explorer que tienen o dan problema con la precarga de los elementos
del juego.
function estadoInicialJuego() {
crearTodosObjetos();
cantElemCarga = 0;
elementosACargar=16; // cambiar a 7 si
experimenta problemas con IE
sonidoExplosion =
document.createElement(“audio”);
document.body.appendChild(sonidoExplosion);
tipoDeAudio =
supportedAudioFormat(sonidoExplosion);
sonidoExplosion.setAttribute(“src”,
“explode1.” + tipoDeAudio);
sonidoExplosion.addEventListener(“canplaythrou
elementoCargado,false);
sonidoExplosion2 =
document.createElement(“audio”);
document.body.appendChild(sonidoExplosion2);
sonidoExplosion2.setAttribute(“src”,
“explode1.” + tipoDeAudio);
sonidoExplosion2.addEventListener(“canplaythro
elementoCargado,false);
sonidoExplosion3 =
document.createElement(“audio”);
document.body.appendChild(sonidoExplosion3);
sonidoExplosion3.setAttribute(“src”,
“explode1.” + tipoDeAudio);
sonidoExplosion3.addEventListener(“canplaythro
elementoCargado,false);
sonidoDisparo =
document.createElement(“audio”); audioType =
supportedAudioFormat(sonidoDisparo);
document.body.appendChild(sonidoDisparo);
sonidoDisparo.setAttribute(“src”, “shoot1.” +
tipoDeAudio);
sonidoDisparo.addEventListener(“canplaythrough
elementoCargado,false);
sonidoDisparo2 =
document.createElement(“audio”);
document.body.appendChild(sonidoDisparo2);
sonidoDisparo2.setAttribute(“src”, “shoot1.”
+ tipoDeAudio);
sonidoDisparo2.addEventListener(“canplaythrou
elementoCargado,false);
sonidoDisparo3 =
document.createElement(“audio”);
document.body.appendChild(sonidoDisparo3);
sonidoDisparo3.setAttribute(“src”, “shoot1.”
+ tipoDeAudio);
sonidoDisparo3.addEventListener(“canplaythrou
elementoCargado,false);
sonidoDisparoPlatillo =
document.createElement(“audio”); audioType =
supportedAudioFormat(sonidoDisparoPlatillo);
document.body.appendChild(sonidoDisparoPlatill
sonidoDisparoPlatillo.setAttribute(“src”,
“saucershoot.” +
tipoDeAudio);
sonidoDisparoPlatillo.addEventListener(“canpla
elementoCargado,false);
sonidoDisparoPlatillo2 =
document.createElement(“audio”);
document.body.appendChild(sonidoDisparoPlatill
sonidoDisparoPlatillo2.setAttribute(“src”,
“saucershoot.” +
tipoDeAudio);
sonidoDisparoPlatillo2.addEventListener(“canpl
elementoCargado,false);
sonidoDisparoPlatillo3 =
document.createElement(“audio”);
document.body.appendChild(sonidoDisparoPlatill
sonidoDisparoPlatillo3.setAttribute(“src”,
“saucershoot.” + tipoDeAudio);
sonidoDisparoPlatillo3.addEventListener(“canpl
elementoCargado,false);
cambioEstadoApp(ESTADO_ESPERA_CARGUE_JUEGO);
}
function elementoCargado(evento) {
cantElemCarga++;
console.log(“loading:” + cantElemCarga);
console.log(“itemsToLoad:” +
elementosACargar); if (cantElemCarga >=
elementosACargar) {
sonidoDisparo.removeEventListener(“canplaythro
elementoCargado, false);
sonidoDisparo2.removeEventListener(“canplaythr
elementoCargado,false);
sonidoDisparo3.removeEventListener(“canplaythr
elementoCargado,false);
sonidoExplosion.removeEventListener(“canplayth
elementoCargado,false);
sonidoExplosion2.removeEventListener(“canplay
elementoCargado,false);
sonidoExplosion3.removeEventListener(“canplay
elementoCargado,false);
sonidoDisparoPlatillo.removeEventListener(“can
elementoCargado,false);
sonidoDisparoPlatillo2.removeEventListener(“ca
elementoCargado,false);
sonidoDisparoPlatillo3.removeEventListener(“ca
elementoCargado, false);
grupoSonido.push({nombre:”explode1”,
elemento:sonidoExplosion, juega:false});
grupoSonido.push({nombre:”explode1”,
elemento:sonidoExplosion2, juega:false});
grupoSonido.push({nombre:”explode1”,
elemento:sonidoExplosion3, juega:false});
grupoSonido.push({nombre:”shoot1”,
elemento:sonidoDisparo, juega:false});
grupoSonido.push({nombre:”shoot1”,
elemento:sonidoDisparo2, juega:false});
grupoSonido.push({nombre:”shoot1”,
elemento:sonidoDisparo3, juega:false});
grupoSonido.push({nombre:”saucershoot”,
elemento:sonidoDisparoPlatillo,
juega:false});
grupoSonido.push({nombre:”saucershoot”,
elemento:sonidoDisparoPlatillo2,
juega:false});
grupoSonido.push({nombre:”saucershoot”,
elemento:sonidoDisparoPlatillo3,
juega:false});
cambioEstadoApp(ESTADO_TITULO_DEL_JUEGO);
} }
Reproducir sonidos
reproducirSonido(SONIDO_EXPLOSION,.5);
function reproducirSonido(sonido,volume) {
ConsoleLog.log(“reproducir sonido” + sonido);
var sonidoEncontrado = false;
var indiceSonido = 0;
var sonidoTemp;
if (grupoSonido.length> 0) {
var sonidoT = grupoSonido[indiceSonido];
nombre == sonido) {
sonidoEncontrado = true; sonidoT.juega =
true;
} else {
indiceSonido++;
}
} }
if (sonidoEncontrado) {
ConsoleLog.log(“sonido encontrado”);
sonidoTemp =
grupoSonido[indiceSonido].elemento;
//sonidoTemp.setAttribute(“src”, sonido + “.”
+ tipoDeAudio); //sonidoTemp.loop = false;
//sonidoTemp.volume = volume;
sonidoTemp.play();
tipo:tipoDeAudio,juega:true});
}
}
Ahora vamos a movernos dentro de otro tipo de contenedor de
aplicación, el objeto contenedor.
function crearObjetosContenedores(){
for (var ctr = 0; ctr < particulasMax; ctr++){
var nuevaParticula = {};
contenedorDeParticulas push(nuevaParticula);
}
console.log(“ contenedorDeParticulas=” +
contenedorDeParticulas length); }
Nota destacada!
La funcionalidad puede ser extendida para agregar una partícula al
contenedor cuando no este disponible. No tenemos que agregar esa
funcionalidad a nuestro juego, pero es bastante común hacerlo en
algunos algoritmos Atento de contenedores.
Aquí esta la nueva función crearExplosion() modiicada completamente:
function crearExplosion(x,y,num,tipo) {
reproducirSonido(SONIDO_EXPLOSION,.5); for
(var contarParticulas=0;contarParticulas<num;
contarParticulas++){
if (contenedorDeParticulas.length > 0){
nuevaParticula =
contenedorDeParticulas.pop();
nuevaParticula.dx = Math.random()*3;
if (Math.random()<.5){
nuevaParticula.dx *= -1;
}
nuevaParticula.dy = Math.random()*3;
if (Math.random()<.5){
nuevaParticula.dy *= -1;
}
nuevaParticula.contarVida = 0;
nuevaParticula.x = x;
nuevaParticula.width = 2;
nuevaParticula.height = 2;
nuevaParticula.y = y;
nuevaParticula.tipo = tipo;
//ConsoleLog.log(“nuevaParticula.vida=” +
nuevaParticula.vida);
particulas.push(nuevaParticula);
}
} }
if (remover) {
contenedorDeParticulas.push(particulasTemp);
particulas.splice(contarParticulas,1);
jugador
jugador x += jugador.moverX*contadorDeFotogramas.paso; jugador.y
+= jugador moverY*contadorDeFotogramas paso;
misilesDelJuagdor
misilesJugadorTemp x +=
misilesJugadorTemp.dx*contadorDeFotogramas.paso;
misilesJugadorTemp.y +=
misilesJugadorTemp.dy*contadorDeFotogramas paso;
piedras
piedrasTemp x += piedrasTemp.dx*contadorDeFotogramas paso;
piedrasTemp.y += piedrasTemp.dy*contadorDeFotogramas.paso;
platillo
platilloTemp x += platilloTemp.dx*contadorDeFotogramas paso;
platilloTemp.y += platilloTemp.dy*contadorDeFotogramas.paso;
misilesDelPlatillo
misilesDelPlatilloTemp.x +=
misilesDelPlatilloTemp.dx*contadorDeFotogramas paso;
misilesDelPlatilloTemp.y +=
misilesDelPlatilloTemp.dy*contadorDeFotogramas.paso;
particulas
particulasTemp x += particulasTemp.dx*contadorDeFotogramas paso;
particulasTemp.y += particulasTemp.dy*contadorDeFotogramas.paso;
Ya hemos cubierto casi todos los cambios que pensamos hacer para
acercar nuestra pequeña aplicación a la realidad.