Está en la página 1de 515

HTML5 Canvas referencia y

ejemplos
Juan Carlos Reyes C.

Todos los nombres propios de programas, sistemas operativos,


equipos, hardware, etc. que aparezcan en este libro son marcas
registradas de sus respectivas compañías u organizaciones.

Reservado todos los derechos. El contenido de esta obra está


protegida por la ley, que establece penas de prisión y/o multas, además
de las correspondientes indemnizaciones por daños y perjuicios, para
quienes reprodujeran, plagiaren, distribuyeren o comunicaren
públicamente, en todo o en parte, una obra literaria, artistica o cientiica,
o su transformación, interpretación o ejecución artística ijada en
cualquier tipo de soporte o comunicada a través de cualquier medio ,
sin la preceptiva autorización.

© BUBOK PUBLISHING S L., 2013 Primera Edición


ISBN:
Impreso enEspaña / Printed in Spain Editado por Bubok
Agradecimientos
Ante todo me es muy necesario darles las gracias a mi querida esposa
Sissi, quién me ha soportado durante mucho tiempo al embarcarme en
cada uno de mis proyectos, incluso por mas tiempo del que me hubiera
atrevido a pedirle. Te quiero Sissi.

También me gustaría darles las gracias a Yensi, mi hermana, por tu gran


colaboración en mantenernos siempre unidos y tranquilos, en darle
ese calor necesario a nuestro hogar y a mi familia mientras yo me
embarcaba en este y algunos otros proyectos.

Y por último, quisiera agradecerles a mis hijos y en especial a ti Saraí


por tu gran ayuda en mis largas horas de preparación de este
proyecto, y en general a todos por su gran aporte a este trabajo, ya
que son ellos los que cada día me impulsan y motivan a iniciar cada
uno de mis proyectos nuevos, aunque ironicamente muchas veces
estos proyectos me roben el tiempo que pudiera dedicarles, gracias
Saraí, Juan David y Camila.

Sissi, si se pudo!
Juan Carlos Reyes C.

Tiene unos 15 años de experiencia en diseño y desarrollo web. Ha


trabajado en y para muchas empresas privadas y agencias de
publicidad, tales como Roberto Eliaschev, Ghersi-Quintero, Puma
pblicidad, Laboratorios Leti, Merck, Aventis, Calox, SmithKline Beecham,
Krat, Empresas Polar entre otras. Juan Carlos es creador de diferentes
sotware de codigo abierto para blogs, cms, e-commerce y analitica web.

Durante su carrera freelance y profesional, Juan Carlos ha construido


y colaborado en la construcción de mas de 6o sitios y aplicaciones
web. Con una perspectiva fuera de lo ordinario, durante toda su
carrera, ya que se mueve entre el diseño gráico y el desarrollo de
aplicaciones web, su trabajo ha abarcado desde simples logotipos,
creación de empaques hasta grandes y complejos sitios web vistosos,
funcionales y optimos.
Indice
Agradecimientos........................................................................................................
Sobre el
autor..............................................................................................................................
Indice...........................................................................................................................
Introducción..............................................................................................................
Para quién es este
libro................................................................................................................14
Convenciones usadas en este
libro............................................................................................14
Cómo está organizado el
libro...................................................................................................15
Iconos y
terminología................................................................................................................
Código fuente de los ejemplos de este
libro.............................................................................16
Prologo........................................................................................................................
Lo
básico..........................................................................................................................
Qué es exactamente el canvas de
HTML5...............................................................................19
Las 5 principales características del
canvas.............................................................................19
Dispositivos del
cliente...............................................................................................................20
Navegadores
web........................................................................................................................20
Navegadores
mobiles.................................................................................................................20
Websites y páginas
web..............................................................................................................21
HTML5..........................................................................................................................
Javascript....................................................................................................................
Fundamentos de un objeto
javascript......................................................................................25
Crear
Objetos.........................................................................................................................
Propiedades de
objetos..............................................................................................................26
Literales de
objetos.................................................................................................................... 29
Objetos como propiedades de un objeto
window.................................................................. 31
Que hay en un
nombre...............................................................................................................33
Funciones como
callbacks..........................................................................................................36
La variable
this........................................................................................................................... 36
Closures......................................................................................................................
La
referencia..................................................................................................................
El elemento
canvas.....................................................................................................................46
Como trabaja el objeto
canvas..................................................................................................46
Atributos y métodos del objeto
canvas....................................................................................46
Width............................................................................................................................
Height...........................................................................................................................
Context2d...................................................................................................................
getContext()...............................................................................................................
toDataURL()...............................................................................................................
toBlob()........................................................................................................................
save()...........................................................................................................................
restore().......................................................................................................................
La matriz de
transformación.................................................................................................... 5
Métodos primarios de
transformación.................................................................................. 51
scale()..........................................................................................................................
rotate().........................................................................................................................
translate()....................................................................................................................
setTransform()............................................................................................................
Métodos de
Path........................................................................................................................65
beginPath().................................................................................................................
closePath()..................................................................................................................
arc()..............................................................................................................................
rect().............................................................................................................................
ill().................................................................................................................................
stroke().........................................................................................................................
moveTo()......................................................................................................................
lineTo().........................................................................................................................
arcTo()..........................................................................................................................
quadraticCurveTo()..................................................................................................
bezierCurveTo()........................................................................................................
clip()..............................................................................................................................
isPointInPath()............................................................................................................
drawSystemFocusRing().........................................................................................
drawCustomFocusRing()........................................................................................
scrollPathIntoView()..................................................................................................
ellipse()........................................................................................................................
Métodos de
rectángulos...........................................................................................................10
clearRect()..................................................................................................................
strokeRect()................................................................................................................
illRect().........................................................................................................................
Métodos de gradientes y
patrones...........................................................................................106
createLinearGradient()...........................................................................................
createRadialGradient()...........................................................................................
createPattern()...........................................................................................................
Métodos de
Imagen..................................................................................................................112
drawImage()...............................................................................................................
Métodos de
Texto......................................................................................................................118
illText().........................................................................................................................
strokeText().................................................................................................................
measureText()............................................................................................................
Métodos de manipulación de imagenes Image
Data........................................................133
getImageData()..........................................................................................................
putImageData()..........................................................................................................
createImageData()....................................................................................................
Métodos de
accesibilidad........................................................................................................14
drawSystemFocusRing().........................................................................................
drawCustomFocusRing()........................................................................................
setCaretSelectionRect()..........................................................................................
caretBlinkRate().........................................................................................................
La región
clipping.....................................................................................................................14
Las propiedades del
contexto..................................................................................................145
canvas..........................................................................................................................
illStyle...........................................................................................................................
font................................................................................................................................
globalAlpha................................................................................................................
globalCompositeOperation....................................................................................
lineCap........................................................................................................................
lineWidth.....................................................................................................................
lineJoin........................................................................................................................
miterLimit......................................................................................................................
shadowBlur.................................................................................................................
shadowOfsetX............................................................................................................
shadowOfsetY.............................................................................................................
strokeStyle...................................................................................................................
textAlign.......................................................................................................................
textBaseline................................................................................................................
La región
Hit............................................................................................................................162
addRegionHit()..........................................................................................................
removeRegionHit()...................................................................................................
Introducción
Estoy convencido de que el canvas de HTML5 representa uno de los
avances más importantes en internet desde la creación del primer
navegador web en los años 90. El canvas integra una poderosa
herramienta de desarrollo gráico con la tecnología de los navegadores
web, lo que hace suponer, que cualquier desarrollador web con
conocimientos de código javascript, puede agregar animación y
gráicos impresionantes a sus websites.

Los gráicos que son generados en el canvas con tecnología basada


en el navegador, diieren de los gráicos generados con tecnologías
basadas en el servidor. Los códigos generados por el canvas y
mostrados en el dispositivo del cliente son ejecutados por el
navegador web y no por el servidor donde esta hospedada la página
web. Esto signiica que tu puedes agregar elementos canvas con
impresionantes gráicos y animaciones directamente a tus páginas web
sin necesidad de escribir ni una sola linea de código del lado del
servidor.

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.

El estandar del canvas de HTML5 esta teniendo una rápida y gran


aceptación y la mayoría de los grandes navegadores web ya lo están
implementando.

Para quien es este libro

Este libro se ha enfocado para ser usado por diseñadores y


desarrolladores web, desde principiantes hasta avanzados, que
quieran dominar y conocer a fondo el canvas de sus páginas HTML5 y
generar buenas aplicaciones web interactivas y animadas. Los
profesionales que deseen crear grandes aplicaciones basados en
canvas como juegos, animaciones o gráicos complejos, aquí tendrán
una gran herramienta de trabajo y estudio, una guía de bolsillo y
consulta diaria, de lectura, etc., porque de alguna manera se que son
ustedes los que le podrán sacar el máximo provecho a esta obra.

Los principiantes quizas encuentren algo complicadas ciertas


secciones y terminos en este libro, pero esto no debe ser un
impedimento para que también sea una gran guía de consulta, de
aprendizaje y sobre todo de estudio contínuo.

En lo personal se que muchas veces hace falta un medio, ademas de


internet, para poder aprender y consultar, para que son cada una de
las herramientas que nos ofrecen algunos lenguajes de programación,
y se que no siempre tenemos internet al alcance de la mano para estas
consultas, y con la idea en mente de que siempre es necesario, y que
nunca pasará de moda, y no solo para consultar sino hacerlo una guia
de aprendizaje, he concebido este libro para ofrecerles cubrir esta
necesidad que tenemos muchos, de cada día ser mejores
profesionales, y esto solo se consigue cuando conocemos
verdaderamente todas y cada una de las herramientas disponibles
para su uso y no una consulta exporadica a los sitios web de
información, aunque estos tambien resulten muy útiles.

Tanto como si eres novato o si tiene mucha experiencia en el


desarrollo de aplicaciones web basadas en canvas, obtendrás un
beneicio adicional al incluir este libro dentro de las herramientas
necesarias cuando vas a desarrollar tus ideas y aplicaciones, estoy
seguro de que el contenido de este libro te será de gran ayuda.

Convención usada en este libro

Los códigos de ejemplos en este libro estan escritos con un texto


monoespacial sobre un marco negro al 5% de tinta.
var canvas =
document.getElementById(‘canvas’),
context = canvas.getContext(‘2d’);
Los nombres de los archivos de los ejemplos usados para ilustrar cada
uno de los métodos, propiedades, atributos, etc. estan resaltado de
esta manera:
nombre del archivo - composicionCanvas.js
Los links a páginas web, nombres de métodos, funciones, variables, o
cualquier código escrito en este libro, están en un texto monoespacial:
http://www.google.com

Como está organizado este libro

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.

Sección 4 - Ayuda en línea


Como en cualquier caso despúes de haber inalizado la instrucción, la
pregunta propia que nos hacemos, es: Y ahora que?, bueno tratamos
de ayudarte un poco con esta pregunta y te mostramos los links a
muchas páginas de ayudas en linea, tutoriales, tips, curiosidades,
proyectos, trabajos terminados, etc. Para que cada quien pueda
consultar y moverse dentro de esta nueva tecnología que aún esta en
sus primeros pasos e ir avanzando con ella.

Iconos y terminología gráfica usada en este libro

Sólo usaremos un icono enmarcado dentro de un gradiente para


indicar una nota importante o destacada del item actual, este es similar
al siguiente:
Nota Destacada
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

start y right es lo mismo que end. De igual manera, cuando el


navegador muestra los textos de derecha a izquierda, significa que el
valor para el atributo dir esrtl, right es lo mismo que start y
left es lo mismo que end.

Código fuente de los ejemplos de este libro

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.

Todos los ejemplos han sido probado en varios navegadores que


incluyen Internet Explorer, Firefox, Safari, Google Chrome y Opera.
Prologo
Este libro documenta la API de javascript para dibujar en la etiqueta
canvas de HTML5. Por lo tanto asumimos que conoces del lenguaje de
programación del lado del cliente javascript y tiene por lo menos los
conceptos básicos del uso de javascript en las páginas web.
Lo básico
Canvas fue originalmente creado por Apple en el año 2004 para ser
aplicados en los widgets y dar mayor poder a sus gráicos en el
navegador Safari. Luego esta tecnología fue adoptada por Firefox,
Opera y Google Chrome. Hoy en día canvas es parte de la nueva
especiicación HTML5 para las próximas generaciones de tecnología
web.

El canvas de HTML5 es una etiqueta HTML que puedes incrustar en un


documento HTML5 con el proposito de dibujar gráicos en javascript. El
canvas de HTML5 es solo un bitmap, cada pixel dibujado dentro del
canvas es sobrescrito sobre el.

Que es exactamente el canvas de HTML5

El canvas es un estandar para aplicaciones escritas en javascript que


corren dentro de una página web descargada desde un servidor y
mostrada por un navegador en un dispositivo del cliente.

Las 5 principales caracteristícas del canvas

Es Interactivo : El canvas de HTML5 puede estar atento y responder


ante cualquier acción del usuario. Mover el mouse, presionar un botón,
trazos con el dedo. Todo esto gracias a tu código javascript y la
aplicación de manegjo de acciones usado.

Es animado: Los objetos pueden moverse dentro de un canvas HTML5,


desde un simple bote de una bola hasta grandes y complejas
animaciones.

Es accesible: Hoy en día la mayoría de los navegadores web soportan


todo o gran parte del canvas HTML5. Tus aplicaciones basadas en el
elemento canvas pueden ser usadas en un gran rango de dispositivos,
desde inmensos y poderosos ordenadores hasta smartphone y
tabletas.

Es flexible: Un canvas de HTML5 puede ser usado para mostrar textos,


lineas, formas, imagenes, videos,..., todo con o sin animaciones. Esto es
un medio superlexible.
Esta creciendo rápidamente: HTML5 y las caracteristicas del elemento
canvas están ganando popularidad.

Dispositivos del cliente

Los dispositivos del cliente incluyen ordenadores tales como PC’s de


sobremesa o de escritorio y dispositivos mobiles tales como
smartphone, tabletas y portátiles. El dispositivo del cliente es donde tu
navegador web reside y tu canvas es mostrado. El sitio web que deine
tu canvas, esta hospedado en un servidor. Tus páginas web son
descargadas desde el servidor y mostrado por tu navegador web.

Navegadores web

Los navegadores web son aplicaciones que construyen y muestran


páginas web basadas en instrucciones de Hypertext Markup
Language (HTML). Los grandes navegadores web y sus sitios de
descargas son listados a continuación:

Internet Explorer: www.windows microsot.com/en-


US/internetexplorer/downloads/ie Firefox: www.mozilla.org/irefox
Chrome: www.google.com/chrome
Safari: www.apple.com/support/safari
Opera: www.opera.com

Navegadores mobiles

Los navegadores mobiles son cargados en tu dispositivo mobil como


App, que a su vez son descargados desde las tiendas virtuales
especiicadas por cada una de las marcas comercializadoras de esta
tecnología. Para buscar navegadores en las tiendas de App, dirigete a
la sección de navegadores o escribe en el buscador de App, el nombre
del navegador que desees, tales como: internet explorer, irefox,
chrome, safari, opera.

Websites y páginas web

Los websites son un grupo de páginas web deinidas por unos


elementos HTML llamadas tags o etiquetas, las tags o etiquetas HTML
deinen las hojas de las páginas web y sus contenidos, incluyendo el
canvas.

Ejemplo de algunas de las tags o etiquetas HTML más usadas en las


aplicaciones web basadas en canvas:

<!DOCTYPE HTML> : declara el documento para la página web.


<html>: delimita el código HTML usado.
<head>: deine el código que contiene toda la información acerca de
tus páginas web.
<script>: delimita un area para códigos script como por ejemplo el
canvas. <body>: deine el area principal de tus páginas web.
<div>: provee información formateda a tus páginas web.
<canvas>: deine el area del canvas.

HTML5

El HTML5 (HyperText Markup Language, versión 5) es la quinta


revisión del lenguaje de programación “ básico” de la World Wide
Web, el HTML. Esta nueva versión pretende remplazar al actual
(X)HTML, corrigiendo problemas con los que algunos desarrolladores
web se encuentran, así como rediseñar el código actualizandolo a
nuevas necesidades que demanda la web de hoy en día.

Actualmente el uso de HTML5 está creciendo a pasos agigantados, y


ya existen muchas empresas en todo el mundo que están
desarrollando sus sitios webs en esta versión del lenguaje. A diferencia
de otras versiones de HTML, los cambios en HTML5 comienzan
añadiendo semántica y accesibilidad implícitas, especiicando cada
detalle y borrando cualquier ambigüedad. Se tiene en cuenta el
dinamismo de muchos sitios webs (facebook, twenti, etc), donde su
aspecto y funcionalidad son más semejantes a aplicaciones webs que
a documentos.

Se estaba o se esta siendo muy abusivo en el uso de elementos DIV


para estructurar una web en bloques. El HTML5 nos brinda varios
elementos que perfeccionan esta estructuración estableciendo qué es
cada sección, eliminando así DIV innecesarios. Este cambio en la
semántica hace que la estructura de la web sea más coherente y fácil
de entender por otras personas y los navegadores podrán darle más
importancia a según qué secciones de la web, facilitándole además la
tarea a los buscadores, así como cualquier otra aplicación que
interprete sitios web.

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.

Cuando los navegadores realizan un render de una página,


construyen un “ Modelo de Objeto de Documento” (Document Object
Model - DOM), una colección de objetos que representan los elementos
del HTML en la página. Cada elemento - <p>, <div>, <span> - es
representado en el DOM por un objeto diferente.

Todos los objetos DOM comparten unas características comunes,


aunque algunos tienen más que otros. En los navegadores que
soportan rasgos del HTML5, algunos objetos tienen una única
propiedad y con una simple ojeada al DOM podremos saber las
características que soporta el navegador.

Existen cuatro técnicas básicas para saber cuando un navegador


soporta una de estas particulares características, desde las más
sencillas a las más complejas.

1. Comprueba si determinadas propiedades existen en objetos


genéricos o globales (como window o navigator). Por ejemplo al
comprobar soporte para la “ Geolocalización” .

2. Crear un elemento, luego comprobar si determinadas propiedades


existen en ese elemento. Por ejemplo al comprobar soporte para
canvas.
3. Crear un elemento, comprobar si determinados métodos existen en
ese elemento, llamar el método y comprobar los valores que devuelve.
Por ejemplo: comprobar qué formatos de video soporta.
4. Crear un elemento, asignar una propiedad a determinado valor,
entonces comprobar si la propiedad mantiene su valor. Ejemplo:
comprobar que tipo de <input> soporta.

Las webs se dividirán en los siguientes elementos:

<section></section> - Se utiliza para representar una sección


“ general” dentro de un documento o aplicación, como un capítulo de
un libro. Puede contener subsecciones y si lo acompañamos de h1-h6
podemos estructurar mejor toda la página creando jerarquías del
contenido, algo mu favorable para el buen posicionamiento web.

<article></article> - El elemento de artículo representa un


componente de una página que consiste en una composición
autónoma en un documento, página, aplicación, o sitio web con la
intención de que pueda ser reutilizado y repetido. Podría utilizarse
en los artículos de los foros, una revista o el artículo de periódico, una
entrada de un blog, un comentario escrito por un usuario, un widget
interactivo o gadget, o cualquier otro artículo independiente de
contenido.
Cuando los elementos de <article> son anidados, los elementos de
<article> interiores representan los artículos que en principio son
relacionados con el contenido del artículo externo. Por ejemplo, un
artículo de un blog que permite comentarios de usuario, dichos
comentarios se podrían representar con <article>.

<aside></aside> - Representa una sección de la página que


abarca un contenido tangencialmente relacionado con el contenido
que lo rodea, por lo que se le puede considerar un contenido
independiente. Este elemento puede utilizarse para efectos
tipográicos, barras laterales, elementos publicitarios, para grupos de
elementos de la navegación, u otro contenido que se considere
separado del contenido principal de la página.

<header></header> - Elemento <header> representa un grupo


de artículos introductorios o de navegación.

<nav></nav> - El elemento <nav> representa una sección de una


página que es un link a otras páginas o a partes dentro de la página:
una sección con links de navegación. No todos los grupos de enlaces
en una página tienen que estar en un elemento <nav>, sólo las
secciones que consisten en bloques principales de la navegación son
apropiadas para ser utilizadas con el elemento <nav>. Puede utilizarse
particularmente en el pie de página para tener un menú con un listado
de enlaces a varias páginas de un sitio, como el Copyright; home page,
política de uso
y privacidad. No obstante, el elemento <footer> es plenamente
suiciente sin necesidad de tener un elemento <nav>.

<footer></footer> - El elemento <footer> representa el pié de


una sección, con información acerca de la página/sección que poco
tiene que ver con el contenido de la página, como el autor, el copyright
o el año.

El elemento input adquiere gran relevancia al ampliarse los elementos


que se permitiran en el “ type” .
<input type=”search”> para cajas de búsqueda.
<input type=”number”> para adicionar o restar números
mediante botones. <input type=”range”> para seleccionar un
valor entre dos valores predeterminados.
<input type=”color”> seleccionar un color.
<input type=”tel”> números telefónicos.
<input type=”url”> direcciones web.
<input type=”email”> direcciones de email.
<input type=”date”> para seleccionar un día en un
calendario. <input type=”month”> para meses.
<input type=”week”> para semanas.
<input type=”time”> para fechas.
<input type=”datetime”> para una fecha exacta, absoluta y
tiempo. <input type=”datetime-local”> para fechas
locales y frecuencia.

Otros elementos muy interesantes:


<audio> y <video> - Nuevos elementos que permitirán
incrustar un contenido multimedia de sonido o de vídeo,
respectivamente. Es una de las novedades más importantes e
interesantes en este HTML5, ya que permite reproducir y controlas
vídeos y audio sin necesidad de plugins como el de Flash.
El comportamiento de estos elementos multimedia será como el de
cualquier elemento nativo, y permitirá insertar en un video, enlaces o
imágenes, por ejemplo. Youtube, ya ha anunciado que deja el Flash y
comienza a proyectar con HTML5.

<embed> - Se emplea para contenido incrustado que necesita


plugins como el Flash. Es un elemento que ya reconocen los
navegadores, pero ahora al formar parte de un estándar, no habrá
conlicto con <object>.

<canvas> - Este es un elemento complejo que permite que se


generen gráicos al hacer dibujos en su interior. Es utilizado en Google
Maps y en un futuro permitirá a los desarrolladores crear aplicaciones
muy interesantes.

Para una lista completa de las nuevas características de HTML5, visita:


www.w3.org/TR/html5-dif/#new-elements

Javascript

Es tu código javascript el que dibujará imagenes en tus canvas. Sin


javascript, un canvas es solo un espacio blanco.

Javascript fue desarrollado por Netscape a mediados de los 90, es un


lenguaje totalmente diferente de Java, la cual fue desarrollado por Sun
Mycrosystem a principios de los 90.
No vamos a realizar un estudio exhaustivo de todo el lenguaje
javascript, ya que no es el objetivo de este libro. Sin embargo hay
algunos detalles que es importante resaltar y entender para poder
trabajar con mayor claridad en el elemento canvas de HTML5. El más
importante de estos detalles es entender que las funciones son objetos
de primera clase de javascript, que es consecuencia de la forma en
que este lenguaje las deine y las maneja. ¿Que quiere decir esto?.

Para poder entender lo que signiica que una función es un objeto,


primero debemos entender que es un objeto javascript. Luego
abordaremos lo que signiica primera clase.

Fundamentos de un objeto javascript


La mayoría de los lenguajes orientados a objetos (OO) deine un tipo
object fundamental del cual se deriva el resto. En javascript ocurre
exactamente esto pero hasta aqui llega la similitud. A nivel básico, el
object de javascript tiene poco en común con el objeto fundamental
deinido en la mayoría de los lenguajes OO.

A primera vista, un object javascript puede parecer un objeto


monótono y trivial. Una vez que esta creado no contiene datos ninguno
y además una semantica reducida. Pero precisamente esta
característica es la que le proporciona un gran potencial.

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.

¿Y ahora?¿Que podemos hacer con este objeto? Aparentemente, no


contiene nada: ni información, ni semántica compleja, nada. Nuestra
creación no será interesante hasta que empecemos a añadirle
propiedades.

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.

Vamos a ver el siguiente fragmento de código:

var coche = new Object();


coche.marca = ‘ford’;
coche.modelo = ‘mondeo’;
coche.anio = 2013;
coche.fechaCompra = new Date(2013, 01, 08);

Aqui hemos creado una nueva instancia de Object y le asignamos


una variable llamada coche. A continuación, la completamos con un
número de propiedades de diferentes tipos: dos cadenas, un numero y
una instancia Date.

No necesitamos declarar estas propiedades antes de asignarlas,


simplemente, existirán por el sólo hecho de darles un valor. Esto nos da
mucha lexibilidad. Pero, antes de alegrarse demasiado, recuerde que
esta operatividad tiene un precio.

Por ejemplo, supongamos que en el siguiente código de nuestra


página con script HTML queremos cambiar el valor de la fecha de
compra:
coche.fechaCompra = new Date(2013, 03, 09);
No es ningún problema, a no ser que cometamos un error involuntario
como:
coche.fehcaCompra = new Date(2013, 03, 09);

No existe un compilador que nos advierta que hemos cometido un


error; hemos creado una nueva propiedad llamada fehcaCompra y más
tarde nos preguntaremos porqué la nueva fecha no fumciona cuando
hacemos referencia a la propiedad deletreándola correctamente. Tene
un gran poder conlleva a una gran responsabilidad (¿lo ha oído
alguna vez?), por lo tanto hay que estar muy atento con lo que se
escribe.

En este ejemplo hemos aprendido que una instancia Object de


javascript, a la que nos referimos como object de aquí en adelante,
es una colección de propiedades, cada una de las cuales contiene un
nombre y un valor. El nombre de la propiedad es una cadena y el valor
puede ser cualquier objeto de javascript, sea Number, String,
Date, Array, un object básico o cualquier otro tipo de objeto
javascript (incluyendo funciones, como veremos un poco más
adelante).
Esto signiica que el objetivo principal de una instancia Object es
servir como contenedor para una colección determinada de otros
objetos. Esto le podría hacer recordar conceptos de otros lenguajes,
como por ejemplo, un mapa Java o diccionario o hashes de otros
lenguajes.

Las propiedades no están limitadas a los tipos String o Number,


ya que puede ser otra instancia Object, que a su vez tiene su
priopio conjunto de características, que también pueden ser objetos
con propiedades determinadas y así sucesivamente hasta cualquier
nivel que sea sensato y tenga sentido para el modelo de datos que
estamos intentando crear.
Supongamos que añadimos una nueva propiedad a nuestra instancia
coche que identiica al propietario. Ésta es otro objeto javascript que
contiene propiedades como el nombre y la ocupación del propietario:

var propietario = new Object();


propietario.nombre = ‘Juan Reyes’;
propietario.profesion = ‘animador’;
coche.propietario = propietario;

Para acceder a la propiedad anidada escribiremos lo siguiente:


var nombrePropietario =
coche.propietario.nombre;

No hay limites en los niveles de anidado que podemos utilizar (salvo el


que imponga el sentido común). Cuando lleguemos a este punto, la
jerarquía de nuestro objeto será como la que se muestra en el cuado
siguiente:
Cadena La jerarquía muestra que los objeJuan Reyes
tos son contenedores para hacer referencias a otros objetos o tipos
Cadena incluidos en javascript.
animador
Nota destacada
Esto no es necesario para todas las variables intermedias (como
propietario), que hemos creado como un objeto pedagógico. En breve,
veremos formas más compactas y eficaces para declarar objetos y sus
propiedades.
Atento

Hasta aquí hemos referenciado las propiedades de un objeto


utilizando el carácter punto. Pero, en realidad, esto es un sustituto de
un operador mas general para hacer referencia a propiedades.

¿Que pasaría si tuviéramos una propiedad denominada


color.scheme?¿Se ha ijado en el punto que existe en el medio
del nombre? Esto añade diicultad debido a que el intérprete de
javascript tratará de buscar scheme como una propiedad anidada de
color.

Podría pensar simplemente en no hacerlo de esta forma. ¿Pero que


pasaría con los caracteres espacio?¿Y que ocurre con otros caracteres
que podrían ser confundidos con delimitadores en lugar de ser parte
de un nombre?

Y lo más importante, ¿Que sucede si ni siquiera sabemos cuál es el


nombre de la propiedad pero es el valor de otra variable o se obtiene
como resultado de evaluar una expresión?
Para todos estos casos, el carácter punto no es adecuado y debemos
utilizar una notación más general si queremos acceder a las
propiedades. El formato del operador general para hacer referencia a
un operador es

object [propertyNameExpression]

donde propertyNameExpression es una expresión javascript


cuya evaluación como cadena deine el nombre de la propiedad a la
que se quiere hacer referencia. Por ejemplo, las siguientes son
equivalentes entre ellas:

coche.marca
coche[‘marca’]
coche[‘m’ + ‘a’ + ‘r’ + ‘c’ + ‘a’]

Lo mismo ocurre con estas otras referencias:


var m = ‘marca’; coche[m];
Utilizar el operador general es la única forma de mencionar
propiedades cuyos nombres no deinen identiicadores javascript
válidos, como por ejemplo,
coche[“el nombre de alguna’s propiedades!”]
que contiene caracteres no válidos para identiicadores javascript, o
cuyos nombres son los valores de otras variables.

Construir objetos creando nuevas instancias con el operador new y


asignar cada propiedad utilizando sentencias de asignación
separadas puede ser una tarea tediosa. En el próximo apartado
veremos una notación más compacta y fácil de leer para declarar
objetos y sus propiedades.
Literales de objetos
En la sección anterior, diseñamos un objeto que deinía algunas de las
características de un coche, asignandole una variable llamada
coche. Para ello, utilizamos dos operadores new, una variable
intermedia llamada propietario y una serie de sentencias de
asignación. Esto es monótono (ademas de farragoso y con más
posibilidades para cometer un error) y también es difícil analizar
visualmente la estructura del objeto cuando realizamos una inspección
rápida del código.

Afortunadamente, podemos usar una notación más compacta y fácil de


revisar de forma visual. Considere la siguiente sentencia:

var coche = {
marca : ‘Ford’,
modelo : ‘Mondeo’,
anio : 2013,
fechaCompra : new Date(2013,01,08),
propietario: {

nombre : ‘Juan Reyes’,


profesion : ‘animador’

}
};
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.

Esta notación que se denomina JSON (para más información consulte


la pagina web: http://www.json.org), es preferida por la
mayoría de creadores de páginas, en lugar de la creación del objeto
mediante múltiples asignaciones. Su estructura es simple, un objeto se
deine como un listado de propiedades separadas por comas, todo ello
entre llaves. Cada una se establece listando un nombre y su valor,
separadas por el carácter dos puntos (:).

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

muestra en el ejemplo anterior. Al emplearla como formato de


intercambio, las fechas se suelen expresar como una cadena que
contiene el formato ISO 8601 o como un número que hace referencia a
la fecha como un valor en milisegundos que se obtiene como resultado
de utilizar Date.getTime(). Cuando emplea JSON como formato
de intercambio, existen algunas reglas estrictas que debe seguir, como
escribir los nombres de propiedades entre comillas. Véase
http://www.json.org o RFC 4627
(http://www.ietf.org/rfc/rfc4627.txt) para más
detalles.

Cuando se observa en la propiedad propietario, las declaraciones de


objetos se pueden anidar. Por otra parte, tambien podemos expresar
arrays en JSON colocando la lista con los elementos separados por
comas dentro de corchetes, como en la siguiente expresión:
var algunosValores =
[a,b,c,d,e,f,g,h,i,j,k,l,m,n];

Como hemos visto en los ejemplos de esta sección, las referencias a


objetos se almacenan habitualmente en variables o en propiedades de
otros objetos. Véamos un caso especial de esto último.

Objetos como propiedades de un objeto window


Hasta ahora hemos visto dos formas de lamacenar una referencia a un
objeto javascript: variables y propiedades. Ambas utilizan notaciones
diferentes, como se muestra en el siguiente fragmento de código:

var estaVariable = ‘Antes yo estaba contigo, ahora estoy solo.’;


algunObjeto.estaVariable = ‘No te preocupes, siempre he estado aquí.’;

Cada una de estas sentencias asigna una instancia String (creada


a través de literales) a algo: una variable en la primera sentencia y una
propiedad de objeto en la segunda. ¿Pero estas sentencias realizan
operaciones diferentes? Realmente no.

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 signiica que las siguientes sentencias, si se utilizan fuera del


ámbito de una función, son equivalentes:
var somos = son;
y
window.somos = son;
y
somos = son;

Independientemente de la notación que utilicemos, se creará una


propiedad window llamada somos (si no existe antes) y se le
asignará el valor son. Además, como son no tiene ningun valor, se
asume que es una propiedad de 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:

√ Un objeto javascript es una colección de propiedades sin orden. √


Las propiedades consisten en un nombre y un valor. √ Los objetos se
pueden declarar utilizando literales de objetos. √ Las variables de alto
nivel son propiedades de window.

Ahora veamos que queríamos decir cuando nos referíamos a las


funciones javascript como objetos de primera clase.
Funciones como objetos de primera clase

En muchos lenguajes OO tradicionales los objetos pueden contener


datos y también pueden poseer métodos. Los datos y los métodos
usualmente son conceptos diferentes, sin embargo, Javascript sigue
otro camino.

Las funciones se consideran objetos, como cualquiera de los otros


tipos deinidos en javascript, por ejemplo String, Number o Date.
De la misma forma que en cualquier objeto, las funciones son
establecidas por un constructor javascript, en este caso Function, y
pueden ser:

√ 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.

Que hay en un nombre


Un gran número de programadores de javascript trabajan asumiendo
el falso concepto de que las funciones son entidades deinidas, cuando
realmente no es así. Como con otras instancias de objetos (por ejemplo
String, Date o Number), se hace referencia a ella sólo cuando
son asignadas a variables, propiedades o parámetros.

Consideremos objetos del tipo Number: con frecuencia, expresamos


instancias de Number en su notación literal, como por ejemplo 135.
La sentencia
135;

es perfectamente válida, pero también es inútil. La instancia Number


no servirá para nada a no ser que se asigne a una propiedad o a una
variable, o se vincule con el nombre de un parámetro. Si no es así, no
existe ninguna forma de hacer referencia a ella. Lo mismo se aplica
para las instancias de los objetos Function.

Pero podría preguntarse que pasa con el siguiente código:

function hacerAlgunaCosa(){ alert(‘haces


alguna cosa’);
}

¿Acaso no se crea una función denominada hacerAlgunaCosa?

No es así. Aunque esta notación le pueda resultar familiar y sea muy


utilizada para generar funciones de alto nivel, se trata de la misma
sintaxis empleada por var para crear propiedades window. La palabra
clave function crea, automáticamente, una instancia Function y la
asigna a una propiedad window establecida mediante la función
“ deinida” , como en la siguiente sentencia:

hacerAlgunaCosa = function() { alert(‘haces alguna cosa’);


}

Si le parece extraña, considere otra que utiliza exactamente el mismo


formato, excepto que ahora se utiliza el literal de Number:
unNumeroMaravilloso = 135;

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.

Si nunca ha visto la sintaxis del literal de una función, le podrá parecer


rara. Se compone de la palabra clave function, seguida de su lista
de parámetros entre llaves y luego el cuerpo. Cuando declaramos una
función deinida, se crea una instancia Function y se asigna una
propiedad de window, que se origina automáticamente mediante el
nombre de dicha función. La instancia Function consiste en un
único nombre, al igual que un literal de Number o de String. El
siguiente cuadro ilustra este concepto.

Número 135 Función

(){
alert(’haces alguna cosa’);
}

window

unNumeroMaravilloso hacerAlgunaCosa

Una instancia Function es un objeto sin nombre como el número 135 o


cualquier otro valor de javascript. Sólo se deine cuando se referencia
la misma

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:

function holaMundo() { alert(‘hola a todos!’); } holaMundo = function() {


alert(‘hola a todos!’); } window holaMundo = function() { alert(‘hola a
todos!’); }

Aunque esto pueda parecerse a hacer juego de malabares con la


sintaxis, es importante entender que las instancias Function son
valores que se pueden asignar a variables, propiedades o parámetros,
como las que se realizan sobre otros tipos de objetos. Y, de la misma
forma que ocurre con ellos, las que no poseen un nombre no tienen
ninguna utilidad, a no ser que se asigne a una variable, propiedad o
parámetro a través del cual se les pueda hacer referencia.
Hemos visto ejemplos de asignación de funciones a variables y
propiedades pero podríamos preguntarnos como pasarlas como
parámetros. Vamos a ver la manera de hacerlo y su porqué.

Navegadores Gecko y los nombres de las funciones


Los navegadores basados en el motor de diseño Gecko, como Firefox
y Camino, almacenan el nombre de las funciones definidas utilizando la
sintaxis de alto nivel en una propiedad no estándar de la instancia de
la función
Atento
denominada name. Aunque esto puede que no sea muy útil en
general, en el caso particular de navegadores Gecko es muy
aprovechable para los desarrolladores de plugins y depuradores de
navegadores.
Funciones como callbacks
Las funciones de alto nivel están muy bien cuando nuestro código
sigue un lujo síncrono ordenado pero la naturaleza de las páginas
HTML (una vez abiertas) están muy lejos del sincronismo. Estemos
manejando eventos, iniciando temporizadores o realizando peticiones
Ajax, la naturaleza del código en una página web es asíncrona. Y uno
de los conceptos mas importantes es el de la función callback.

Utilicemos el ejemplo de un temporizador. Podemos iniciarlo


(supongamos que está establecido en cinco segundos) pasándole el
valor adecuado de la duración al método
window.setTimeout().¿Pero como nos informa este método de
que el temporizador ha terminado para que podamos hacer lo que sea
que estemos esperando hacer? Nos avisara llamando a una función
que le proporcionaremos nosotros.

Cosideremos el siguiente código:


function holaMundo() { alert(‘hola a
todos!’); } setTimeout(holaMundo,5000);

Declaramos una función llamada holaMundo y establecemos el


inicio de un temporizador en 5 segundos, expresaos como 5000
milisegundos en el segundo parámetro. En el primero del método
setTimeout() pasamos una referencia a la función. Pasar una
función como parámetro no es diferente de hacerlo con cualquier otro
valor, igual que asignamos un Number como segundo parámetro.

Cuando el temporizador termina, se llama a la función holaMundo.


Debido a que el método setTimeout() realiza una llamada a un
elemento de nuestro propio código, esta función se denomina
callback.

Este código de ejemplo podría ser considerado ingenuo, por los


desarrolladores de javascript de nivel avanzado, ya que la creación
del nombre holaMundo es innecesaria. A no ser que vaya a ser
llamado en algún lugar de la página, no hay necesidad de crear la
propiedad holaMundo de window para almacenar
momentaneámente la instancia Function, que se pasará como el
parámetro callback. Una forma más elegante de codiicar este
fragmento sería:

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

Los lenguajes OO automáticamente proporcionan los medios para


hacer referencia a la instancia actual de un objeto desde el interior de
un método. En lenguajes como Java y C++, una variable llamada this
señala la instancia actual. En javascript, existe un concepto similar e
incluso se utiliza la misma palabra clave this, que también permite el
acceso a un objeto asociado con una función. Pero hay que advertir a
los programadores de lenguajes OO que tengan precaución. La
implementación de this en javascript se diferencia de la de sus
homólogos de forma sútil pero signiicativa.

En lenguajes OO basados en clases, el puntero this, generalmente,


hace referencia a la instancia de la clase para el método que se ha
declarado. En javascript, donde las funciones son objetos de primera
clase que no son declarados como parte de nada, el objeto
referenciado por this (denominado contexto de la función) no se
determina por como se declara la función, sino por como se invoca.
Esto signiica que la misma función puede tener diferentes contextos
dependiendo de como se realice la llamada Al principio, puede
parecer algo extraño pero es bastante útil.
En el caso por defect, el contexto (this) de la invocación de una función
es el objeto cuya propiedad contiene la referencia utilizada para hacer
la llamada. Volvamos al ejemplo del coche para aclarar este concepto,
modiicando la creación del objeto del siguiente modo (con la parte
añadida resaltada):
var coche = {

marca : ‘Ford’,
modelo : ‘Mondeo’,
anio : 2013,
fechaCompra : new Date(2013,01,08),
propietario: {

nombre : ‘Juan Reyes’,


profesion : ‘animador’
}
quienSoy : function() {
return this.anio+’ ‘+this.marca+’
‘+this.modelo;
}
};
Hemos añadido al ejemplo original una propiedad llamada quienSoy
que hace referencia a la instancia Function. La nueva jerarquía del
objeto se muestra en la siguiente igura:

Cadena Ford
Cadena Mondeo Número 2013
Fecha
08-01-2013
Cadena Juan Reyes

Cadena animador nombre profesion

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.

Aunque esto puede ser el comportamiento implícito habitual, javascript


nos proporciona los medios para controlar explícitamente lo que se va
a utilizar como contexto de la función.

Podemos deinirlo con lo que queramos, haciendo la llamada a través


de los métodos call() o apply() de Function.
Sí, incluso las funciones tienen métodos deinidos por el constructor de
Function por ser objetos de primera clase.

El método call() hace la llamada especiicando como primer parámetro


el objeto que se va a utilizar como contexto, mientras que el resto de
los parámetros se convertiran en los de la función invocada (el
segundo parámetro de call() se convierte en el primer argumento de la
función invocada y así sucesivamente). El método apply() funciona de
forma similar, excepto en que lo que se espera de su segundo
parámetroes que sea un array de objetos, los cuales se convierten en
los argumentos de la función invocada. Por si esto le parece algo
confuso, ha llegado el momento de realizar un ejemplo más completo.
Considere el código del ejemplo siguiente:

Ejemplo 1. El valor del contexto de la función depende de cómo se


llama a la misma.
nombre del archivo - funcionContexto.html
<!DOCTYPE html>
<html>

<head>
<title>funcion Contexto</title> <script>

var o1 = { handle:’o1’ }; var o2 = {


handle:’o2’ }; var o3 = { handle:’o3’ };
window.handle = ‘window’;

function quienSoy(){ return this.handle;


}

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.

La secuencia de alertas ilustra lo siguiente:

√ Cuando la función se llama directamente como una función de alto


nivel, su contexto es la instancia window(línea 13 del código).
√ Cuando se llama como la propiedad de un objeto (en este caso o1),
este se convierte en el contexto de la función (línea 14 del código). Se
puede decir que la función actúa como un método para dicho objeto,
como en los lenguajes OO. Pero tenga cuidado al considerar esta
analogía. Se puede equivocar fácilmente si no es meticuloso, como se
verá en el resto de este ejemplo.
√ Emplear el método call() de Function hace que el contexto se deina
como el objeto que se pasa como primer parámetro a call(), en este
caso o2 (línea 15 del código). En este ejemplo, la función actúa como
un método para o2, aunque no tenga ningún tipo de asociación (ni
siquiera como una propiedad) con o2.
√ Como un call(), utilizar el método apply() de Function deine el
contexto como el objeto que se pasa como primer parámetro (línea 16
Cuando se recurre a comandos y funciones que utilizan callbacks, lo
anterior demuestra ser un concepto importante.

Closures

Para los creadores de páginas con conocimientos de lenguajes OO


tradicionales o programación basada en procedimientos, los
closures serán con frecuencia un concepto difícil de asimilar,
mientras que para aquellos que posean una base en programación
funcional les resultará una idea con la que se sentirán cómodos y
podrán estar familizarizados con ella. Para los no iniciados, vamos a
deinir lo qué son los closures.

Dicho de la forma más sencilla posible: un closure es una instancia


Function asociada con las variables locales de su entorno que son
necesarias para su ejecución.

Cuando se declara una función, ésta tiene la capacidad de hacer


referencia a cualquier variable que este en su ámbito en el punto de
ejecución en el que se declara. Esto es previsible y no debería ser una
novedad para los desarrolladores de cualquier nível. Pero, con
closures, estas variables son llevadas junto con la función incluso
después de que el lugar de la declaración esté fuera del ámbito,
cerrando la declaración.

La capacidad de las funciones callbacks de hacer referencia a


variables locales cuando ya han sido declaradas es una herramienta
esencial para escribir javascript eicaz. Utilizando de nuevo un
temporizador, veamos el ejemplo ilustrativo siguiente:

Ejemplo 2. Los closures permiten el acceso al ámbito de la declaración


de una función.

nombre del archivo - funcionContexto.html


<!DOCTYPE html>
<html>

<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.

Pero cuando abrimos la página y dejamos que se ejecute durante unos


instantes, vemos el resultado que se muestra en la imagen 1 2.

Como podemos observar, funciona, ¿pero, por qué? Aunque es cierto


que el bloque en el cual se declara local está fuera del ámbito cuando
el manejador ready inaliza, el closure creado por la declaración de la
función, que incluye a la variable local, permanece en su interior
mientras la función existe.

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

la creación de closures (aunque ésta no sea su intención) pero, por


otra, puede hacer difícil su localización dentro del código. Los closures
no intencionados pueden tener consecuencias no previstas. Por
ejemplo, las referencias circulares pueden conducir a pérdidas de
memoria. Un ejemplo clásico de esto es la creación de elementos del
DOM que vuelven ha hacer referencia a variables closure, evitando
que éstas se puedan recuperar.

Otra característica importante de los closures es que el contexto de una


función nunca se incluye como parte de los mismos. Por ejemplo, el
siguiente código no se ejecutará según lo previsto:
...
this id = ‘algunID’;
$(‘*’).each(function(){

alert(this id); });

Recuerde que cada invocación de la función tiene su propio contexto,


por lo que en el código anterior el contexto de la función del callback
que se pasas a each() es un conjunto envuelto y no la propiedad de la
función externa deinida como ‘algunID’. Cada invocación a callback
muestra un cuadro de diálogo de alerta donde se onserva
sucesivamente el id de cada elemento del conjunto envuelto.

Cuando necesitamos acceder al objeto que actúa como contexto en la


función externa, podemos utilizar una sintaxis habitual que crea una
copia de la referencia this en una variable local que será incluida en el
closure. Considere el siguiente cambio en el ejemplo anterior:

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>.

Este elemento deine una región en el documento destinada a ser


usada como un canvas bitmap donde el código script, puede ser usado
para renderizar gráicos de forma interactiva. Seguramente usted
notará que la sintaxis markup de esta etiqueta es relativamente corta
comparado con lo que realmente es requerido para poder dibujar y
usar la tecnología que esta dentro de esta, lo que nos lleva a muchos a
preguntarnos, como trabaja este objeto canvas?.

Como trabaja el objeto <canvas>

Canvas es una herramienta muy poderosa, para dibujar, crear juegos,


animaciones, etc., la mayoría de profesionales se dirigen a este como
de ‘modo inmediato’. Cuando trabajas en canvas, todo lo que haces es
dibujar pixeles dentro de la página. Canvas no tiene ni idea en
absoluto de tus intenciones al dibujar o crear contenido en el. Todo
trata acerca de pixeles, la mayoría de las creaciones canvas limpian
completamente el canvas entre cada fotograma y redibuja cada cosa
en una posición actualizada.

Atributos y métodos del objeto <canvas>

El objeto canvas posee dos propiedades que pueden ser accesadas


muy fácilmente a través de código javascript, estas propiedades son:
width y height. Estas dan información acerca del ancho y alto de
renderizado del objeto canvas dentro de una página HTML.
Width

Descripción Se reiere al ancho de la supericie de dibujo del elemento


canvas. Por defecto los buscadores crean el elemento canvas con el
mismo tamaño que la supericie de dibujo, Si de cualquier manera tu
sobreescribes el tamaño del elemento canvas desde CSS, entonces el
buscador escalará la supericie de dibujo para reparar el elemento.

Tipo Entero no negativo


Valores permitidos
Valores por defecto

Cualquier entero valido no negativo. Tu puedes agregar un signo mas


o un espacio en blanco al comienzo, pero tu no puedes agregar un
suijo px. ya que técnicamente no esta permitido por la especiicación
canvas.
300

Height

Descripción
Tipo
Valores permitidos
Valores por defecto

Se reiere al alto de la supericie de dibujo del elemento canvas. Por


defecto los buscadores crean el elemento canvas con el mismo tamaño
que la supericie de dibujo, Si de cualquier manera tu sobreescribes el
tamaño del elemento canvas desde CSS, entonces el buscador
escalará la supericie de dibujo para reparar el elemento.
Entero no negativo

Cualquier entero valido no negativo. Tu puedes agregar un signo mas


o un espacio en blanco al comienzo, pero tu no puedes agregar un
suijo px. ya que técnicamente no esta permitido por la especiicación
canvas.
150

Ejemplo de sintaxis del canvas


<!DOCTYPE html> <html>

<head> <title>Ejemplo de sintaxis


canvas</title> <style>
...
#canvas {
...
}
</style>

</head>
<body>
<canvas id=’canvas’ width=’600’ height=’300’>

Tu navegador no soporta canvas de HTML 5


</canvas>
<script src=’ejemplo.js’></script>

</body>
</html>

Otros atributos de la etiqueta canvas de HTML5 son listados en la


siguiente tabla juntamente con algunos de sus valores permitidos.

atributos

accesskey class contenteditable contextmenu data-X dir draggable


hidden id itemid itemprop itemref itemscope itemtype lang spellcheck
style tabindex title

valor

lista espaciada de clave(s) de acelerador nombre(s) de la clase


true | false | inherit
id de menú
datos deinidos por el usuario
ltr | rtl
true | false | auto
hidden
identiicador único alfanúmerico
id de los microdatos en el formato URL valor de microdata
lista de D´s que puedan contener microdata itemscope
tipo de microdatos en el formato URL código de lenguaje
true | false
información de estilos
number
releja el titulo de la sección o del canvas

Tabla 1 - atributos HTML5 de la etiqueta canvas y sus valores


Contexto 2d
El contexto 2d HTML5 (El objeto
canvasRenderingContext2d), recuperado a través de una
llamada al método getContext() del objeto canvas, es el lugar
donde se lleva a cabo toda la acción. El
canvasRenderingContext2d contiene todos los métodos y
propiedades que necesitamos para dibujar dentro del objeto canvas.
El canvasRenderingContext2d (o contexto como lo
llamaremos de aquí en adelante), usa un sistema de coordenadas
cartesianas con 0,0 en la esquina superior izquierda del objeto
canvas, e incrementa su valor a medida que avanza desde la izquierda
y hacia abajo.
Sin embargo, todas estas propiedades y métodos son usados en
conjunto con el estado actual (current state). Un concepto que
realmente debe ser aximilado para llegar a entender en profundidad
como trabajar con el elemento canvas de HTML5.
he current state is actually a stack of drawing state that apply globally to
the entire canvas. Tu manipularás todos estos estados cuando estés
dibujando en el objeto canvas. Estos estados incluyen:

Una matriz de transformación


Métodos para escalar, rotar, transformar y trasladar o mover.
La región clipping
Creada con el método clip().
Las propiedades del contexto
Propiedades incluidas: strokeStyle, illStyle, globalAlpha, lineWidth,
lineCap, lineJoin, miterLimit, shadowOfsetX, shadowOfsetY, shadowBlur,
shadowColor, globalCompositeOperation, Font, textAlign y
textBaseline.
Existen también seis métodos públicos para el objeto canvas. ellos son:
getContext(), setContext(), supportsContext(),
toDataURL(),toDataURLHD(), toBlob(),
toBlobHD() y commit(). Estos métodos devuelven una
cadena de datos que representa la imagen bitmapeada del objeto
canvas como se encuentra renderizada en la actualidad. Esto es similar
a una captura de pantalla. Al proveer diferentes tipos M ME como
parámetros, tú puedes recuperar los datos en diferentes formatos. El
formato básico es image/png, pero image/jpeg y otros formatos
también pueden ser recuperados.

getContext(contextId)

Descripción
Devuelve el contexto gráico asociado con el elemento canvas. Cada
elemento

canvas tiene un contexto y cada contexto esta asociado con un


elemento canvas, es decir, si llamas nuevamente al método
getContext(“2d”), este te devolverá el mismo objeto. Este
método acepta un argumento que especiica el tipo de dibujo que
quieres hacer con el canvas. Pasando la cadena 2d obtiene el objeto
canvasRenderingContext2d, con el que puedes dibujar en
dos dimensiones.
El estándar HTML5 sólo deine ‘2d’ como argumento válido para este
método. Paralelamente existe otro estándar llamado WebGL,
desarrollado para gráicos 3d en navegadores que lo soportan. Tu
puedes pasar ‘webgl’ como argumento para este método y obtener
un objeto que te permite renderizar en 3D.

Ahora bien, para efectos de este libro, solo usaremos el objeto:


canvasRenderingContext2d.

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)

Descripción Devuelve false si el método getContext() es


llamado con los mismos argumentos y true en caso contrario.
Este valor devuelto no es garantía de que el método
getContext() devolverá o no un objeto, ya que las condiciones
(por ejemplo, la disponibilidad de recursos del sistema) pueden variar
con el tiempo.
Lanza una excepción InvalidStateError si se han utilizado los
métodos setContext() o transferControlToProxy().

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.

Ejemplo del método toDataURL():

// Copia el contenido del canvas en un


elemento image // y pega esta imagen en el
documento
var canvas =
document.getElementById(“canvas”); var image
= document.createElement(“img”);
image.src = canvas.toDataURL();
document.body.appendChild(image);

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 elemento canvas. El primer argumento de este método es
una función que invoca el navegador con una referencia al blob. El
segundo argumento especiica el tipo de imagen, tal como
image/png, la cual es el valor por defecto. El argumento inal
representan un nivel de calidad que va desde 0 hasta 1.0 ambos
inclusive, para imágenes jpeg. Otros argumentos deberían ser
agregados a este método en el futuro para mayor control en las
características de la imagen. Este método devuelve los datos a una
resolución de 96 ppp.

toBlobHD(callback, type, args…)

Descripción
crea un blob que representa un archivo que contiene la imagen del
elemen

to canvas. El primer argumento de este método es una función que


invoca el navegador con una referencia al blob. El segundo argumento
especiica el tipo de imagen, tal como image/png, la cual es el valor
por defecto. El argumento inal representan un nivel de calidad que va
desde 0 hasta 1.0 ambos inclusive, para imágenes jpeg. Otros
argumentos deberían ser agregados a este método en el futuro para
mayor control en las características de la imagen. Este método devuelve
los datos con la resolución nativa del canvas.
transferControlToProxy()

Descripción
Devuelve un objeto CanvasProxy que se puede utilizar para
transferir el

control desde este canvas a otro documento (por ejemplo, un iframe


de otro origen) o de un worker.
Lanza una excepción InvalidStateError si se han utilizado los
métodos getContext() o setContext().

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

gráicos salvados. Esto permite cambiar temporalmente el estado de los


gráicos para luego poder ser restaurados con una llamada al método
restore(). El estado gráico de un canvas incluye todas las
propiedades del objeto context (excepto para las propiedades de
solo lectura). Están también incluidas la matriz de transformación
resultante de la llamada a los métodos rotate(), scale() y
translate().Adicionalmente, esto incluye también a la región
clipping especiicada por el método clip().Note, sin embargo, que el
path actual y la posición actual no son parte del estado gráico, por lo
tanto no es posible salvarlos con este método.

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;

nombre del archivo - reloj.html


<!DOCTYPE HTML>
<title>Clock</title>
<canvas></canvas>
<script>

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

Métodos primarios de transformación

scale(float x, float y)

Descripción Agrega una escala de transformación a la matriz de


transformación. Los argumentos x e y deinen cuanto ajustar en el eje
X e Y respectivamente, es decir, expande o contrae distancias a lo
largo del eje de las abscisas X e Y. Pasar valores negativos a este
método da como resultado el voltear el original a través de las
abscisas, es decir, es como si estuviera relejada en un espejo (ver
imagen2.1). Ejemplo: context scale(2,2);

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>

<head> <title>Ejemplo del m&eacute;todo


scale()</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=’scale.js’></script>
</div>
</body>
</html>

nombre del archivo - scale.js


var canvas =
document.getElementById(“canvas”); var
context = canvas.getContext(“2d”);
var rectWidth = 150;
var rectHeight = 75;
dibujarCuadricula(context, ‘lightgrey’, 10,
10);

//trasladamos el contexto a la posición


indicada en las coordenadas x e y
context.translate(300, 200);

//escalamos el ancho a la mitad


context.scale(1, 0.5);
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();
}
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(); }
}

El resultado se mostrará mas o menos de esta manera:


imagen 2.3
- método rotate().
referenc
ia
Ejemplo de aplicación del método rotate();

nombre del archivo - rotate.html


<!DOCTYPE html>
<html>

<head>
<title>Ejemplo del m&eacute;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>

nombre del archivo -rotate.js


var canvas =
document.getElementById(“canvas”); var
context = canvas.getContext(“2d”);

var rectWidth = 150;


var rectHeight = 75;
dibujarCuadricula(context, ‘lightgrey’, 10,
10);

//trasladamos el contexto a la posición


indicada en las //coordenadas x e y
context.translate(300, 200);

//rotamos el contexto 45 grados en el sentido


de las agujas //del reloj
context.rotate(Math.PI / 4);
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();
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();

nombre del archivo - translate.html


<!DOCTYPE html>
<html>

<head>
<title>Ejemplo del m&eacute;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>

nombre del archivo - translate.js


var canvas =
document.getElementById(“canvas”); var
context = canvas.getContext(“2d”);

var rectWidth = 150;


var rectHeight = 75;
dibujarCuadricula(context, ‘lightgrey’, 10,
10);

//trasladamos el contexto a la posición


indicada en las //coordenadas x e y
context.translate(300, 200);

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().

transform(float a, float b, float c, float d, float e, float f)

Descripción
Aplica la transformación especiicada por los seis (6) argumentos. Las
trans

formaciones soportadas por canvas son comúnmente conocidas como


aine transform. Estos tipos de transformaciones pueden modiicar
distancias entre dos puntos y los ángulos entre las líneas. Los
argumentos de este método especiica los seis argumentos no triviales
de una matriz de transformación aine T : A C E
BD F
001

Una transformación aine ordinaria puede ser descrita por los


argumentos desde la a hasta la f por la siguiente ecuación:
X’ = AX + CY + E
Y’ = BX + DY + F

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

una transformación shear de un factor k paralela al eje de las abscisas


X, llamar a transfrom(1,0,k,1,0,0) y para el eje de las abscisas Y llamar a
transform(1,k,0,1,0,0).
Ejemplo:
//rotar en el sentido de las agujas del reloj
alpha radianes del punto (x,y)
function rotar(c, alpha, x, y){

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); }

//preparar una transformación shear


function shear(c, kx, ky){
context.transform(1, ky, kx, 1, 0, 0);
Ejemplo de aplicación del método transform();

nombre del archivo - transform.html


<!DOCTYPE html>
<html>
<head>
<title>Ejemplo del m&eacute;todo transform()
</title> <style>
#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>
<scriptsrc=’transform.js’></script>
</div>
</body>
</html>

nombre del archivo - transform.js


var canvas =
document.getElementById(“canvas”); var
context = canvas.getContext(“2d”);

var rectWidth = 150;


var rectHeight = 75;
dibujarCuadricula(context, ‘lightgrey’, 10,
10);

//matrix de traslación: //1 0 tx


//0 1 ty
//0 0 1

var tx = canvas.width / 2; var ty =


canvas.height / 2;
//aplicar transformación context.transform(1,
0, 0, 1, tx, ty);
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();
}
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();

Otro Ejemplo de aplicación del método transform();

nombre del archivo - transformAvanzado.html <!DOCTYPE


html>
<html>

<head> <title>Otro ejemplo del m&eacute;todo


transform() </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=’transformAvanzado.js’></script>
</div>
</body>
</html>

nombre del archivo - transformAvanzado.js


var canvas =
document.getElementById(‘canvas’), context =
canvas.getContext(‘2d’),
texto=’transfromar’,
angulo = Math.PI/45,
clockwise = true,
tamFuente = 100,
origen = { },
pausa = true,
escala = 1.028;

//
Funciones....................................
function dibujarTexto() {
context.strokeText(texto, 0, 0); }
// Manejadores de
eventos..................................

canvas.onclick = function() { pausa = !pausa;


if (!pausa) {

clockwise = !clockwise; escala = 1/escala; }


};
//
Animacion....................................
setInterval(function() {
if (!pausa) {
context.clearRect(-origen.x, -origen.y,
canvas.width, canvas.height);

context.rotate(clockwise ? angulo : -angulo);


context.scale(escala, escala);
dibujarTexto();

}
}, 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();

El resultado se mostrará mas o menos de esta manera:


nombre del archivo - setLineDash.html
<!DOCTYPE html>
<html>

<head>
<title>Aplicaci&oacute;n del m&eacute;todo
setLineDash() </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=’150’>

Tu navegador no soporta canvas de HTML5


</canvas>
</div>

<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

La mayoría de los sotware de dibujo e ilustración, tales como scalable


vector graphics (SVG), apple´s cocoa y/o adobe illustrator son basados
en path. Con estos sotware tu deines un path que consecuentemente
podrás delinear, rellenar o ambos inclusive.

Path y las tintas invisibles

Una buena práctica para crear un path, y luego delinearlo o rellenarlo,


es crearlo con tintas invisibles. Todos sabemos que para leer un
documento escrito con tintas invisible necesitas elementos extras que
te hagan la tarea mucho mas sencilla.

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()

Descripción Resetea el path actual, limpiando todos los subpaths


desde el path actual. Este método es llamado cuando vas a comenzar
un nuevo path.

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.

arc(float x, float y, float radius, float starAngle, float


endAngle, boolean anticlockwise)

Descripción
Agrega los subpaths que vienen a representar un arco o un círculo al
actual

path. Tú puedes controlar la dirección en que se dibujarán los


subpaths (a diferencia del método rect()), con una variable
booleana. Sí existe un subpath cuando el método es llamado, este
dibujará una línea desde el último punto en el subpath existente hasta
el primer punto delante del arco del path. Este método toma 6
parámetros, los primeros dos parámetros representan un punto en el
centro de un círculo; el tercer parámetro representa el radio del círculo,
el cuarto y quinto parámetros representan el angulo inicial y el angulo
inal, respectivamente del círculo que el navegador dibujará. El último
argumento es opcional y representa la dirección en la cual el
navegador dibujara el arco. Si el valor se establece en false, como
viene por defecto, el navegador dibujará el arco en dirección al
sentido de las agujas del reloj, si por el contrario se establece en true,
el navegador dibujará el arco en la dirección contraria al sentido de
las agujas del reloj.

la linea gruesa corresponde a:


radio
linea inicial
rgba(0,0,0,0.5);
-box-shadow: 4px 4px 8px
rgba(0,0,0,0.5);
}
</style>
</head>
<body>
<canvas id=’canvas’ width=’400’ height=’300’>
Tu navegador no soporta canvas de HTML 5
</canvas>
<script src=’arc.js’></script>
</body>
</html>

nombre del archivo - arc.js


var canvas =
document.getElementById(‘canvas’), context =
canvas.getContext(‘2d’);
function drawScreen() {

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);

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();

}
context.restore(); }
dibujarCuadricula(context, ‘lightgray’, 10,
10); drawScreen();

El resultado se mostrará mas o menos de esta manera:


de los siguientes métodos:

context.moveTo(x, y);
context.lineTo(x + w, y);
context.lineTo(x + w, y + h);
context.lineTo(x, y + h);
context.closePath();

Ejemplo de aplicación del método rect();

nombre del archivo - rect.html


<!DOCTYPE html>
<html>

<head>
<title>
Aplicaci&oacute;n del m&eacute;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’>

Canvas not supported


</canvas>
</div>

<script src=’javascript/rect.js’></script>
</body>
</html>

nombre del archivo


- rect.js
var canvas =
document.getElementById(‘canvas’), context =
canvas.getContext(‘2d’);

//borramos todos los subpaths desde el path


actual context.beginPath();
//agregamos un subpath con cuatro puntos
context.rect(10, 10, 100, 100);
//delineamos el subpath que contiene los
cuatro puntos context.stroke();
//borramos todos los subpaths desde el path
actual context.beginPath();
//agregamos un subpath con cuatro puntos
context.rect(90, 90, 200, 200);
function rect(x, y, w, h, direccion) {
if (direccion) {
// En sentido contrario a las agujas del
reloj context.moveTo(x, y);
context.lineTo(x, y + h);
context.lineTo(x + w, y + h);
context.lineTo(x + w, y);

}else {
context.moveTo(x, y);
context.lineTo(x + w, y);
context.lineTo(x + w, y + h);
context.lineTo(x, y + h);

}
context.closePath();
}

fill(Path path, optional CanvasFillRule fillRule =


“nonzero”)

Descripción El método rellena el actual path con el color, gradiente o


patrón especiicado por la propiedad del contexto .
Cualquier subpath que no este cerrado, serán rellenos como si el
método closePath() haya sido invocado (Tenga en cuenta que no
causa verdaderamente esos subpaths al ser cerrado). Rellenar un
path no borra el path. Usted puede llamar al método
stroke()despues de llamar al método sin redeinir el path.
Cuando el path se corta a sí mismo o cuando los subpaths se
sobreponen, el método aplica por defecto como argumento opcional la
“ nonzero winding rule” , para determinar que puntos se encuentran
dentro del path y cuales están fuera. Esto signiica, por ejemplo, que si
has deinido un path cuadrado dentro de un path circular y el subpath
del cuadrado esta en la dirección opuesta al path del círculo, entonces
el interior del cuadrado estará fuera del path y no será rellenado.

El método fill()

Este método posee un parámetro opcional w, la cual si no es


especificado o si es la cadena “ cero” , este método debe rellenar todos
los subpaths utilizando la regla del trazado distinta de zero (nonzero
winding rule). Si el parámetro opcional es la cadena “ evenOdd” , este
método debe rellenar todos los subpaths utilizando la regla par-impar
(even

Atento
odd rule). Los subpaths abiertos deben ser implícitamente cerrados
cuando ha sido rellenado (sin afectar los subpaths reales).

stroke(Path path)

Descripción El método stroke() dibuja la línea externa del actual


path. El path deine la geometría de las líneas producidas, pero la
apariencia visual de esta línea depende de las propiedades
strokeStyle, lineWidth, lineCap, lineJoin y
miterLimit. El término stroke se reiere al lápiz o pincel que
dibujará la línea, esto signiica “ dibujar la línea externa de” . El método
stroke() contrasta con el método , el cual rellena el interior del
path mientras que este delinea la línea externa del path. Puede
aceptar un argumento opcional que representa un objeto path y
puede ser un path svg.

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.

Los paths cuando se rellenan o delinean, deben ser dibujados sin


afectar al path actual predeterminado o cualquier objeto path y deben
estar sujetas a los efectos de sombras, transparencias, region clipping
y operadores de globalComposite.

Ejemplo de aplicación de los métodos arc(), beginPath(),closePath(),


fill(), rect() y stroke():

nombre del archivo - strokeFill.html


<!DOCTYPE html>
<html>

<head> <title> M&eacute;todos de Path</title>


<style>
#canvas {
background: #eeeeee;

}
#radios {
padding: 10px; }
</style>
</head>
<body>

<canvas id=’canvas’ width=’620’ height=’475’>

Tu navegador no soporta canvas de HTML5


</canvas>
<script src=’strokeFill.js’></script>

</body>
</html>

nombre del archivo - strokeFill.js


var context =
document.getElementById(‘canvas’).getContext(

// Dibujar
atributos....................................
context.font = ‘28pt Helvetica’;
context.strokeStyle = ‘green’;

context.lineWidth = ‘1’; // Line width set to


2 for text
//
Textos.......................................
context.strokeText(‘Lineas’, 60, 80);
context.strokeText(‘Ambos’, 440, 80);

//
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();

//arcos con lineas


abiertas..................................
context.beginPath();
context.arc(115, 280, 40, 0, Math.PI*3/2);
context.stroke();
context.beginPath();
context.arc(300, 280, 40, 0, Math.PI*3/2);

context.beginPath();
context.arc(500, 280, 40, 0, Math.PI*3/2);
context.stroke();

// arcos con líneas


cerradas.................................
context.beginPath();
context.arc(115, 400, 40, 0, Math.PI*3/2);
context.closePath();
context.stroke();
context.beginPath();
context.arc(300, 400, 40, 0, Math.PI*3/2);
context.closePath();

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.

Ejemplo de aplicación de los métodos moveTo() y lineTo():

nombre del archivo - cuadricula.html


<!DOCTYPE html>
<html>

<head> <title> M&eacute;todos de moveTo() y


lineTo() </title>

<style>
#canvas {
background: #eaeaea;

}
#radios {
padding: 10px; }

</style>
</head>
<body>

<canvas id=’canvas’ width=’620’ height=’475’>

Tu navegador no soporta canvas de HTML5


</canvas>
<script src=’cuadricula.js’></script>
</body>
</html>

nombre del archivo - cuadricula.js


var context =
document.getElementById(‘canvas’).getContext(

//
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>

<head> <title>Ejemplo del m&eacute;todo


arcTo() </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=’arcTo.js’></script>
</div>
</body>
</html>

nombre del archivo


- arcTo.js
var canvas =
document.getElementById(‘canvas’), context =
canvas.getContext(‘2d’);
dibujarCuadricula(context, ‘lightgrey’, 10,
10);

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.

Ejemplo de aplicación del método quadraticCurveTo():

nombre del archivo - quadraticCurveTo.html


<!DOCTYPE html>
<head>

<title>Dibujar curvas bezier


cuadr&aacute;ticas</title> <style>
body {
}

#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>

nombre del archivo - quadraticCurveTo.js


var canvas =
document.getElementById(‘canvas’), context =
canvas.getContext(‘2d’),
MARGEN_FLECHA = 40,
RADIO = 6,
puntos = [
{ x: canvas.width - MARGEN_FLECHA, y:
canvas.height - MARGEN_FLECHA },

{ 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(); }

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...................................

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.

Ejemplo de aplicación del método bezierCurveTo():

nombre del archivo - bezierCurve.html


<!DOCTYPE html>
<html>

<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’>

Tu navegador no soporta canvas de HTML5


</canvas>
<div id=’controles’>
Color linea:

<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>

- va,y los puntos de control, haz click fuera


de ella para terminar.</p>

<input id=’botonOkInstrucciones’
type=’button’ value=’Okay’ autofocus/>
<input id=’botonNoMasInstrucciones’
type=’button’ value=’No mostrar mas las
instrucciones’/>
</div>

<script src = ‘bezierCurve.js’></script>


</div>
</body>
</html>

nombre del archivo - bezierCurve.js


var canvas =
document.getElementById(‘canvas’),
context = canvas.getContext(‘2d’),
botonBorrarTodo =
document.getElementById(‘botonBorrarTodo’),
selectEstiloLinea=document.getElementById(‘se
checkboxLineasGuias=document.getElementById(‘
instrucciones =
document.getElementById(‘instrucciones’),
botonOkInstrucciones =
document.getElementById(‘botonOkInstrucciones
botonNoMasInstrucciones =
document.getElementById(‘botonNoMasInstruccion

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);

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();

}
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);

if (loc.x > mousedown.x) bandas.left =


mousedown.x; else bandas.left = loc.x;
if (loc.y > mousedown.y) bandas.top =
mousedown.y; else bandas.top = loc.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 dibujarLineasGuiasHorizontales (y) {


context.beginPath();
context.moveTo(0, y + 0.5);
context.lineTo(context.canvas.width, y +
0.5); context.stroke();

function dibujarLineasGuiasVerticales (x) {


context.beginPath();
context.moveTo(x + 0.5, 0);
context.lineTo(x + 0.5,
context.canvas.height); context.stroke();

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_

PUNTOS_DE_CONTROL, 0, Math.PI*2, false);


context.stroke();
}

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);
}
}
};

canvas.onmousemove = function (e) {


if (arrastrar || puntoArrastre) {
e.preventDefault(); // previene la seleccion

if(lineasGuias) {
dibujarLineasGuias(loc.x, loc.y);
}
}

if (arrastrar) {
actualizarBandas(loc);
dibujarPuntosControlYFinales();

}
else if (puntoArrastre) {
actualizarPuntoDeArrasytre(loc);
dibujarPuntosControlYFinales();
dibujarCurvasBezier();
}
};

canvas.onmouseup = function (e) {

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...............................

botonBorrarTodo.onclick = function (e) {


context.clearRect(0, 0, canvas.width,
canvas.height); dibujarCuadricula(context,
ESTILO_CUADRICULA, ESPACIO_CUADRICULA,

ESPACIO_CUADRICULA);
editar = false; arrastrar = false;
};

selectEstiloLinea.onchange = function (e) {


context.strokeStyle =
selectEstiloLinea.value;
};
checkboxLineasGuias.onchange = function (e) {
lineasGuias = checkboxLineasGuias.checked;
};

// Manejadores de eventos -
instrucciones......................

botonOkInstrucciones.onclick = function (e) {


instrucciones.style.display = ‘none’;
};

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);

El resultado se mostrará mas o menos de esta manera:


en su plenitud y seguir dibujando lo que hayas empezado, por
supuesto si es lo que quieres, de lo contrario, el canvas estará limitado
a esta área durante toda la operación de dibujo. Este método puede
aceptar como argumentos opcionales un objeto path, que puede ser
un path svg y el otro argumento, tabién opcional es indicar la regla
de relleno que se desea usar, por defecto si no se indica alguna otra,
se usará la ‘nonzero winding rule.’

Ejemplo de aplicación del método clip():

nombre del archivo - clip.html


<!DOCTYPE html>
<html>

<head> <title>Ejemplo del m&eacute;todo


clip()</title> <style>

#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>

nombre del archivo


- clip.js
var canvas =
document.getElementById(‘canvas’), context =
canvas.getContext(‘2d’);

//
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);

setTimeout( function (e) {


context.clearRect(0, 0, canvas.width,
canvas.height); dibujarCuadricula(context,
‘lightgrey’, 10, 10); dibujarTexto();

}, 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......................................

canvas.onmousedown = function (e) { animar();


};

// Iniciar
aplicación...................................
dibujarCuadricula(context, ‘lightgrey’, 10,
10); dibujarTexto();

El resultado se mostrará mas o menos de esta manera:


especiicado esta dentro o en los extremos del path actual y false en
caso contrario.

Ejemplo de aplicación del método isPointPath():


nombre del archivo
- isPointInPath.html
<!DOCTYPE html>
<html>
<head>

<title>Aplicaci&oacute;n del m&eacute;todo


isPointInPath()</title>
<style>
body {
}

#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’>

Tu navegador no soporta canvas de HTML5


</canvas>
</div>
<script src=’isPointInPath.js’></script>

</body>
</html>

nombre del archivo - isPointInPath.js


var canvas =
document.getElementById(‘canvas’), context =
canvas.getContext(‘2d’);

//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.lineJoin = ‘miter’; context.lineWidth


= 30;
context.font = ‘24px Helvetica’;

context.strokeStyle = ‘blue’;

context.rect(200,100,200,200); //manejadores
de
eventos......................................
context.canvas.onmousedown = function (e) {

if (context.isPointInPath(200,150)) {
} };

El resultado se mostrará mas o menos de esta manera:


segundo y tercer argumento representan las coordenadas del punto a
ser chequeado.La coordenada x debe ser un valor entre 0 y
canvas.width y la coordenada y debe ser un valor entre 0 y
canvas. height.

scrollPathIntoView(path, element)

Descripción Este método permite el desplazamiento por la trayectoria


de la vista actual. Su uso está destinado principalmente en
aplicaciones móviles o dispositivos de pequeñas dimensiones. Usando
este método, los desarrolladores pueden mover a la vista actual
aquella parte del canvas que se encuentra fuera de la pantalla. Este
método puede aceptar como primer argumento opcional un objeto
path que puede ser un path svg.
Cuando este método es invocado se deben seguir los siguientes
pasos: 1. Deje que el area especíicada sea el rectangulo del cuadro
limite de la trayectoria prevista.
2. Deje que el descendiente hipotetico descendiente sea un hipotetico
elemento que es un descendiente renderizado del elemento canvas,
cuyas dimensiones son las del rectangulo especiicado.
3. El desplazamiento del hipotetico descendiente dentro de la vista con
la coniguración de la marca superior.
4. Opcionalmente, informar al usuario de que el simbolo de
intercalación y/o selección cubre el rectangulo especíicado del canvas.
Los user agents pueden esperar hasta la próxima vez que el ciclo de
eventos alcance su paso “ actualizar el renderizado” de informar
opcionalmente al usuario.
Nota destacada!

“ Informar al usuario, como se usa en esta sección, puede significar


llamar a una API de accesibilidad al sistema, lo cúal notificará a las
tecnologías de asistencia, tales como herramientas de magnificación.
Para conducir correctamente la magnificación basado en un cambio de
foco, una API de accesibilidad del sistema de conducción de un

Atento

amplificador de pantalla, necesita de los límites para el objeto que se


terminará centrando. Los métodos anteriores tienen por objeto permitir
esto al permitir que el user agent informe del cuadro delimitador de la
ruta utilizada para hacer que el anillo de enfoque limite los elementos
del elemento pasado como argumento, si ese elemento esta enfocado,
y la caja de delimitación de la zona a la que el user agent se desplaza
como cuadro delimitador de la selección actual.

ellipse(float x, float y, float radiusX, float radiusY, float


rotation, float startAngle, float endAngle,boolean
anticlockwise)

Descripción
Dibuja una elipse según los argumentos especíicados. Si la trayectoria
del obje

to no posee un subpath, este método añade una línea recta desde el


último punto del subpath al punto de inicio del arco. Luego añadirá los
puntos de inicio y inal de la circunferencia y las conecta con un arco.
Este método posee 8 argumentos:
x e y representan las coordenadas, en pixeles, del centro de el arco
en relación con la esquina superior izquierda del canvas.
radiusX y radiusY representan las coordenadas, en pixeles, de
la ruta a seguir por el arco.
rotation representa a la rotación, dada en radianes, y signiica que
el eje semi-mayor esta inclinado en dirección al sentido de las agujas
del reloj desde el eje x, es decir, podríamos decir que representa
cuanto la elipse gira. startAngle representa el ángulo de inicio,
en radianes, donde 0 representa las 3 en punto de el arco.
endAngle representa el angulo inal, dado en radianes.
anticlockwise representa la dirección del arco a dibujar, si se
establece en true, el arco es dibujado en sentido contrario a las agujas
del relos, es decir de principio a in y por el contario si se establece en
false el arco es dibujado en la dirección del sentido de las agujas del
reloj, de principio a in.
Si dibujas una elipse full, la dirección es lo menos importante, pero, si
dibujas solo parte de una elipse, entonces, la dirección es muy
importante. Este método devuelve un objeto DOM del tipo nodo.

Ejemplo de aplicación del método ellipse:

nombre del archivo - elipse.html


<!DOCTYPE html>
<html>
<head>
<title>Dibujar elipse</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>

<canvas id=”canvas” width=”600” height=”400”>

Tu navegador no soporta canvas de HTML5


</canvas>
<script src=’elipse.js’></script>

</body>
</html>

nombre del archivo


- elipse.js
var canvas =
document.getElementById(‘canvas’), context =
canvas.getContext(‘2d’);

function dibujarCuadricula(context, color,


stepx, stepy) { context.save();
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();

}
context.restore();
}

function drawScreen() { var x = 15;


var y = 15;
var rx = 20;
var ry = 10;
var rotacion = 0; var inicio = 0;

var direccion = false;

context.beginPath();
context.strokeStyle = “black”;
context.lineWidth = 2;

//full circle
context.stroke(); context.closePath();

}
dibujarCuadricula(context, ‘lightgray’, 10,
10); drawScreen();

resetClip()

Descripción Este método sustituye la actual región clipping por otra


cuyas dimensiones se establecen comenzando por el punto (0,0) del
canvas (esquina superior izquierda) y su altura y anchura se
corresponde con el espacio de coordenadas del canvas, es decir, se
extiende por todo el alto y ancho de la supericie del canvas.

Métodos de Rectangulos
La API de Canvas provee tres métodos para aclarar o limpiar, dibujar y
rellenar rectángulos, respectivamente:

clearRect(float x, float y, float width, float height)

Descripción Aclara todos los pixeles en la intersección del rectángulo


especiicado y la actual región clipping. Por defecto la región clipping
es del tamaño del canvas, es decir, si tu no has cambiado la región
clipping, los pixeles aclarados son exactamente los pixeles
especiicados por los argumentos del método. Aclarar los pixeles
signiica llevar el color a un completamente negro transparente.
Efectivamente limpia o aclara el pixel, permitiéndole al canvas
mostrarse.
strokeRect(float x, float y, float width, float height)

Descripción Dibuja la línea exterior deinida por un punto de inicio


especiicada en los argumentos x e y, desplegándose hacia los
siguientes argumentos width y height. Para este método use los
atributos: lineWidth, lineCap, lineJoin, miterLimit y
strokeStyle para determinar como debe mostrarse el stroke.
Si especiicas un valor de 0 en el width o el height, el método
dibujará una línea horizontal o vertical, respectivamente. El método no
hará nada si le das un valor de 0 a ambos argumentos (width y
height).

fillRect(float x, float y, float width, float height)

Descripción Rellena el rectángulo especiicado con el atributo . Si


especiicas un valor de 0 en el width o en el height, el método no
hará absolutamente nada.

Ejemplo de aplicación de los métodos de rectangulos:

nombre del archivo - rectángulos.html


<!DOCTYPE html>
<html>
<head>
<title>Dibujar rectangulos</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>

<canvas id=”canvas” width=”600” height=”400”>

Tu navegador no soporta canvas de HTML5


</canvas>
<script src=’rectangulos.js’></script>

</body>
</html>

nombre del archivo - rectángulos.js


var canvas =
document.getElementById(‘canvas’), context =
canvas.getContext(‘2d’);
context.lineJoin = ‘miter’;
context.lineWidth = 30;
context.font = ‘24px Helvetica’;

context.strokeStyle = ‘blue’;
context.strokeRect(75, 100, 200, 200);

context.canvas.onmousedown = function (e) {


en esta sección te indicarán como hacerlo.

createLinearGradient(float x0, float y0, float x1, float y1)

Descripción Crea un gradiente lineal. Los parámetros pasados a este


método representan dos puntos la cual especiica la línea de gradiente.
El método devuelve una instancia de canvasGradient, la cual se
le puede agregar color con el método
canvasGradient.addColorStop().

Ejemplo de aplicación de el método createLinearGradient:

nombre del archivo - linearGradient.html


<!DOCTYPE html>
<html>
<head>
<title>Crear Gradientes Lineales</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>

<canvas id=”canvas” width=”600” height=”400”>

Tu navegador no soporta canvas </canvas>


<script src=’linearGradient.js’></script>

</body>
</html>

nombre del archivo - linearGradient.js


var canvas =
document.getElementById(‘canvas’),
context = canvas.getContext(‘2d’),
gradient =
context.createLinearGradient(canvas.width/2,
canvas. height,canvas.width/2, 100);
gradient.addColorStop(0, ‘blue’);
gradient.addColorStop(0.25, ‘green’);
gradient.addColorStop(0.5, ‘yellow’);
gradient.addColorStop(0.75, ‘orange’);
gradient.addColorStop(1, ‘red’);

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>

<canvas id=”canvas” width=”600” height=”400”>

Tu navegador no soporta canvas </canvas>


<script src=’radialGradient.js’></script>

</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>

nombre del archivo - radialGradient.js


var canvas =
document.getElementById(‘canvas’), context =
canvas.getContext(‘2d’),
gradient = context.createRadialGradient(
canvas.width/2, canvas.height, 200,
canvas.width/2, 100, 100);
gradient.addColorStop(0, ‘yellow’);
gradient.addColorStop(0.25, ‘green’);
gradient.addColorStop(0.5, ‘blue’);
gradient.addColorStop(0.90, ‘purple’);
gradient.addColorStop(1, ‘red’);

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.

Ejemplo de aplicación del método createPattern:

nombre del archivo - pattern.html


<!DOCTYPE html>
<html>

<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>

nombre del archivo - pattern.js


var canvas =
document.getElementById(‘canvas’), context =
canvas.getContext(‘2d’),
repeatRadio =
document.getElementById(‘repeatRadio’),
noRepeatRadio =
document.getElementById(‘noRepeatRadio’),

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

destino (canvas destination)recurso (image source) dy sy dx sx


dh sh
dw imagen 2.23 - esquema de drawImage(). sw
Ejemplo de aplicación del método drawImage:

nombre del archivo - drawImage.html


<!DOCTYPE html>
<html>

<head>
<title>Aplicaci&oacute;n del m&eacute;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’

max=’3.0’ step=’0.01’ value=’1.0’/>


</div>
<canvas id=’canvas’ width=’800’ height=’520’>

Tu navegador no soporta canvas de HTML5


</canvas>
</div>
<script src=’drawImage.js’></script>

</body>
</html>

nombre del archivo - drawImage.js


var canvas =
document.getElementById(‘canvas’), context =
canvas.getContext(‘2d’),
imagen = new Image(),

escalaDeSalida =
document.getElementById(‘escalaDeSalida’);
guiaDeEscala =
document.getElementById(‘guiaDeEscala’),
escala = guiaDeEscala.value,
escala = 1.0,
ESCALA_MINIMA = 1.0,
//
Funciones....................................

function dibujarEscalado() { var w =


canvas.width, h = canvas.height, sw = w *
escala, sh = h * escala;

// Limpiamos el canvas, y dibujamos la imagen


escalada al tamaño del canvas
context.clearRect(0, 0, canvas.width,
canvas.height); context.drawImage(imagen, 0,
0, canvas.width, canvas.height);
// Dibujamos la marca de agua en el frente de
la imagen
dibujarMarcaDeAgua();
// Finalmente, dibujamos el canvas escalado
de acuerdo a la escala // actual, dentro de
el mismo. Note que el canvas recurso y el
context.drawImage(canvas, 0, 0, canvas.width,
canvas.height,
-sw/2 + w/2, -sh/2 + h/2, sw, sh); }

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() {

lineaDos = ‘verano!’, medidaTexto = null,


TAM_FUENTE = 118;

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;

imagen.src = ‘qqq (133).jpg’;


imagen.onload = function(e) {
var radioMaximo = 200;
var porcentaje = 20;
context.drawImage(imagen, 0, 0, canvas.width,
canvas.height);
dibujarMarcaDeAgua();
dibujarTextoEscalado(guiaDeEscala.value);
};

El resultado se mostrará mas o menos de esta manera:


imagen 2.24 - aplicación del método drawImage().
Métodos de textos

fillText(string text, number x, number y, number


maxWidth)

Descripción Este método acepta 4 argumentos de los cuales tres son


requeridos y uno es opcional. El argumento text, representa el texto
a dibujar en el canvas; los argumentos x e y representan el punto de
anclaje (anchor point) de el texto en el canvas. La interpretación de
este punto depende de los valores asignados a las propiedades
textAlign y textBaseline, y por último, esta el argumento
max, el cual es opcional y que especiica el ancho máximo para el
texto. Si el texto llegara a ser mas grande que este valor cuando
dibujamos usando la propiedad font y la actual matriz de
transformación, entonces el texto será dibujado una versión más
pequeña o condensada de la fuente instanciada.

dibuja texto usando la fuente actual y la propiedad . Los argumentos x


e y especiican donde el texto debe dibujarse en el canvas, pero la
interpretación de este argumento depende de las propiedades
textAlign y textBaseline respectivamente. Si textAlign
esta en ‘left’ o en ‘start’ (valor por defecto), para el canvas
signiica que dibujará el texto de izquierda-a-derecha (también su valor
por defecto), y si esta en ‘end’ para el canvas signiica dibujar el texto
de derecha-a-izquierda. Si textAlign esta en ‘center’,
entonces el texto se dibujará horizontalmente centrado en la
coordenada x especiicada. De lo contrario ( si textAlign esta en
‘right’, o ‘end’ para texto de derecha-a-izquierda, o si esta en
‘start’ para texto de izquierda-a-derecha) el texto es dibujado a la
izquierda de la coordenada x especiicada.
SitextBaseline está en ‘alphabetic’ (valor por defecto),
‘bottom’ o ‘ideographic’, la mayoría de los gráicos aparecerán
sobre la coordenada y especiicada. Si textBaseline está en
‘center’ el texto será centrado verticalmente en la coordenada y
especiicada. Y si textBaseline esta en ‘top’ o en ‘hanging’, la
mayoría de los gráicos aparecerán mas abajo de la coordenada y
especiicada.

strokeText(string text, number x, number y, number


maxWidth)

Descripción
Este método acepta los mismos argumentos y trabaja de la misma
manera que

, con la diferencia de que en lugar de aplicársele al relleno de las


fuentes dibujadas, lo hace a la línea externa de la fuente dibujada,
usando para ello la propiedad strokeStyle. strokeText()
produce interesantes efectos gráicos cuando es usado en fuentes mas
grandes, pero es communmente mas usado para dibujar textos que
strokeText().

measureText(string text)

Descripción
Este método acepta solo un argumento: la cadena de texto a ser
medida.

Este método devuelve un objeto TextMetrics que contiene un


conjunto de propiedades que contienen las medidas del texto dado.
Estas medidas estan basadas en la fuente actual , y son todas las
medidas que se pueden obtener al momento de escribir este libro.

Configurar la fuente antes de llamar a measureText

Un error común al llamar a measureText() es que se configura la


fuente después de llamar al método. Recuerde que este método mide
el ancho de una cadena basado en la fuente actual. Es decir, si tu
cambias la fuente después de llamar a este método o lo llamas antes de
configurarla, el ancho devuelto no reflejará el ancho actual

Atento de la fuente actual.

Las medidas del texto

Tu puedes obtener muchas medidas, en pixeles CSS, de una cadena


cualquiera, con las propiedades
width,actualBoundingBoxLeft,actualBoundingBoxR
fontBoundingBoxDescent,
actualBoundingBoxAscent,
actualBoundingBoxDescent, emHeightAscent,
emHeight

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.

Ejemplo de aplicación de los métodos de textos:

nombre del archivo - textos.html


<!DOCTYPE html>
<html>

<head>

<title>Aplicaci&oacute;n de los
m&eacute;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 {

border:2px thin #666; margin-top:5px;


}
#textos{

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”>

Tu navegador no soporta canvas de HTML5.


</canvas>
</div>
<div id=”contenedor”>
<form>

<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&ntilde;o: <input type=”range”
id=”tamañoTexto”
min=”0” max=”200” step=”1” value=”50”/>
<br>
</div>
<div id=”colores”>

Relleno : <select id=”tipoRelleno”>

<option value=”colorFill”>Color de relleno</


option>
<option value=”linearGradient”>Gradiente
lineal</option>
<option value=”radialGradient”>Gradiente
radial</option>
<option value=”pattern”>Patrones</option>
</select>
<br>
Color Texto 1: <input class=”color”
id=”colorRellenoTexto1” value=”0C78F2”/>
<br>
Color Texto 2: <input class=”color”
id=”colorRellenoTexto2” value =”EB0C22”/>
<br>

Rellenar o Delinear : <select


id=”RellenarODelinear”>

<option value=”stroke”>Delinear</option>
<option value=”both”>Ambos</option> </select>
<br>
Alineaci&oacute;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&oacute;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>

Transparencia : <input type=”range”


id=”transparenciaTexto”
min=”0.0” max=”1.0” step=”0.01” value=”1.0”/>

<br>
</div>
<div id=”adicionales”>
min=”-100”
max=”100”
step=”1”
value=”1”/>

<br>

Sombreado Y:<input type=”range”


id=”sombreadoY” min=”-100”
max=”100”
step=”1”
value=”1”/>

<br>

Desenfoque Sombra: <input type=”range”


id=”desenfoqueSombra”
min=”1”
max=”100”
step=”1”
value=”1” />

<br>
Color sombra: <input class=”color”
id=”colorSombra” value=”707070”/>
<br>

Ancho del canvas: <input type=”range”


id=”anchoDelCanvas”
min=”0”
max=”1000” step=”1”
value=”600”/>

<br>
Alto del canvas:
<input type=”range” id=”altoDelCanvas”
min=”0”
max=”1000”
step=”1”
value=”300”/>

<br>

Estilo ancho canvas: <input type=”range”


id=”estiloDeAnchoDelCanvas”
min=”0”
max=”1000”
step=”1”
value=”600”/>

<br>
Estiloalto canvas:

<input type=”range”
id=”estiloDeAltoDelCanvas” min=”0”
max=”1000”
step=”1”
value=”300”/>

<br>

<br>
</div>
<div id=”areaTexto”>

<textarea id=”imageDataDisplay” rows=10


cols=35></textarea><br>
<input type=”button” id=”crearImageData”
value=”Create Image Data”>
</div>
</form>

</div>

<script src=’textos.js’></script> </body>


</html>

nombre del archivo


- textos.js
window.addEventListener(“load”,
iniciarAplicacion, false);
function aplicacionTextos() {

var mensajeBienvenida = “escribe lo que


quieras”; var tamanoFuente = “40”;
var nombreFuente = “palatino”;
var colorRellenoTexto = “#0C78F2”;
var transparenciaTexto = 1;

var sombreadoY = 1;
var desenfoqueSombra = 1;
var colorSombra = “#707070”;
var alineacionVertical = “middle”; var
alineacionHorizontal = “center”;

var grosorFuente =”normal”;


var estiloFuente = “normal”; var tipoRelleno
= “colorFill”; var colorRellenoTexto2
=”#EB0C22”; var patrones = new Image();

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 + “ “ +

tamanoFuente + “px “ + nombreFuente;


context.shadowColor = colorSombra;
context.shadowBlur = desenfoqueSombra;
context.globalAlpha = transparenciaTexto;
var posicionY = (canvas.height/2);

var medidasTexto = context.measureText(


mensajeBienvenida);
var anchoTexto = medidasTexto.width;

vartempColor;
if (tipoRelleno == “colorFill”) {
tempColor = colorRellenoTexto;
} else if (tipoRelleno == “linearGradient”) {
var gradient = context.createLinearGradient(

gradient.addColorStop(0,colorRellenoTexto);
gradient.addColorStop(.6,colorRellenoTexto2);
tempColor = gradient;

} else if (tipoRelleno == “radialGradient”) {


var gradient = context.createRadialGradient(

1);
gradient.addColorStop(0,colorRellenoTexto);
gradient.addColorStop(.6,colorRellenoTexto2);
tempColor = gradient;

} else if (tipoRelleno == “pattern”) {


var tempColor =
context.createPattern(patrones,”repeat”);

} else {
tempColor = colorRellenoTexto;
}

switch(RellenarODelinear) {

break;
case “stroke”:
context.strokeStyle = tempColor;
context.strokeText ( mensajeBienvenida,

break; case “both”:


context.strokeStyle= “#000000”;
context.strokeText ( mensajeBienvenida,
break; }
}

function cambiarCajaTexto(e) {
var target = e.target;
mensajeBienvenida = target.value;
dibujarEnCanvas();

function cambiarAlineacionVertical(e) { var


target = e.target;
alineacionVertical = target.value;
dibujarEnCanvas();

function cambiarAlineacionHorizontal(e) { var


target = e.target;
alineacionHorizontal = target.value;
dibujarEnCanvas();

function cambiarRellenarODelinear(e) { var


target = e.target;
RellenarODelinear = target.value;
dibujarEnCanvas();

function cambiarTamanoTexto(e) { var target =


e.target; tamanoFuente = target.value;
dibujarEnCanvas();

function cambiarColorRellenoTexto(e) { var


target = e.target;
colorRellenoTexto = “#” + target.value;
dibujarEnCanvas();

function cambiarFuenteUsada(e) { var target =


e.target; nombreFuente = target.value;
dibujarEnCanvas();
}

function cambiarGrosorFuente(e) { var target


= e.target; grosorFuente = target.value;
dibujarEnCanvas();

function cambiarEstiloFuente(e) { var target


= e.target; estiloFuente = target.value;
dibujarEnCanvas();

}
var target = e.target;
dibujarEnCanvas(); }

function cambiarSombreadoY(e) { var target =


e.target; sombreadoY = target.value;
dibujarEnCanvas();

function cambiarDesenfoqueSombra(e) { var


target = e.target;
desenfoqueSombra = target.value;
dibujarEnCanvas();

function cambiarColorSombra(e) { var target =


e.target; colorSombra = target.value;
dibujarEnCanvas();

}
function cambiarTransparenciaTexto(e) { var
target = e.target;
transparenciaTexto = (target.value);
dibujarEnCanvas();

function cambiarColorRellenoTexto2(e) { var


target = e.target;
colorRellenoTexto2 = “#” + target.value;
dibujarEnCanvas();

function cambiarTipoRelleno(e) { var target =


e.target; tipoRelleno = target.value;
dibujarEnCanvas();

function cambiarAnchoDelCanvas(e) { var


target = e.target; canvas.width =
target.value; dibujarEnCanvas();

function cambiarAltoDelCanvas(e) { var target


= e.target; canvas.height = target.value;
dibujarEnCanvas();

}
function cambiarTamanoCanvas(e) {

var styleWidth = document.getElementById(


“estiloDeAnchoDelCanvas”);
var styleHeight = document.getElementById(
“estiloDeAltoDelCanvas”);
var styleValue = “width:” + styleWidth.value
+ “px; height:” + styleHeight.value +”px;”;
canvas.setAttribute(“style”, styleValue );
dibujarEnCanvas();
}

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 soportarCanvas () { return


Modernizr.canvas;
}

function eventWindowLoaded() {
var patrones = new Image();
patrones.src = “texture.jpg”;
patrones.onload = cargarRecursosAplicacion;
}

function cargarRecursosAplicacion() {
aplicacionTextos();
}

El resultado se mostrará mas o menos de esta manera:


Una matriz de datos del objeto imageData contiene cuatro enteros
por pixel, cada uno de los cuales pertenece al rojo, verde, azul y la
transparencia de cada pixel, estos valores son conocidos como los
valores alpha.
Una vez hecho esto, el ancho (width) de el objeto imageData
devuelto por el método getImageData() no es necesariamente el
mismo ancho (width) que tu pasaste como argumento al método
getImageData(). Esto es porque el primero representa los
pixeles del dispositivo, mientras que el último representa los pixeles
CSS.

Pixeles de dispositivo y pixeles de CSS

Para imágenes con calidades superiores, los navegadores pueden


usar múltiples pixeles de dispositivos por cada pixel de css. Por
ejemplo tu puedes tener un canvas de unos 200 pixeles cuadrados,
para un total de 40000 pixeles de css, pero si el navegador representa
cada pixel de css con 2 pixeles de dispositivos, tu podrías tener
160000

Atento
(400 x 400) pixeles de dispositivo. Tu puedes sacar muchos pixeles de
dispositivo con las propiedades width y height de tu objeto
imagedata.

putImageData(ImageData imagedata, float x, float y, [float


dirtyX, float dirtyY, float dirtyWidth, float dirtyHeight])

Descripción Coloca los datos de la imagen dentro del canvas en las


coordenadas (x, y), donde (x, y) son expresados en pixeles de CSS.
El rectángulo sucio representa los datos de la región de la imagen que
el navegador copiará en la vista activa de el canvas. Tu especiicas ese
rectángulo en pixeles de dispositivo. Este método acepta siete
argumentos, estos son:
El argumento imageData que representa un objeto imageData.
Los argumentos x e y que representan las coordenadas de un punto
de destino en el canvas. Los pixeles desde los datos de la imagen
serán copiados al inicio del canvas en este punto. Estos argumentos no
son transformados por la matrix de transformación actual.
Los argumentos dirtyX y dirtyY que representan la ubicación
horizontal y vertical desde la esquina superior izquierda de los datos
de la imagen, expresados en pixeles de dispositivos. Sus valores por
defecto son 0.
Y por último los argumentos dirtyWidth y dirtyHeight que
representan el ancho y alto del rectángulo sucio, expresados en
pixeles de dispositivo. Sus valores por defecto son el ancho y el alto de
los datos de la imagen.

canvas
y imagedata

dirtyY dirtyX (canvas.width / imagedata.width)


x dirtyX dirtyY (canvas.he ght / imagedata.height)
dirtyW d rtyH (canvas.he ght / imagedata.height)
dirtyW (canvas.width/ magedata.width)
imagen 2.26 - esquema del método putImageData()
putImageData() no es afectado por la configuración global
Cuando tu colocas los datos de imagen dentro del canvas con el
método putImageData(), estos datos de imagen no son afectados
por la configuración global del canvas, tales como globalAlpha y
globalCompositeOperation. El navegador tampoco
considera cosas como: la composición,

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.

putImageData(): necesita tanto los pixeles de dispositivo como los


pixeles CSS

Cuando tu llamas al método putImageData() con sus siete


argumentos, tu especificas una impresión dentro del canvas (con el
segundo y tercer argumento) y un rectángulo sucio dentro de los datos
de la imagen que tu quieres copiar dentro del canvas (los últimos
cuatro argumentos).

Atento

Tu especificas la impresión del canvas en pixeles CSS, mientras que el


rectángulo sucio de los datos de la imagen lo especificas en pixeles de
dispositivo. Si tu inadvertidamente usas las mismas unidades para
ambos casos, putImageData() podría no trabajar como tu lo
esperas.

createImageData(number width, number height)


Descripción Este método puede tomar dos set de argumentos:
ImageData createImageData(width, height)
ImageData createImageData(ImageData data) Para
el primero de estos, los argumentos width y height representan el
ancho y alto en pixeles de CSS del objeto ImageData que se
quiere. Mientras que en el segundo, el argumento data es un objeto
ImageData ya existente del cual se toma el tamaño para el nuevo
objeto ImageData a ser creado.
Este método devuelve un objeto ImageData creado nuevamente
que tiene el ancho y alto especiicado o el mismo tamaño del argumento
data. Todos los pixeles dentro de este nuevo objeto ImageData
están inicializados como negro transparentes (todos los componentes
de color y el alpha están en 0). Los valores de las propiedades
width y height devueltos por el objeto ImageData no siempre
coinciden con las propiedades width y height pasados en los
argumentos.

Ejemplo de aplicación de los métodos de manipulación de imagenes

nombre del archivo - imageData.html


<!DOCTYPE html>
<html>

<head>
<title>
Aplicaci&oacute;nde los m&eacute;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’>

<input type=’button’ id=’botonResetear’


value=’Resetear’/>
</div>
<canvas id=’canvas’ width=’800’ height=’520’>

Canvas not supported


</canvas>
</div>

<script src=’imageData.js’></script> </body>


</html>

nombre del archivo


- imageData.js
var canvas =
document.getElementById(‘canvas’), context =
canvas.getContext(‘2d’), botonResetear =
document.getElementById(‘botonResetear’),

imagen = new Image(),


imageData,
copiaImageData =
context.createImageData(canvas.width,

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

for (i=0; i < 3; i++) {


copiaImageData.data[i] = imageData.data[i];
}

// inicia con los componentes alpha del


primer pixel, // copia imagedata, y hace la
copia mas transparente
for (i=3; i < imageData.data.length - 4;
i+=4) {
copiaImageData.data[i] = imageData.data[i] /
2; // Alpha: mas transparente

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;

// restaura el canvas al ultimo estado del


evento maose down
context.putImageData(imageData, 0, 0);
// coloca los datos de la imagen mas
transparentes dentro del rectangulo de la
banda

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);

function inicioBanda(x, y) { mousedown.x = x;


mousedown.y = y;

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().

getImageDataHD(number x, number y, number width,


number height)

Descripción Devuelve un objeto imageData que contiene una matriz


de datos enteros 4xwxh, donde w y h representan el ancho y alto de
la imagen en pixeles de dispositivo. Se puede extraer el ancho y el alto
con los atributos width y height del objeto imageData.
Este método devuelve los datos con la misma resolución que el canvas.
Como se habrá dado cuenta el objeto imagedata devuelto por este
método tiene tres propiedades, las cuales son:
width: el ancho de los datos de la imagen en pixeles de dispositivo.
height: el alto de los datos de la imagen, también en pixeles de
dispositivo. data: una matriz de valores que representan los pixeles
del dispositivo. Las propiedades width y height son solo de
lecturas, de tipo long y sin signo.
Una matriz de datos del objeto imageData contiene cuatro enteros
por pixel, cada uno de los cuales pertenece al rojo, verde, azul y la
transparencia de cada pixel, estos valores son conocidos como los
valores alpha.
Una vez hecho esto, el ancho (width) de el objeto imageData
devuelto por el método getImageData() no es necesariamente el
mismo ancho (width) que tu pasaste como argumento al método
getImageData(). Esto es porque el primero representa los
pixeles del dispositivo, mientras que el último representa los pixeles
CSS.
putImageDataHD(ImageData imagedata, float x, float y,
[float dirtyX, float dirtyY, float dirtyWidth, float
dirtyHeight])

Descripción Coloca los datos de la imageno el objeto Im,ageData


dentro del canvas en las coordenadas (x, y), donde (x, y), con la
misma densidad de pixeles del canvas (independientemente del valor
del atributo resolution del objeto ImageData), son expresados en
pixeles de CSS. El rectángulo sucio representa los datos de la región
de la imagen que el navegador copiará en la vista activa de el canvas.
Tu especiicas ese rectángulo en pixeles de dispositivo.
Este método acepta siete argumentos, estos son:
El argumento imageData que representa un objeto imageData.
Los argumentos x e y que representan las coordenadas de un punto
de destino en el canvas. Los pixeles desde los datos de la imagen
serán copiados al inicio del canvas en este punto. Estos argumentos no
son transformados por la matrix de transformación actual.
Los argumentos dirtyX y dirtyY que representan la ubicación
horizontal y vertical desde la esquina superior izquierda de los datos
de la imagen, expresados en pixeles de dispositivos. Sus valores por
defecto son 0. Y por último los argumentos dirtyWidth y
dirtyHeight que representan el ancho y alto del rectángulo sucio,
expresados en pixeles de dispositivo. Sus valores por defecto son el
ancho y el alto de los datos de la imagen. Los atributos globalAlpha y
globalCompositeOperation, así como los atributos de sombra, son
ignorados a los efectos de llamado de este método. Los pixeles en el
canvas son reemplazados en su mayoría sin composición, mezclas
alpha, ni sombras, etc.
Una excepción NotSupportedError es lamnzada si alguno de los
argumentos son valores ininitos.

createImageDataHD(number width, number height)

Descripción
Este método puede tomar solo un set de argumentos:

ImageData createImageDataHD(width, height) Los


argumentos width y height representan el ancho y alto en pixeles
de CSS del objeto ImageData que se quiere.
Este método devuelve un objeto ImageData creado nuevamente
cuyas dimensiones son iguales a las dimensiones pasadas con los
argumentos, multiplicado por el número de pixeles en el canvas que a
su vez corresponden a cada unidad de espacio de coordenadas.
Todos los pixeles dentro de este nuevo objeto ImageData están
inicializados como negro transparentes (todos los componentes de
color y el alpha están en 0). Los valores de las propiedades width y
height devueltos por el objeto ImageData no siempre coinciden
con las propiedades width y height pasados en los argumentos.

Ejemplo de aplicación de los métodos de manipulación de imagenes


HD

nombre del archivo - getImageDataHD.html


<!DOCTYPE HTML>
<html>

<head>
<title>Ejemplo de los m&eacute;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>

nombre del archivo - getImageDataHD.js


var image = new Image();
function init() {

image.onload = demo;
image.src = “images/slider-img2.jpg”;
}
function demo() {
var canvas =
document.getElementsByTagName(‘canvas’)[0];
var context = canvas.getContext(‘2d’);

// dibujamos la imagen dentro del canvas


context.drawImage(image, 0, 0);
// tomamos los datos de la imagen para
manipularlas var input =
context.getImageDataHD(0, 0, canvas.width,
canvas. height);
// Tomamos un lugar vacio para colocar los
datos var output =
context.createImageDataHD(canvas.width,
canvas. height);

// alias algunas variables por conveniencia


// note que estamos usando input.width e
input.height aquí // ya que podrían no ser lo
mismo que canvas.width y //canvas.height
// (en particular, puede ser diferente en las
pantallas de alta //resolución)
var w = input.width, h = input.height;
var inputData = input.data;
var outputData = output.data;

// detección de bordes
for (var y = 1; y < h-1; y += 1) {
for (var x = 1; x < w-1; x += 1) {

for (var c = 0; c < 3; c += 1) {


var i = (y*w + x)*4 + c;
outputData[i] = 127 + -inputData[i - w*4 - 4]
inputData[i - w*4] - inputData[i - w*4 + 4] +

-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);
}

El resultado se mostrará mas o menos de esta manera:


caretBlinkRate()

Descripción Devuelve un objeto imageData que contiene una matriz


de datos enteros 4xwxh, donde w y h representan el ancho y alto de
la imagen en pixeles de dispositivo. Se puede extraer el ancho y el alto
con los atributos width y height del objeto imageData.

drawSystemFocusRing(path, element)

Descripción Dibuja un anillo de enfoque del estilo apropiado a lo largo


de la ruta prevista, siguiendo las convenciones de la plataforma.
Cuando este método es invocado se deben seguir los siguientes
pasos: 1. Sí el elemento no esta enfocado o no es un descendiente del
elemento con cuyo contexto ha sido asociao, entonces aborta estos
pasos. 2. Sí el usuario ha solicitado el uso de anillos de enfoque
particulares (por ejemplo: anillos de enfoque de alto contraste), o si el
elemento tendría un anillo de selección alrededor de él, entonces
dibuje un anillo de enfoque del estilo apropiado a lo largo del path
previsto, siguiendo las convenciones de la plataforma. El anillo de
enfoque no debe estar sujeta a los efectos de sombra, transparencias
o a los operadores de globalComposite, pero debe estar sujeta a la
región clipping.
3. Opcionalmente informar al usuario de que el foco está en el lugar
determinado por la trayectoria deseada. Los user agents pueden
esperar hasta la próxima vez que el ciclo de eventos alcance su paso
“ actualizar la representacion de” informar opcionalmente al usuario.
Este método acepta dos parámetros:
path: La ruta en la cual el anillo de enfoque se va a dibujar, tambien
acepta un objeto path, la cual puede ser un path svg.
element: El elemento en la cual el anillo de enfoque se va a dibujar.
Este método devuelve un objeto de tipo DOM nodo.

Nota destacada!

Algunas plataformas solo dibujan anillos de enfoques en torno a


elementos que se han centrado en el teclado y no los que se centran
en el ratón. Otras plataformas simplemente no llaman a los anillos de
enfoque en torno a ningún elemento en absoluto a menos que las
características de accesibilidad pertinentes estén habilitadas. Es por
eso

Atento

que este método se centrará en seguir estas convenciones. Los user


agent que implementen distinciones basadas en la manera en que el
elemento estuvo enfocado están animando a clasificar un enfoque
impulsado por el método focus() basado en el tipo de evento de la
interacción del usuario desde que se dispara la llamada (si la hay).

drawCustomFocusRing(path, element)

Descripción Dibuja un anillo de enfoque a lo largo de la ruta prevista y


conjunto de resultados false.
Cuando este método es invocado se deben seguir los siguientes
pasos: 1. Sí el elemento no esta enfocado o no es un descendiente del
elemento con cuyo contexto ha sido asociao, entonces devuelve false y
aborta estos pasos. 2. Deja que el resultado sea true.
3. Sí el usuario ha solicitado el uso de anillos de enfoque particulares
(por ejemplo: anillos de enfoque de alto contraste), entonces dibuje un
anillo de enfoque del estilo apropiado a lo largo del trazo previsto y del
conjunto de resultados en false.
El anillo de enfoque no debe estar sujeta a los efectos de sombra,
transparencias o a los operadores de globalComposite, pero
debe estar sujeta a la región clipping.
4. Opcionalmente informar al usuario de que el foco está en el lugar
determinado por la trayectoria deseada. Los user agents pueden
esperar hasta la próxima vez que el ciclo de eventos alcance su paso
“ actualizar la representacion de” informar opcionalmente al usuario.
5. devuelve resultados.

Este método acepta dos parámetros:


path: El trazado en la cual el anillo de enfoque se va a dibujar, tambien
acepta un objeto path, la cual puede ser un path svg.
element: El elemento en la cual el anillo de enfoque se va a dibujar.
Este método devuelve un objeto de tipo DOM nodo.

La región Clipping

Combinar los métodos save() y restore() con la región clip de


canvas, nos permite limitar el área para dibujar de un path y sus
subpaths. Podemos hacer esto la primera vez usando el método
rect() para dibujar un rectángulo o un área en la que nos gustaría
dibujar, sin que podamos correr el riesgo de invadir la parte exterior de
esta región, es decir, limitando esta área a mi dibujo en particular,
entonces llamamos al método clip(). Esto deine el área que
coniguramos con el método rect() como la región clip. Ahora, lo que
pasará en el contexto actual, será que únicamente se mostrará lo que
dibujemos en la porción que hemos deinido anteriormente, es decir, se
mostrará solo lo que este dentro de la región clipping. Piensa en esto
como una mascara que tu puedes usar en tus operaciones de dibujo.
Por defecto la región clipping es del mismo tamaño que el elemento
canvas. A continuación vamos a ver un ejemplo que nos ayudará a
comprender de forma visual, lo que podemos hacer con un área
clipada.

Ejemplo de aplicación de la región clipping:

nombre del archivo - regionClipping.html <!DOCTYPE html>


<html>

<head> <title>Ejemplo de sintaxis


canvas</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=’regionClipping.js’></script>
</div>
</body>
</html>

nombre del archivo - regionClipping.js


var canvas =
document.getElementById(‘canvas’), context =
canvas.getContext(‘2d’);

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 drawScreen() {
context.save();
context.beginPath();

//establecemos una region clipping en 50×50


comenzando 200,100 context.rect(200, 100, 70,
70);
context.clip();

//creamos un circulo negro


context.beginPath();
context.strokeStyle = “black”;
context.lineWidth = 5;
context.arc(300, 200, 100, (Math.PI/180)*0,
(Math.PI/180)*360,

false);

//circulo full
context.stroke(); context.closePath();
context.restore();

//reclipamos el canvas completamente


context.beginPath();
context.rect(0, 0, 600, 400); context.clip();

//creamos una linea naranja


context.beginPath();
context.strokeStyle = “orange”;
context.lineWidth = 5;
context.arc(300, 200, 150, (Math.PI/180)*0,
(Math.PI/180)*360,
Descripción Se reiere al contexto del elemento canvas. El uso mas
común del atributo canvas es para acceder a los atributos ancho y alto
del elemento canvas: context. canvas.width y
context.canvas.height respectivamente.

fillStyle

Descripción
Tipo
Valor por defecto

Especiica un color, gradiente o un patrón que subsecuentemente usa


el contexto para rellenar formas. Los valores válidos son: una cadena
que contiene un color CSS, un canvasGradient object y un
canvasPattern Object. cualquiera
#000000

font

Descripción
Tipo
Valor por defecto

Especiica la fuente que el contexto usa cuando llamas a los métodos


illText() o strokeText(). Debe ser una fuente válida CSS conigurada.
String
10px sans-serif.

Atento
Configurar las propiedades de la fuente.

Se puede configurar cualquier fuente del texto que se dibuja con la


propiedad font del contexto, la cual consiste en una cadena con los
componentes de una fuente css3 válida. En la tabla 2 se listan los
componentes de estas fuentes en el orden que deben usarse para
esta propiedad. Una vez configurada la cadena de esta propiedad,
esta es impresa haciendo uso del método

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.

Tabla 2 - componentes y valores válidos de las fuentes


Ejemplo de aplicación de la propiedad font y sus valores:

nombre del archivo - font.html


<!DOCTYPE html>
<html>

<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>

<body> <div id=”contenedor”>


<canvas id=’canvas’ width=’500’ height=’700’>

Tu navegador no soporta canvas de HTML5


</canvas>
</div>

<script src=’javascript/font.js’></script>
</body>
</html>

nombre del archivo


- font.js
var canvas =
document.getElementById(‘canvas’), context =
canvas.getContext(‘2d’),

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();

while(i > DISTANCIA_Y*4) {


context.beginPath();
context.moveTo(0, i);
context.lineTo(context.canvas.width, i);
context.stroke();
i -= DISTANCIA_Y;

}
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;
});

El resultado se mostrará mas o menos de esta manera:


Atento
Composición del Canvas

Se refiere a como finalmente podemos controlar los efectos de


transparencia y las capas de los objetos cuando dibujamos en el
canvas. Para operaciones de composición del canvas podemos
disponer de dos funciones: globalAlpha y globalCompositeOperation.

globalAlpha

Descripción
Tipo
Valor por defecto

Especiica la coniguración global del alpha, la cual debe ser un número


entre 0 (transparente) y 1.0 (totalmente opaco). El buscador multiplica
el valor de cada pixel dibujado por la propiedad globalAlpha,
incluyendo cuando dibujas imágenes.
numbers.
1.0

Atento

Nota destacada!

Ademas de los colores especificados como semi transparentes con el


componente alpha de los sistemas de colores rgba() o hsla(), también
es posible usar la propiedad globalAlpha, la cual es aplicado por
el navegador a toda forma o imagen que hayas dibujado. El valor para
esta propiedad debe estar entre 0.0 lo cual significa que es totalmente
transparente y 1.0 que es totalmente opaco.

globalComposite-Operation

Descripción Determina como el navegador dispone un objeto sobre


otro. Tipo String.
Valor por defecto
source-over

Por defecto cuando dibujas un objeto (source) encima de otro


(destination) en un canvas, el navegador simplemente dibuja el
recurso (source) sobre el destino (destination). Esta manera de
componer los objetos que dibujas, no debe causarte ninguna
sorpresa, ya que después de todo, eso es exactamente lo que pasa si
tu dibujas en un papel, una cosa sobre otra.
Ahora bien, si tu coniguras la propiedad
globalCompositeOperation del contexto de canvas, tu
puedes cambiar el valor por defecto de una composición por
cualquiera de los valores listados en la siguiente tabla. Estos valores
son conocidos como operadores porter-duf, los cuales fueron descritos
por homas Porter y Tom Duf para Lucas Films ltd., en un articulo
publicado en la revista computer graphics de julio de 1984. Puedes
leer este artículo en: http://keithp.com/~keithp/porterduf/ p253-
porter.pdf.
En la tabla 3 se muestran todos los valores válidos para la propiedad
globalCompositeOperation, también muestra como el objeto
recurso (source), mostrado como un circulo, se compone sobre un
objeto destino (destination), mostrado como un cuadrado. El valor por
defecto, source-over, esta resaltado en la tabla.

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

destination-out Renderiza A en la propiedad top de B donde A no es


transparente

Renderiza B en la propiedad top de A pero solo donde B no es


transparente
Renderiza solo B y solo donde A no es transparente
Renderiza solo B y solo donde A es transparente

destination-over Renderiza B en la propiedad top de A donde A no


es transparente

lighter Renderiza la suma de A y B


copy Se olvida de B y renderiza solo A

xor Renderiza A donde B es transparente. Renderiza transparente


donde ni A ni B son transparentes

Tabla 3 - valores para la propiedad globalComposite-Operation


Ejemplo de aplicación de algunas de las propiedades de composición
del canvas:

nombre del archivo - composicionCanvas.html


<!DOCTYPE html>
<html>

<head> <title>Ejemplo de composici&oacute;n


canvas</title> <style>
#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=’composicionCanvas.js’></script>
</div>
</body>
</html>

nombre del archivo - composicionCanvas.js


var canvas =
document.getElementById(‘canvas’); var
context = canvas.getContext(‘2d’);

dibujarCuadricula(context, ‘lightgrey’, 10,


10);
//creamos un area para mostrar el ejemplo
//dibujamos un cuadro rojo
context.globalCompositeOperation = “source-
over”; //draw a red square next to the other
one
context.globalCompositeOperation =
“destination-out”; context.globalAlpha = .5;
context.globalCompositeOperation = “source-
atop”;

//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

Cuando dibujas en el canvas tu puedes controlar que la terminación


de la linea sea igual a una de las mostradas en la imagen anterior -
conocidas como lineCap . Los finales de linea son controladas por la
propiedad lineCap del contexto de canvas.
En el contexto canvas la terminación de la linea por defecto es butt,
la cual da salida a la linea como si fuera cortada de tajo en ese mismo
punto. Pero round y square agregan un remate al final de la linea;
round agrega un semicirculo al final de la linea con un diametro igual
a la mitad del ancho de la linea; square agrega un rectangulo al final
de la linea equivalente a la mitad del ancho de la linea.

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

miter round bevel

imagen 2.32 - valores de la propiedad lineJoin para los encuentros


de lineas
lineJoin

lineas cuando estas coincidan, esto es conocido como unión de lineas


(line join en en inglés). Las uniones de lineas son controladas por la
propiedad lineJoin del contexto.
Un valor bevel para la propiedad lineJoin es el resultado de
formar un triangulo en la esquina opues

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

Ancho linea larg


Ancho linea largo m i te r
imagen 2.33 - formato del miter para los encuentros de lineas
miterLimit
Tu puedes controlar el largo del valor miter de la propiedad lineJoin,
cuando este es usado en tus dibujos, puedes especificar un limite a
este valor con la propiedad miterLimit, la cual es el largo del miter
dividido entre la mitad Atento del ancho de la linea.

Al dibujar o trabajar con formas, textos o imágenes dentro del elemento


canvas, quizás tu desees también añadirle una sombra a tus diseños,
el contexto de canvas posee cuatro atributos ideales para tal in.

shadowBlur

Descripción
Tipo
Valor por defecto

Determina como el navegador extiende la sombra exterior; el número


más alto es el mas extendido fuera de la sombra. El valor shadowBlur
no es un valor pixel pero si un valor usado en una ecuación de
desenfoque gaussiano. Debe ser un valor no negativo.

Numbers
0.0

shadowColor

Descripción
Tipo
Valor por defecto

Especiica el color a usar por el navegador al dibujar la sombra. El valor


para esta propiedad es a menudo especiicada como parcialmente
transparente para que se vea el fondo a través de ella. Debe ser un
color CSS válido. String
transparent black

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)

RRGGBB :Green (verde)


rgb expresados como enteros
rgb expresados como porcentaje: purple (púrpura) Negro
transparente
Expresa un valor amarillo saturado
Expresa un valor amarillo al 50% de opacidad Como un string con
un color css válido

Tabla 4 - ejemplos de cadenas para expresar colores con la


propiedad shadowColor.

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

De acuerdo con las especificaciones del canvas, los navegadores


deben dibujar las sombras solo si: a. has especificado un color con la
propiedad shadowColor, lo cual indicará que no es completamente
transparente.
b. Has indicado un valor diferente de 0 en alguna de las tres
propiedades del contexto de canvas para las sombras:
shadowBlur, o . Otra manera sencilla para no mostrar las sombras
es establecer shadowColor como indefinido. Sin embargo, hay que
tomar en cuenta que al configurar esta propiedad como indefinida, no
trabajará en todos los navegadores, al momento de escribir esta guía
de referencia solo trabajaba en los navegadores basados en webKit,
en los navegadores tales como Firefox u Opera no funciona. Para estar
seguro de que tus sombras se dibujarán o no en todos los
navegadores, deberas darle algún valor a las propiedades de
sombras, esto lo puedes hacer de forma manual o usando los métodos
del contexto save() o restore().
strokeStyle

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

Posicionar horizontal y verticalmente los textos

Cuando dibujas textos en un canvas con los métodos strokeText() y


fillText(), estas especificando las coordenadas x e y donde deben
dibujarse los textos dentro del canvas; sin embargo, exactamente
donde el navegador dibuja los textos depende de dos propiedades
del contexto: textAlign y textBaseline.

textAlign

Descripción Determina la colocación horizontal del texto dibujado con


los métodos - Text() o strokeText().Valores válidos son:
start, end, left y right.

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

start y right es lo mismo que end. De igual manera, cuando el


navegador muestra los textos de derecha a izquierda, significa que el
valor para el atributo dir esrtl, right es lo mismo que start y
left es lo mismo que end.

textBaseLine

Descripción Determina la colocación vertical del texto dibujado con los


métodos - Text() o strokeText(). Los valores válidos para
esta propiedad son: top, hanging, middle, alphabetic,
ideographic y bottom.

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

usado para muchos de los lenguajes de India. Los valores top,


}
#canvas {

border:2px solid #666; margin-top:5px;


}
p{
display:inline; margin-left:60px; margin-
right:130px; font-size:16px;
color:red;
}
p#last{
margin-right:0px; color:red;
}
</style>
</head>

<body> <div id=”contenedor”>


<p>izquierda a derecha</p>
<p>centrado</p>
<p id=”last”>derecha a izquierda</p>

<canvas id=’canvas’ width=’800’ height=’500’>

Tu navegador no soporta canvas de HTML5


</canvas>
</div>

<script src=’javascript/posicionTextos.js’>
</script> </body> </html>

nombre del archivo - composicionCanvas.js


var canvas =
document.getElementById(‘canvas’),
context = canvas.getContext(‘2d’),
tamFuente = 24,
valoresTextAlign = [‘start’, ‘center’,
‘end’],
valoresTextBaseline = [‘top’, ‘middle’,
‘bottom’, ‘alphabetic’, ‘ideographic’,
‘hanging’],
x, y;

//
Funciones....................................
function dibujarCuadricula(context, color,
stepx, stepy) { context.save();
context.strokeStyle = color;
context.lineWidth = 0.5;
height);

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();
}
context.restore(); }
function dibujarIndicadorPosicion() {
context.strokeRect(x, y, 7, 7); }
function dibujarTextos(texto, textAlign,
textBaseline) { if(textAlign)
context.textAlign = textAlign;
if(textBaseline)
context.textBaseline = textBaseline;
}
function dibujarLineaTexto() {
context.strokeStyle = ‘red’;

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);

for (var align=0; align <


valoresTextAlign.length; ++align) { for (var
baseline=0; baseline <
valoresTextBaseline.length;
++baseline) {
x = 40 + align*tamFuente*15;
y = 40 + baseline*tamFuente*3.3;

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

desde la izquierda hacia la derecha, mientras que rtl es usado para


indicarle al canvas que los textos se deben dibujar desde la derecha
hacia la izquierda. Este método devuelve el valor actual, que si no se
ha indicado ningún otro, se establece inicialmente en inherit,
pero al cambiarsele el valor, este toma inmediatamente el nuevo valor
asignado.

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

Ejemplo de aplicación de la propiedade lineDashOffset:

nombre del archivo - dash.html


<!DOCTYPE html>
<html>

<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’>

Tu navegador no soporta canvas de HTML5


</canvas>
<button onclick=”incrementarDash()”>A
marchar!</button>

</div>
<script src=’javascript/dash.js’></script>
</body>
</html>

nombre del archivo


- dash.js
window.onload = function () {
contexto =
document.getElementById(“canvas”).getContext(
contexto.translate(0.5,0.5);

//Agregar una función de marcador de posición


para //los navegadores que no tienen
setLineDash() if (!contexto.setLineDash) {

contexto.setLineDash = function () {}; }

// La primera linea dashed


contexto.setLineDash([1,2]); contexto.mozDash
= [5]; contexto.beginPath();
contexto.moveTo(15,45);
contexto.lineTo(485,45); contexto.stroke();

// La segunda linea dashed


contexto.setLineDash([2,4]); contexto.mozDash
= [5]; contexto.beginPath();
contexto.moveTo(15,70);
contexto.lineTo(485,70); contexto.stroke();
// La tercera linea dashed
contexto.setLineDash([5]); contexto.mozDash =
[5]; contexto.beginPath();
contexto.moveTo(15,95);
contexto.lineTo(485,95); contexto.stroke();

// la cuarta linea dashed


contexto.setLineDash([10]); contexto.mozDash
= [5]; contexto.beginPath();
contexto.moveTo(15,120);
contexto.lineTo(485,120); contexto.stroke();

// 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

Este es un objeto devuelto por el método measureText() del


canvasRenderingContext2d. Las medidas devueltas en este
objeto viene dada en pixeles CSS

width, actualBoundingBoxLeft,
actualBoundingBoxRight,
fontBoundingBoxAscent,
fontBoundingBoxDescent,
actualBoundingBoxAscent,
actualBoundingBoxDescent, emHeightAscent,
emHeightDescent, hangingBaseline,
alphabeticBaseline, ideographicBaseline

Atributos del objeto textMetrics

Direccion horizontal X

width

Descripción
Representa el ancho del texto dado. viene expresado en pixeles CSS

actualBoundingBoxLeft
Descripción

Representa la distancia paralela a la linea base desde el punto de


alineación

dado por el atributo textAlign al lado izquierdo del rectángulo


delimitador en el texto dado, en pixeles CSS; valores positivos indican
una distancia que va desde la izquierda al punto de alineación dado.

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

Descripción Representa la distancia paralela a la linea base desde el


punto de alineación dado por el atributo textAlign al lado
derecho del rectángulo delimitador en el texto dado, en pixeles CSS;
valores positivos indican una distancia que va desde la derecha al
punto de alineación dado.

Direccion vertical Y

fontBoundingBoxAscent

Descripción Representa la distancia desde la linea horizontal indicada


por el atributo textBaseline a la parte superior del rectángulo
delimitador más alto de todas las fuentes usadas para renderizar el
texto, en pixeles CSS; valores positivos indican una distancia que va
desde la parte superior a la linea base dada.

fontBoundingBoxAscent y fontBoundingBox Descent


Estos valores son útiles cuando se representa un fondo(background)
que debe tener una altura constante, incluso si el texto exacto
renderiza cambios El atributo actualBoundingBoxAscent (y su
corresponAtento diente atributo para el descenso) son útiles cuando
dibujas una caja limitadora de un texto especifico.

fontBoundingBoxDescent

Descripción Representa la distancia desde la linea horizontal indicada


por el atributo textBaseline a la parte inferior del rectángulo
delimitador más alto de todas las fuentes usadas para renderizar el
texto, en pixeles CSS; valores positivos indican una distancia que va
desde la parte inferior a la linea base dada.

actualBoundingBoxAscent

Descripción

Representa la distancia desde la linea horizontal indicada por el


atributo

textBaseline a la parte superior del rectángulo delimitador del


texto dado, en pixeles CSS; valores positivos indican una distancia que
va desde la parte superior a la linea base dada.
Nota destacada!
Este número puede variar en gran medida basado en el texto de
entrada, incluso si la primera fuente especificada cubre todos los
caracteres de la entrada. Por ejemplo, el actualBoundingBoxAscent de
una letra minuscula “ o”
Atento

desde una propiedad alphabetic del atributo textBaseline sería


menor que la de una “ F” mayuscula. El valor fácilmente debe ser
negativo; por ejemplo, la distancia desde la parte superior de la caja
em(textBaseline valor ‘top’) a la parte superior del rectángulo
delimitador cuando el texto dado es solo una coma ” ,” probablemente
(a menos que la fuente sea bastante inusual) será negativo.

actualBoundingBoxDescent

Descripción Representa la distancia desde la linea horizontal indicada


por el atributo textBaseline a la parte inferior del rectángulo
delimitador del texto dado, en pixeles CSS; valores positivos indican
una distancia que va desde la parte baja a la linea base dada.

emHeightAscent

Descripción Representa la distancia desde la linea horizontal indicada


por el atributo textBaseline a la parte superior del cuadro em
(em square) en la linea de caja, en pixeles CSS, valores positivos
indican que la linea base dada esta por debajo de la parte superior
del cuadro em (por lo que este valor, usualmente será positivo). (Cero
si la linea base dada esta en la parte superior de el cuadro em; la mitad
del tamaño de la fuente si la linea base dada esta en el medio del
cuadro em).

emHeightDescent

Descripción Representa la distancia desde la linea horizontal indicada


por el atributo textBaseline a la parte inferior del cuadro em (em
square) en la linea de caja, en pixeles CSS, valores positivos indican
que la linea base dada esta detrás de la parte inferior del cuadro em
(por lo que este valor, usualmente será negativo). (Cero si la linea base
dada esta en la parte superior de el cuadro em).

hangingBaseline

Descripción Representa la distancia desde la linea horizontal indicada


por el atributo textBaseline al punto hanging a la linea base
de la linea de caja, en pixeles CSS; valores positivos indican que la
linea base dada está detrás del valor hanging de la linea base.
(Cero si la linea base dada esta en el punto hanging de la linea
base).

alphabeticBaseline

Descripción Representa la distancia desde la linea horizontal indicada


por el atributo textBaseline al valor alphabetic a la linea
base de la linea de caja, en pixeles CSS; valores positivos indican que
la linea base dada está detrás del valor alphabetic de la linea
base. (Cero si la linea base dada esta en el punto alphabetic de
la linea base).

ideographicBaseline

Descripción Representa la distancia desde la linea horizontal indicada


por el atributo textBaseline al valor ideographic a la linea
base de la linea de caja, en pixeles CSS; valores positivos indican que
la linea base dada está detrás del valorideographic de la linea
base. (Cero si la linea base dada esta en el punto ideographic de
la linea base).

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 Un objeto canvasGradient representa a un color gradiente


que puede ser asignado por las propiedades strokeStyle y illStyle de
un objeto canvasRenderingContext2d. Los métodos
createLinearGradient() y createRadialGradient() del
canvasRenderingContext2d devuelven objetos canvasGradient. Una
vez que se haya creado un objeto canvasGradient, use el método
addColorStop() para especiicar que color debe aparecer en que
posiciones dentro del gradiente. Entre las posiciones que usted
especiique, los colores se interpolan para crear un degradado suave o
desvanecimiento. Si se especiica sin escalas de color, el gradiente será
negro transparente uniforme.

Métodos del objeto canvasGradient addColorStop(number


offset, string color)

Descripción
Especiica colores ijos dentro de un gradiente. Si tu especíicas 2 o mas
para

das de color, el gradiente interpolará suavemente los colores entre las


paradas. Antes de la primera parada, el gradiente mostrará el color de
la primera parada. Después de la última parada, el gradiente mostrara
el color de la última parada. Si tu especiicas solo un color, el gradiente
será un color sólido. Si tu no especíicas paradas de colores, el
gradiente será un negro transparente uniforme. Este método acepta
dos argumentos, el primero ofset representa la posicion del color
especiicado por el segundo argumento, este es un valor de coma
lotante dentro de un rango de 0.0 a 1.0 que representa una fracción
entre el comienzo y el punto inal del gradiente.Un ofset de 0
representa el punto de inicio y 1 representa el punto inal. El segundo
argumento representa el color a ser mostrado en la posición del primer
argumento, este debe ser una cadena válida para un color CSS.
Colores en otros puntos a lo largo del gradiente son interpolados
basados en esta y otras paradas de color

El Objeto ImageData

Descripción Un objeto ImageData contiene los componentes rojo,


verde, azul y alpha de una región rectangular de pixeles. Obtienes un
objeto ImageData con los métodos createImageData() o
getImageData(), ambos objetos
CanvasRenderingContext2D.
Los nuevos objetos ImageData deben inicializarse de manera que
su atributo de anchura se establece en el número de pixeles por ila en
los datos de la imagen, su atributo de altura se establece en el número
de ilas de los datos de la imagen, su resolución se ajusta a la densidad
de pixeles del objeto y su atributo data se inicializa con un objeto
Uint8ClampedArray. El objeto Uint8ClampedArray de be
utilizar un pixel del canvas para su almacenamiento y debe tener un
comienzo en cero y una longitud igual a la longitud de su
almacenamiento, en bytes. El pixel del Canvas debe contener los datos
de la imagen. Por lo menos el valor de un pixel debe ser devuelto con
los datos de la imagen.
Un pixel del Canvas es un cuyos datos se representan ordenados de
izquierda-a-derecha, ila por ila de arriba-a-abajo, comenzando por la
parte superior izquierda, con cada pixel son dado los componentes
rojo (R), verde(G), azul (B) y alpha (A), cada uno de ellos en este mismo
orden. Cada componente de cada pixel representado en esta matriz
debe estar dentro de un rango de 0 y 255, que representa el valor de
8 bits para ese componente. A los componentes le son asignados los
indices consecutivos comenzando con 0 para el componente rojo del
pixel superior izquierdo. Así el componente color de un pixel en (x,
y) dentro de un objeto ImageData imagen puede ser accesado
como sigue:

Los elementos son de lectura/escritura, pero la longitud de la matriz es


ija. Para cualquier objeto ImageData i,i.data.lenght siempre
será igual a i.width*i.height*4.

Propiedades del objeto ImageData width

Descripción Devuelve el componente de anchura real de los datos en


el objeto ImageData, en pixeles. Para los objetos devueltos por las
variantes no HD de los métodos en esta API, esto corresponderá a la
anchura dada por los métodos. Para las variantes de alta deinición
(HD), el número de pixeles puede ser diferente que el número de
unidades correspondiente de espacio de coordenadas.

height

Descripción
Devuelve el componente de altura real de los datos en el objeto
ImageData,

en pixeles. Para los objetos devueltos por las variantes no HD de los


métodos en esta API, esto corresponderá a la altura dada por los
métodos. Para las variantes de alta deinición (HD), el número de
pixeles puede ser diferente que el número de unidades
correspondiente de espacio de coordenadas.
resolution

Descripción
Devuelve el número teórico de pixeles en los datos del objeto
ImageData

por unidad de espacio coordenadas correspondiente. Este valor se


determina de forma automática a partir de la imagen de la fuente
cuando se crea el objetoImageData, solo se utiliza para asegurar
que los objetos ImageBitmap tienen la densidad de pixeles
correcta a partir de objetos ImageData.

data

Descripción Devuelve una matriz unidireccional que contiene los datos


rgba, como enteros a partir de 0 a 255.

El Objeto CanvasPatternCanvasPattern es devuelto por

el método createPattern()Descripción Un objeto

del objeto CanvasRenderingContext2D. Un objeto


CanvasPattern puede ser usado como el valor de las
propiedades strokeStyle y fillStyle del objeto
CanvasRenderingContext2D.
Un objeto CanvasPattern no tiene propiedades o métodos
propios.

El Objeto CanvasProxy CanvasProxy puede ser


neutralizado (como cualquier objeto Descripción Un
objeto

transferible), lo que signiica que ya no puede ser transferida, y puede


ser desactivada, lo que signiica que ya no puede ser obligado a
contextos renderizados. Cuando es creado por primera vez, un objeto
CanvasProxy debe ser nulo. Un objeto CanvasProxy se crea
con un enlace a un elemento canvas. Un objeto CanvasProxy que
no ha sido deshabilitado debe tener una fuerte referencia a su
elemento canvas.
Para transferir un objeto CanvasProxy antiguo a uno nuevo, un
user agent debe crear un nuevo objeto CanvasProxy
referenciando al mismo elemento canvas, de forma que, obtendra uno
nuevo, que debe neutralizar y desactivar al anterior objeto para
inalmente devolver uno completamente nuevo.

Métodos del objeto CanvasProxy setContext(context)

Descripción Establece un contexto de renderizado del elemento


canvas del objeto CanvasProxy al objeto dado.
Lanza una excepción InvalidStateError si el objeto CanvasProxy se
ha transferido.

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.

Crear un objeto Path

Para crear un nuevo objeto path existen tres variantes:


path = new Path() Cuando no son pasados argumentos al método este
crea un nuevo objeto path path = new Path(path)
Pasando un path existente, el método creará una copia del path
pasado en los argumentos
path = new Path(SVGpath) Pasando una cadena que representa un
objeto Path SVG, este creará un objeto Path que consiste en el path
SVG pasado en los argum,entos. Ejemplo de creación de un objeto
path

nombre del archivo - path.js


<script>
// Un cuadrado
path1 = new Path();
path1.rect(5,5,90,90);
// Un rectangulo con esquinas redondeadas
path2 = new Path();
path2.arc(50,50,45,Math.PI / 2,Math.PI *
1.5,false); path2.lineTo(200,5);
path2.arc(200,50,45,Math.PI * 1.5,Math.PI /
2,false); path2.closePath();
context.stroke(path1);
context.stroke(path2);
canvas.onclick = function (e)
{
var context = e.target.getContext(‘2d’);

var coordY = coords[1];


// Test de clicks para el cuadrado
// ... }
// ... }
}
<script>

Métodos del objeto Path addPath(path, transform)

Descripción Agrega al path el path dado por el primer argumento


pasado al método. El segundo argumento es representado por alguno
de los métodos de transformación.
Ejemplo de aplicación del método addPath()

nombre del archivo - addPath.js


var canvas =
document.getElementById(‘canvas’);
var context = canvas.getContext(‘2d’);
// transformar un path cuando se agrega a
otra path
var duck = new Path(‘M 0 0 c 40 48 120 -32
160 -6 c 0 0 5 4 10 ‘+ ‘-3 c 10 -103 50 -83
90 -42 c 0 0 20 12 30 7 c ‘+
‘-2 12 -18 17 -40 17 c -55 -2 -40 25 -20 35 c
‘+
‘30 20 35 65 -30 71 c -50 4 -170 4 -200
-79z’);
var identity = new SVGMatrix();

var threeDucks = new Path();


threeDucks.addPath(duck,
identity.translate(0,0));
threeDucks.addPath(duck,
identity.translate(100,0));
threeDucks.addPath(duck,
identity.translate(200,0));

addPathByStrokingPath(path, styles, transform)

Descripción Agrega al path elpath dado por el primer argumento


pasado al método, en el caso del estilo de lineas, estas son tomadas
del segundo argumento styles, la cuál puede ser un objeto
DrawingStyle o un objeto CanvasRenderingContext2d.
El tercer y último argumento es representado por alguno de los
métodos de transformación.

addText(text, styles, transform, x, y [, maxWidth])

Descripción Agrega el texto pasado como primer argumento al path


o al canvas. Este método posee dos variantes, las cuales son:
addText(text, styles, transform, x, y [,
maxWidth]) y addText(text, styles, transform,
path [, maxWidth]). La diferencia entre estas dos variantes,
es que en lugar de unas coordenadas, se pasa otro objeto Path como
argumento.
La primera variante de este método acepta 6 argumentos, de los cuales
el último es opcional. El primer argumento representa el texto a ser
dibujado, el segundo representa el estilo del delineado que puede ser
un objeto DrawingStyle o un objeto CanvasRenderingContext2d; el
tercer argumento representa una transformación representada por
cualesquiera de los métodos de transformación del objeto
CanvasRenderingContext2d, el cuarto y quinto argumentos
representan las coordenadas de donde se dibujará el texto, si fuera
esta la variante usada, el texto se dibujara horizontalmente en las
coordenadas dadas, y por último de manera opcional un argumento
que representa el ancho máximo a ocupar por el texto que se dibujara,
si es pasado este argumento, el texto se escalará, si fuera necesario,
hasta ocupar el ancho suministrado por este argumento. Para la
segunda variante los argumentos son solo cinco en lugar de seis
pasados a la primera variante, estos son iguales con la diferencia de
que el cuarto y quinto argumentos que representan las coordenadas,
son sustituidos por un objeto path en donde se dibujará los textos
pasados por el primer argumento. Si fuera este el caso usado,
entonces el texto se dibujara a lo largo del path dado.

Ejemplo de aplicación del método addText()

nombre del archivo - addText.js


var canvas =
document.getElementById(‘canvas’); var
context = canvas.getContext(‘2d’);
var p1 = new Path(‘M 100 350 q 150 -300 300
0’); var p2 = new Path();
var styles = new DrawingStyle();
styles.font = ‘20px sans-serif’;
p2.addText(‘Hello World’, styles, null, p1);

addPathByStrokingText(text, styles, transform, x, y [,


maxWidth])

Descripción Agrega el texto pasado como primer argumento al path


o al canvas. Este método posee dos variantes, las cuales son:
addPathByStrokingText(text, styles,
transform, x, y [, maxWidth]) y
addPathByStrokingText(text, styles,
transform, path [, maxWidth]).
La diferencia entre estas dos variantes, es que en lugar de unas
coordenadas, se pasa otro objeto Path como argumento.
La primera variante de este método acepta 6 argumentos, de los cuales
el último es opcional. El primer argumento representa el texto a ser
dibujado, el segundo representa el estilo del delineado que puede ser
un objeto DrawingStyle o un objeto CanvasRenderingContext2d; el
tercer argumento representa una transformación representada por
cualesquiera de los métodos de transformación del objeto
CanvasRenderingContext2d, el cuarto y quinto argumentos
representan las coordenadas de donde se dibujará el texto, si fuera
esta la variante usada, el texto se dibujara horizontalmente en las
coordenadas dadas, y por último de manera opcional un argumento
que representa el ancho máximo a ocupar por el texto que se dibujara,
si es pasado este argumento, el texto se escalará, si fuera necesario,
hasta ocupar el ancho suministrado por este argumento. Para la
segunda variante los argumentos son solo cinco en lugar de seis
pasados a la primera variante, estos son iguales con la diferencia de
que el cuarto y quinto argumentos que representan las coordenadas,
son sustituidos por un objeto path en donde se dibujará los textos
pasados por el primer argumento. Si fuera este el caso usado,
entonces el texto se dibujara a lo largo del path dado.

moveTo(float x, float y)

Descripción Agrega un nuevo subpath al actual path con el punto que


has especiicado como el único punto de ese subpath. Este método no
limpia ninguno de los subpaths que estén en el path actual.

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 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.

quadraticCurveTo(float cpx, float cpy, float x, float y)

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.

bezierCurveTo(float cpX1, float cpY1, float cpX2, float


cpY2, float x, float y)

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.

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.

arc(float x, float y, radius, starAngle, endAngle,boolean


anticlockwise)

Descripción Agrega los subpaths que vienen a representar un arco o


un círculo al actual path. Tú puedes controlar la dirección en que se
dibujarán los subpaths (a diferencia del método rect()), con una
variable booleana. Sí existe un subpath cuando el método es llamado,
este dibujará una línea desde el último punto en el subpath existente
hasta el primer punto delante del arco del path. Este método toma 6
parámetros, los primeros dos parámetros representan un punto en el
centro de un círculo; el tercer parámetro representa el radio del círculo,
el cuarto y quinto parámetros representan el angulo inicial y el angulo
inal, respectivamente del círculo que el navegador dibujará. El último
argumento es opcional y representa la dirección en la cual el
navegador dibujara el arco. Si el valor se establece en false, como
viene por defecto, el navegador dibujará el arco en dirección al
sentido de las agujas del reloj, si por el contrario se establece en true,
el navegador dibujará el arco en la dirección contraria al sentido de
las agujas del reloj.

rect(float x, float y, float width, float height)

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();

ellipse(float x, float y, float radiusX, float radiusY, float


rotation, float startAngle, float endAngle,boolean
anticlockwise)

Descripción Dibuja una elipse según los argumentos especíicados. Si


la trayectoria del objeto no posee un subpath, este método añade una
línea recta desde el último punto del subpath al punto de inicio del
arco. Luego añadirá los puntos de inicio y inal de la circunferencia y
las conecta con un arco.
Este método posee 8 argumentos:
x e y representan las coordenadas, en pixeles, del centro de el arco
en relación con la esquina superior izquierda del canvas.
radiusX y radiusY representan las coordenadas, en pixeles, de
la ruta a seguir por el arco.

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.

Crear un objeto DrawingStyle

Para crear un nuevo objeto DrawingStyle hay que invocar al


constructor
style = new DrawingStyle([element])

Descripción Este constructor crea un nuevo objeto DrawingStyle,


opcionalmente usa un elemento como argumento especiico para la
resolución de palabras claves y los tamaños relativos de las
especiicaciones de las fuentes

Propiedades del objeto DrawingStyle lineWidth

Descripción 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.
lineCap

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.

miter round bevel

imagen 2.32 - valores de la propiedad lineJoin para los encuentros


de lineas

miterLimit

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.
Ancho linealarg Ancho linea largo m i te r imagen 2.33 - formato
del miter para los encuentros de lineas

lineDashOffset

Descripción Devuelve la coniguración de la actual linea dash, con las


mismas unidades que el patrón linea dash. Puede ser conigurado para
cambiar los valores del patrón linea dash. El valor debe ser un double
no ininito.

font

Descripción
Especiica la fuente usada por el objeto DrawingStyle. La sintaxis es la
misma

que para la propiedad ‘font’ de CSS, se ignoran los valores que no se


pueden analizarse como valores de fuente CSS válidas. Palabras clave
y las longitudes relativas se calculan en relación con el tipo de letra del
elemento canvas.

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.

Tabla 2 - componentes y valores válidos de las fuentes

textAlign

Descripción Determina la colocación horizontal del texto dibujado.


Valores válidos son: start, end, left y right. El valor por
defecto es start.

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.

Métodos del objeto DrawingStyle


setLineDash(segments)

Descripción Este método establece la coniguración de los trazos y


espacios de las lineas dash. Acepta un argumento y es una matriz de
números que especiican una secuencia trazo/espacio/trazo/espacio,
etc. Cuando se haya terminado la secuencia, se repite hasta que
inalice la linea donde es aplicada la función.

getLineDash(segments)

Descripción Este método devuelve la actual coniguración de los trazos


y espacios de las lineas dash.
Ej.segments = styles.getLineDash();

Ejemplo objeto DrawingStyle

nombre del archivo - drawingStyle.js


var canvas =
document.getElementById(‘canvas’); var
context = canvas.getContext(‘2d’);
var p1 = new Path(‘M 100 350 q 150 -300 300
0’); var p2 = new Path();
var styles = new DrawingStyle();
styles.font = ‘20px sans-serif’;
p2.addText(‘Hello World’, styles, null, p1);

La región Hit

La región hit es posiblemente la más excitante de todas las


características agregadas a principios del 2012 por la especiicación
canvas.
Cada elemento canvas cuyo contexto primario es un objeto
CanvasRenderingContext2D debe tener una lista de regiones
Hit asociadas a este mapa de bits.
Las regiones Hit se asocian en una lista denominada: Lista de
Regiones Hit y cada región Hit consiste de la siguiente información:
Un Conjunto de pixeles
Es un conjunto de pixeles contenidos dentro del elemento canvas por
la que esta región es responsable.

Una circunferencia limitadora


Un conjunto de pixeles dentro de un area limitada por una
circunferencia dentro del elemento canvas y que rodea a la region Hit y
cuyo conjunto de pixeles ya se encontraban alli cuando esta fue
creada.
Una especiicación del cursor
Viene expresado ya sea en un valor de cursor CSS o la cadena
“ inherit” , que signiica que el cursor de la región Hit padre, sí este
existe, o del elemento canvas, si no, se va a usar en su lugar.

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)

Descripción Este método agrega una región Hit al canvas. El


argumento es un objeto con los siguientes miembros:
Path (por defecto null)
Un objeto path que describe los pixeles que forman parte de la región.
Si este miembro no se suministra o se establece en null, entonces se
usa la ruta predeterminada actual en su lugar.
ID (por defecto una cadena vacía)
La D a usar para esta región. Esta es usada en eventos mouseEvent
dentro del canvas (event.region) y una manera de hacer referencia a
esta región en futuras llamadas a addRegionHit().
parent D (por defecto null)
El D de la región padre, para propositos de navegación por
herramioenteas de accesibilidad y para el despliegue del cursor.
Cursor (por defecto “ inherit” )
El cursor a usar cuando el mouse esta sobre esta región. El valor
“ inherit” signiica que usa el cursor para la región padre (como ha sido
especiicao por el miembro parentID), si existe, o que usa el cursor del
elemento canvas si la region no tiene padre.
Control (por defecto null)
Un elemento (que es un descendiente del canvas) a la cual los eventos
han de ser enviados y que la herramientas de accesibilidad son
usadas como un sustituto para la descripción e interacción con esta
región.
Label (por defecto null)
Una etiqueta de texto para las herramientas de accesibilidad usado
como una descripción de esta región, si no hay control.
Role (por defecto null)
Un role-ARIA para las herramientas de accesibilidad usado para
determinar como se representa esta región, si no hay control.

La región Hit puede ser usada para una gran variedad de propósitos!

Con un ID, ellos podrían detectar la coalisión de objetos, ya el user


agent tendrá que comprobar que región del mouse esta sobre e incluir
el id de los eventos del mouse.
Con un control, podrían dirigir de forma automática los eventos al
elemento DOM permitiendo por ejemplo: hacer

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)

Descripción Remueve una región Hit (y todos sus ascendientes) desde


el canvas. Los argumentos son el D agregado al crearlo con el método
addHitRegion(). Los pixeles que estaban cubiertos por esta región y
sus descendientes son efectivamente eliminados por esta operación,
dejando la región no interactiva. En particular, las regiones que
ocuparon los mismos pixeles antes de las regiones removidas, se
solapan y no vuelven a su función anterior.

Ejemplo de aplicación de la región Hit

nombre del archivo - regionHit.html


<!DOCTYPE html>
<html>

<head>
<title>Aplicaci&oacute;n de La Region
Hit</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’>

Tu navegador no soporta canvas de HTML5


</canvas>
</div>

<script src=’javascript/regionHit.js’>
</script> </body>
</html>

nombre del archivo - composicionCanvas.js


var canvas =
document.getElementById(‘canvas’), context =
canvas.getContext(‘2d’);
context.beginPath();
context.rect(10,10,100,100);
context.addHitRegion({ id: ‘The First Button’
});
context.beginPath();
context.rect(120,10,100,100);
context.addHitRegion({ id: ‘The Second
Button’ });
canvas.onclick = function (event) {
if (event.region) {
alert(‘You clicked ‘ + event.region); }
};

Las reglas de la API de canvas


Los paths son un elemento básico de cualquier biblioteca de gráicos.
Cada vez que se dibuja un path, el navegador tiene que determinar si
un punto del canvas está dentro de la curva cerrada. Cuando el path
es un simple círculo o un rectángulo, esto es obvio, pero cuando la
trayectoria se corta a sí misma o esta tiene paths anidados, la cosa no
siempre es tan clara.
Existen dos formas comunes para calcular si un punto en un path debe
ser rellenado, estas son: ‘nonzero’ y ‘evenOdd’.

Nonzero Winding Rule

Descripción
Para probar si un punto P esta dentro de un path, utilizando la regla
‘nonzeron

winding rule’, imagina un rayo dibujado desde P, en cualquier


dirección, hasta el ininito (o mejor aún, hasta cierto punto fuera de la
caja delimitadora de los path). Ahora inicializamos un contador en cero
y enumeramos todos los lugares donde el path cruza el rayo. Cada vez
que el path cruza el rayo en dirección al sentido de las agujas del reloj,
agregamos uno al contador. Cada vez que el path cruza el rayo en el
sentido contrario a la dirección de las agujas del reloj, restamos uno a
nuestro contador. Si despues de que todos los pasos han sido
enumerados, el contador es distinto de cero, entonces el punto P esta
dentro del path. Si por el contrario, el recuento es igual a cero,
entonces el punto esta fuera del path.

imagen 2.36 - aplicación de la regla nonzero


Este es un path que consiste en 2 círculos. El círculo exterior se ejecuta
a la izquierda en dirección contraria al sentido de las agujas del reloj y
el círculo interior se ejecuta en dirección del sentido de las agujas del
reloj.
Tenemos 3 puntos y queremos determinar si están dentro del trazado.
La línea imaginaria que en este ejemplo, va desde la parte inferior
izquierda hacia la parte superior derecha, pero se puede dibujar la
forma que desee.
el punto 1. Total = 1 para el interior y pintado
el punto 2. Total = 1 - 1 = 0 por lo exterior y no pintada
punto 3. Total = 1 - 1 + 1 = 1 para el interior y pintado
Ahora, vamos a cambiar la dirección del círculo más cercano:

imagen 2.37
- aplicación de la regla nonzero

el punto 1. Total = 1 para el interior y pintado el punto 2. Total = 1 + 1 =


2 para el interior y pintado el punto 3. Total = 1 + 1 + 1 = 3 para el
interior y pintado

EvenOdd Rule

Descripción
El valor “ EvenOdd” indica la regla par-impar, en el que un punto se
considerará

como fuera de la igura o la forma, si el número de veces que una línea


recta semi-ininita dibujada desde ese punto cruza el trazo de la igura o
la forma entonces este será par.
Para determinar si un punto cae dentro del path, una vez más, dibuja
una línea que pasa por ese punto. Esta vez, le basta con añadir el
número de veces que se cruza el path Si el total es par, el punto está
fuera, y si es impar, el punto está dentro. El sentido o dirección del path
se pasa por alto. por ejemplo: el punto 1. Total = 1 para el interior y
pintado.
el punto 2. Total = 1 + 1 = 2, de modo exterior y no pintado. punto 3.
Total = 1 + 1 + 1 = 3 para el interior y pintado.

imagen 2.38 - aplicación de la regla evenOdd

La regla de liquidación ‘evenOdd’ es más fácil de entender para un


autor experimentado. Por ejemplo, si usted quiere hacer un donuts con
dos círculos, uno grande y otro pequeño, con la regla de liquidaciñón
‘nonzero’, tu tendrías que aplicar un pequeño truco y cambiar la
dirección del círculo pequeño en el sentido contrario al grande, con la
regla ‘evenOdd’, es más sencillo, basta trazar una linea y rellenar los
circulos con la regla de liquidación ‘evenOdd’.

Adicional al canvas

Como el contexto del canvas 2d no tiene soporte para la alineación


‘evenOdd’, mozilla implementa una propiedad con un preijo
‘mozFillRule’ que establece la regla de rellenos en el estado de los
gráicos. Esto tuvo algunos inconvenientes:

1. Arreglando esto se obliga al usuario a comprobar siempre la regla


de liquidación antes de cada relleno o clip, o tiene una convención
para establecer y restablecer la regla de liquidación que no es tan
común su uso.

2. Las regiones Clipping y la detección de colisiones también se ven


afectadas por esta regla, por lo que el nombre de ‘illRule’ es confuso.
3. Agregar más parámetros al estado de los gráicos crea algo de
sobrecarga.
4. Es más trabajo para el autor y el entorno, ya que tiene que hacer
una llamada adicional a través del límite javascript.
5. Casi todos los demás lenguajes gráicos (como PDF y SVG) y las
bibliotecas (como coreGraphics, direct2D y Skia) establecen la
liquidación en tiempos de uso.

Uno de las recopilaciones hechas alrededor del tema son las


siguientes:
enum CanvasWindingRule { “nonzero”, “evenodd”
};
void clip(optional CanvasWindingRule w =
“nonzero”);
boolean isPointInPath(unrestricted double x,
unrestricted double y, optional
CanvasWindingRule w = “nonzero”);

‘ill’, ‘clip’ y ‘isPointInPath ahora tendrán un parámetro opcional que


especiica qué norma de liquidación se va a aplicar. Si no lo especiica,
se obtiene el comportamiento antiguo que es la “ nonzero winding rule’.

El siguiente es un ejemplo que muestra esta característica en acción:

Ejemplo de aplicación de las reglas de la API de canvas


nombre del archivo - evenodd.js
var canvas =
document.getElementById(‘canvas’); var ctx =
canvas.getContext(‘2d’);

ctx.beginPath();
ctx.arc(75, 75, 75, 0, Math.PI*2, true);
ctx.arc(75, 75, 25, 0, Math.PI*2, true);

El resultado se mostrará mas o menos de esta manera: imagen 2.39


- ejemplo de aplicación de la regla evenodd
Los proyectos
Juegos

Los juegos son la razón por la que muchos de nosotros, en principio,


nos interesamos por las computadoras, hoy en día los juegos siguen
siendo la fuerza motriz que impulsa la tecnología informatica a nuevas
alturas. En este apartado, examinaremos como crear un framework
para mini-juego, que se podrá usar como base para crear juegos en el
canvas de HTML5. Vamos a explorar muchos de los bloques de
construcción básicos asociados con el desarrollo de juegos y
aplicarlos al canvas de HTML5 con la API de javascript.

No tenemos espacio, ni tampoco es el objetivo de este libro, para cubrir


cada tipo de juego que tu quieras crear, pero vamos a discutir muchos
temas elementales y algunos otros no tanto, necesarios para la gran
mayoría de los juegos. Al inal del ejercicio vamos a tener una copia
básica del clásico juego de Atari® ‘Asteroids’. Vamos a pasar a través
de la creación de este juego, aplicando en primer lugar algunas de las
técnicas de dibujo y transformaciones especíicas de los objetos
visuales de nuestro juego. Esto le ayudará a conseguir una base
gracias a la puesta en practica de todas los conceptos elementales
vistos en el capitulo 2 - La referencia, y aplicarlos a la creación de
juegos de arcade. Seguidamente vamos a crear como base un
framework de juego que pueda ser fácilmente aplicable a cualquier
juego de arcade que te propongas hacer en el canvas de HTML5.
Después nos sumergiremos en algunas técnicas de juegos y en el
estudio de varios algoritmos propios de la ocasión, y, para inalmente
aplicar todo lo que hemos tratado de crear al producto inal.

Juegos bidimensionales viajando en el espacio y disparando a los


enemigos, son solo el comienzo de lo que puede conseguirse en un
canvas de HTML5. Mientras desarrollamos este tutorial,
profundizaremos más en el uso y conocimientos de las principales
herramientas y técnicas que provee lña API del canvas de HTML5.

Porque juegos en HTML5?

Jugar en el navegador se ha convertido en una de las actividades más


populares para los usuarios de internet. El canvas de HTML5 ofrece a
los desarrolladores una API web para gestionar directamente la
elaboración de un área especíica del navegador. Esta funcionalidad
permite que el desarrollo del juegos en javascript sea mucho mas
potente que nunca.

El canvas comparado con flash

Usted encontrará que el canvas de HTML5 ofrece una funcionalidad


similar al Flash® de Adobe® en ciertas areas, pero, carece de algunas
de las características mas reinadas de Flash®.
Sin límite de tiempo

No hay linea de tiempo basadas en fotogramas para la animación


intrínseca en canvas. Esto signiica que necesitaremos codiicar todas
nuestras animaciones a partir de imágenes o trazados, y aplicar
nuestras propias actualizaciones basadas en fotogramas.
Sin lista de visualización

ActionScript 3® de Flash® ofrece la idea muy potente de una lista de


visualización de objetos, un desarrollador puede agregar cientos de
objetos físicos individuales desde la lista de visualización a la pantalla
de juego. El canvas de HTML5 sólo tiene un único objeto de
visualización (el propio canvas).

Que ofrece el canvas de HTML5?

A pesar de que el canvas de HTML5 carece de algunas de las


características que hacen de la plataforma Flash® un excelente medio
para el desarrollo de juegos, también es verdad que este tiene
algunos puntos fuertes.

Un potente escenario

El canvas de HTML5 es muy semejante al escenario de Flash®. Es una


pieza rectangular de espacio en pantalla, que se puede manipular
mediante programación. Desarrolladores avanzados de Flash®
pueden reconocer al canvas como un primo cercano, tanto a la
BitmapData como a los objetos Shape de actionScript. Podemos dibujar
directamente en el canvas con trazados e imágenes y transformarlos
sobre la marcha.

Objetos de visualización lógicos

Canvas de HTML5 nos dá solo un objeto de visualización físico, pero


podemos crear cualquier número de objetos de visualización lógicos.
Vamos a usar los objetos javascript para almacenar todos los datos
lógicos y métodos necesarios para dibujar y transformar nuestros
objetos de juego lógicos al canvas físico.

Archivo básico para el juego

Antes de comenzar a desarrollar nuestro juego de arcade, vamos a


crear un archivo básico de HTML y otro de javascript. Ambos los
usaremos para nuestros propositos.
Archivo base HTML para el juego

nombre del archivo - defensaEspacial.html


<!DOCTYPE html>
<html>

<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’>

Tu navegador no soporta canvas de HTML5


</canvas>
</div>

<script src=’defensaEspacial.js’></script>
</body>
</html>

nombre del archivo - defensaEspacial.js


window.addEventListener(‘load’,
eventWindowLoaded, false); function
eventWindowLoaded() {

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’;
}
}

El resultado se mostrará mas o menos de esta manera:


imagen 3.1 - base para el juego de arcade
Vamos a comenzar nuestro juego de forma similar a Asteroids de Atari®,
al cuál llamaremos Defensa Espacial.

El diseño de nuestro juego

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 asteroides comienzan el juego como grandes e inmensas


rocas, pero cuando son alcanzados por los misiles del jugador esta se
dividen y forman dos rocas de tamaño mediano, asimismo, cuando
estas rocas medianas son alcanzadas por los misiles del jugador,
nuevamente se dividen y crean dos rocas de menor tamaño, es decir
pequeñas, para luego ser totalmente destruidas una vez sea
alcanzadas por los misiles del jugador.

Cuando el jugador destruye todos los asteroides, avanza de nivel, el


cual consiste en una nueva tanda de asteroides pero ahora con mayor
velocidad. Esto continuará hasta que el jugador agote sus tres naves
espaciales. El jugador sin embargo cada vez que le sume 10000
puntos a la puntuación obtenida, recibirá una nave espacial adicional.

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.

Gráficos del Juego - Dibujar con paths

Entremos de lleno al desarrollo del juego en el canvas, primero


echaremos un vistazo a algunos de los gráicos que utilizaremos en
nuestro juego. Esto nos ayudará a tener una sensación visual del tipo
de código que tendremos que poner en práctica.

Que necesitaremos

Para la simulación del juego de Asteroids, que nosotros hemos llamado


Defensa Espacial, vamos a necesitar algunos gráicos muy simples ,
tales como:
• Un fondo negro sólido.
• Una nave espacial para el jugador que pueda girar y avanzar en la
pantalla del juego. Habrá que hacer

dos fotogramas para la animación de la nave espacial, un fotograma


para representar la nave estatica y un fotograma para representar la
nave en avance.
• Un platillo volador que vuele a traves de la pantalla y le dispare al
jugador.
• Algunas piedras o rocas que representen los asteroides y que el
jugador pueda dispararles. Para ello usaremos cuadrados simples.
Existen dos métodos diferentes que podemos emplear para dibujar los
gráicos de nuestro juego: imágenes
de mapa de bits o paths (trazados). Para el juego en esta sección, nos
centraremos en el uso de los paths.
En el apéndice B de este libro podrá ver como manipular las imagenes
de mapa de bits para los gráicos de
este juego.

Usar paths para dibujar la figura principal del juego

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.

La nave espacial estática (fotograma 1)


El fotograma principal que representará la nave del jugador será
dibujado con paths en una cuadrícula de 30 × 20. Como se muestra en
la siguiente imagen:

imagen 3.2 - Trazado que representa la nave espacial


Dibujar la nave espacial estática(jugador)
nombre del archivo
- defensaEspacial.js
//dibujar la nave espacial (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();

Dibujar con paths


Recordemos como dibujar con paths, revise los siguientes pasos:
1. Siempre empezar un nuevo path con la llamada al método beginPath
().
Atento

2. Establecer strokeStyle () antes de empezar a dibujar el trazado.


2. Establecer fillStyle () antes de empezar a dibujar el trazado.
3. Use una combinación de los métodos context moveTo () y
context.drawTo () para comenzar a trazar las líneas de la trayectoria.
4. Terminar el dibujo con la llamada a los métodos stroke () y fill(), y
cerca del path con closePath ().

La nave espacial de avance (fotograma 2)


Ahora dibujaremos el segundo fotograma que representará la nave
espacial (jugador) cuando este avance. Puede verlo en la imagen 3.3
Dibujar la nave espacial de avance(jugador)

nombre del archivo - defensaEspacial.js


//dibujar la nave espacial de avance
(jugador) 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(); imagen 3.3 - Trazado que


representa la nave espacial en avance

Animación en el canvas

La nave espacial del jugadior que acabamos de crear, tiene dos


fotogramas (estático y avance), pero solo podemos mostrar un
fotograma a la vez. Nuestro juego tendrá que cambiar el fotograma de
animación basado en el estado de la nave espacial del jugador y
debera ejecutarse en un temporizador para que se produzca esta
animación. Vamos a escribir el código que dará vida a nuestro
cronometro de juego.

Bucle para controlar nuestro juego

Los juegos en HTML5 Canvas requieren el uso repetido de un bucle


de actualización/renderizado para simular la animación. Hacemos esto
mediante el uso de la función JavaScript setTimeout(), que llama
repetidamente a la función de nuestra elección en intervalos de
milisegundos. Cada segundo de tiempo de juego/animación se
compone de 1.000 milisegundos. Si queremos que nuestro juego se
ejecute en 30 ciclos por segundo de actualización/renderizado,
llamamos esto a 30 fotogramas por segundo (FPS) promedio. Para
ejecutar nuestro intervalo a 30 FPS, en primer lugar hay que dividir
1.000 por 30. El resultado es el número de milisegundos en cada
intervalo:

const PROM_FOTOGRAMA = 30;


var intervaloTiempo = 1000/PROM_FOTOGRAMA;
gameLoop()
function gameLoop() {

crearDibujo();
window.setTimeout(gameLoop, intervaloTiempo);
}

Al llamar a la función crearDibujo() varias veces en cada intervalo de


tiempo de espera, se puede simular la animación.

Cambio de estado en la nave espacial (jugador)

Simplemente tenemos que cambiar los estados estático y de avance


para simular la animación. Vamos a escribir el código para poder hacer
esto. En el siguiente trozo de código, vamos a empezar a colocar las
variables de nivel de la clase canvasApp en una nueva sección justo
por encima de la función crearDibujo(). Este será el lugar en el futuro
de todas las variables que requieren un alcance global dentro del
objeto canvasApp ().
Dado que hemos realizado algunos cambios importantes en nuestro
codigo, a continuación mostraremos el archivo javascript completo para
poder visualizar estos pequeños cambios realizados.

nombre del archivo - defensaEspacial.js


window.addEventListener(‘load’,
eventWindowLoaded, false); function
eventWindowLoaded() {

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

Nuestro juego probablemente tendrá muchos objetos de visualización


lógicas individuales que necesitan ser actualizados en un solo
fotograma. Podemos hacer uso de los métodos save() y restore() y el
uso de la matriz de transformación para garantizar que el resultado inal
sólo afecte al objeto actual que estamos trabajando, y no todo el
canvas.

La pila del canvas


El estado del canvas se puede guardar en una pila y recuperarse
luego. Esto es importante cuando estamos transformando y animando
los objetos del juego, porque queremos que nuestros transformaciones
afecten sólo el objeto del juego actual y no a todo el canvas. El lujo de
trabajo básico para el uso de la pila del canvas en un juego se parece
a esto:
1. Guardar el canvas actual en la pila.
2. Transformar y dibujar el objeto del juego.
3. Recuperar el canvas salvado de la pila.

Como ejemplo, vamos a conigurar el giro básico para nuestra nave


espacial (jugador). Vamos a girar en 1 grado en cada fotograma.
Debido a que estamos elaborando en la actualidad la nave espacial
(jugador) en la esquina superior izquierda del canvas, vamos a
moverlo a una nueva ubicación. Hacemos esto porque el giro básico
utilizará la esquina superior izquierda de la nave espacial como el
punto de registro: la ubicación del eje utilizado para las operaciones
de rotación y la escala. Por lo tanto, si seguimos con la nave espacial
en las coordenadas 0,0 y se hace girar por la esquina superior
izquierda, usted no verá la nave espacial la mitad del tiempo, ya que su
ubicación sería en los bordes superior e izquierdo del canvas. En su
lugar, colocaremos la nave espacial en las nuevas coordenadas 50,50.
Nosotros vamos a usar el mismo código javascript que en el ejemplo
anterior, cambiando sólo la función crearDibujo(). Para simpliicar este
ejemplo, vamos a eliminar la variable estadoNave y nos
concentraremos en el estado estático solamente. Agregaremos tres
nuevas variables arriba de la función crearDibujo():

var rotacion=0;-mantiene la rotación actual


de la nave (jugador) var x=50; -ocupa la
posición x para empezar a dibujar la nave
(jugador) var y=50; - ocupa la posición y
para empezar a dibujar ela nave (jugador)

A continuación nuevamente el código completo de nuestro archivo


javascript:

nombre del archivo - defensaEspacial.js


window.addEventListener(‘load’,
eventWindowLoaded, false); function
eventWindowLoaded() {

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() {

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 // 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++;

const PROM_FOTOGRAMA = 40;


var intervaloTiempo = 1000/PROM_FOTOGRAMA;
temporizador();

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

Como vimos anteriormente, podemos girar fácilmente los gráicos del


juego en la esquina superior izquierda utilizando el método de
transformación context.rotate(). Sin embargo, nuestro juego tendrá que
rotar los objetos en el centro en lugar de la esquina superior izquierda.
Para ello, tenemos que cambiar el punto de transformación en el centro
de nuestro juego para los objetos gráicos.

Rotando la nave espacial desde el centro

El código para girar la nave espacial (jugador) desde su punto central


es casi exactamente igual que el código utilizado para girarlo desde la
esquina superior izquierda. Lo que tenemos que hacer es modiicar el
punto desde donde se hace el movimiento de traslación. En el código
anterior, colocamos el contexto de dibujo de modo inmediato a las
coordenadas x e y de nuestro objeto juego (50,50). Esto tuvo el
efecto de hacer girar el objeto desde la esquina superior izquierda.
Ahora hay que llevar el movimiento de traslación al centro de nuestro
objeto:

context.translate (x + 0,5 * width, y + 0,5 *


height);

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:

context.moveTo (16 - 0.5 * width, 0 - 0.5 *


height); context.lineTo (30 - 0.5 * width, 19
- 0.5 * height);

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:

context.moveTo (6, -10) / / 16-10, 0-10


context.lineTo (20,9) / / 30-10, 19-10

El método en el que se usan los valores calculados (usando la anchura


y la altura de las variables) es mucho más lexible, mientras que el
método codiicado es mucho menos intenso para el procesador. Con la
versión calculada nuestro código quedaría de la siguiente manera:

nombre del archivo - defensaEspacial.js


window.addEventListener(‘load’,
eventWindowLoaded, false); function
eventWindowLoaded() {
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;
function crearDibujo() {

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);//reestable
la identidad //Trasladar el canvas original
al centro del jugador
context.translate(x+.5*width,y+.5*height);
context.rotate(anguloEnRadianes);

//dibujar la nave espacial estatica (jugador)


context.strokeStyle = ‘red’;

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);

}
}

Desvanecimiento gradual de la nave espacial (jugador)

Cuando una nave espacial entra en la pantalla de nuestro juego


Defensa Espacial, vamos a tener que crear un desvanecimiento desde
opaco a transparente, igual ocurre si nuestra nave es alcanzada por
cualquier objeto enemigo, el desvanecimiento se hara al contrario. En
la siguiente muestra de código, mostraremos como vamos a crear esta
transformación en nuestro juego.
Para usar el atributo context.globalAlpha del canvas, simplemente
tenemos que conigurar un número entre 0 y 1 antes de dibujar los
gráicos del juego. Vamos a crear una nueva variable en nuestro
código, a la cual, llamaremos transparencia, que contendrá el valor
alpha actual de nuestra nave espacial. Vamos a aumentar este valor
gradualmente en 0.01 hasta llegar a 1. Cuando realmente creemos
nuestro juego, lo detendremos en 1 y comenzaremos nuestro nivel de
juego. Sin embargo, aclaremos que para efectos de esta demo solo nos
limitaremos a repetir el mismo nivel una y otra vez.

nombre del archivo - defensaEspacial.js


window.addEventListener(‘load’,
eventWindowLoaded, false); function
eventWindowLoaded() {

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);

//dibujar la nave espacial estatica (jugador)


context.strokeStyle = ‘red’;

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 transparencia+=.01;


if (transparencia > 1) {

transparencia=0; }
}

const PROM_FOTOGRAMA = 45;


var intervaloTiempo = 1000/PROM_FOTOGRAMA;
temporizador();

function temporizador(){
crearDibujo();
window.setTimeout(temporizador,
intervaloTiempo);

}
}

Física y animación de los objetos del juego

Todos los objetos del juego se moverá en un plano de dos


dimensiones. Vamos a utilizar los vectores básicos de movimiento
direccional para calcular el cambio en las coordenadas x e y para
cada objeto del juego. En su nivel más básico, estaremos actualizando
la coordenada x (dx) y la coordenada y (dy) de cada uno de los
objetos del juego en cada fotograma para simular el movimiento. Estos
valores dx y dy se basarán en el ángulo y la dirección en la que
queremos que el objeto se mueva. Todos nuestros objetos de
visualización lógicos sumarán sus respectivos valores de dx y dy a su
valores x e y en cada fotograma de la animación. la nave del jugador
no usará dx ni dy estricto, porque tiene que ser capaz de lotar y girar
de forma independiente. Vamos a analizar en mayor profundidad el
movimiento de la nave del jugador ahora.

Como se moverá la nave del jugador


Nuestra nave cambiará su angulo desde el centro del eje de rotación
cuando el jugador presione las teclas izquierda o derecha del teclado.
Cuando el jugador presione la tecla arriba, la nave acelerará (avanza)
en dirección al angulo que se encuentre actualmente. Porque no hay
fricción aplicada a la nave, esta continuará lotando en dirección del
angulo de aceleración actual, hasta que sea aplicado un angulo
diferente de aceleración. Esto sucede cuando el jugador gira la nave a
un nuevo angulo presionando las teclas derecha o izquierda y luego
presione la tecla arriba para avanzar una vez mas.

La diferencia entre avanzar y moverse

Nuestra nave puede girar en la dirección que desee mientras se este


moviendo en una dirección diferente. Por esta razón, no podemos
simplemente usar los valores clásicos dx y dy para representar el
valor de movimiento en los ejes x e y. Hay que mantener los dos
conjuntos de valores para la nave en cada momento. Cuando el
jugador hace girar la nave pero no avanza, necesitamos dibujar la
nave en el nuevo angulo rotado. Todos los misiles que la nave dispare,
deben también moverse en la misma dirección de la nave. En el eje x
nombraremos este valor avanceX y en el eje y lo llamaremos
avanceY. Los valores moverX y moverY manejarán el movimiento
de la nave en la dirección que se indique cuando el avance sea
aplicado. Se necesitarán los 4 valores para mover la nave hacia una
nueva dirección.

Avanzar en la dirección rotada

Después que la nave ha girado en la dirección deseada, el jugador


puede avanzar hacia adelante al presionar la tecla hacia arriba del
teclado, esta tecla acelera la nave del jugador, solo mientras la tecla se
encuentre presionada. Y gracias a que conocemos la rotación de la
nave, podemos calcular facilmente el angulo de rotación. Solo
entonces podemos añadir los atributos x e y de la nave para hacerla
avanzar.

En primer lugar debemos cambiar los valores de rotación de grados a


radianes.
var anguloEnRadianes = rotacion * Math.PI /
180;
Ustedes han visto esto antes, es exactamente igual a la forma en que
se calculó la transformación de rotación antes de que fuera aplicada a
la nave del jugador.

Cuando tenemos el ángulo de rotación de la nave, debemos calcular


los valores avanceX y avanceY de la dirección actual. Lo hacemos
solo cuando vamos a avanzar, porque es un cálculo costoso, en lo que
se reiere al procesador, ya que consume muchos recursos del sistema.
Nosotros podríamos calcular éstos cada vez que el jugador cambia la
rotación de la nave, pero hacerlo sería una sobrecarga innecesaria
del procesador:

avanceX = Math.cos(angleInRadians); avanceY =


Math.sin(angleInRadians);
Cuando tenemos los valores de los ejes x e y, que representan el
sentido de la dirección actual de la nave del jugador, podemos calcular
los nuevos valores moverX y moverY para la nave del jugador:
moverX = moverX+aceleracionAvance*avanceX;
moverY = moverY+aceleracionAvance*avanceY;

Para aplicar estos nuevos valores a la posición actual de la nave del


jugador, necesitaremos añadirselas a sus actuales posiciones x e y.
Esto no ocurre sólo cuando el jugador presiona la tecla hacia arriba. Si
lo hiciera, la nave del jugador no lotaría, sino que se movería sólo
cuando se presiona la tecla. Debemos modiicar los valores x e y en
cada fotograma con los valores moverX y moverY:

x = x+moverX; y = y+moverY;

Redibujar la nave del jugador para comenzar en un angulo 0.

Como recordarán, cuando dibujamos por primera vez la nave del


jugador, tuvimos la punta de la nave (la parte superior) hacia arriba.
Hicimos esto para facilitar el dibujo, pero no es realmente la mejor
dirección para dibujar nuestra nave cuando lo que intentamos es
realizar calculos de comprensión rotatoria. Cuando la nave apunta
hacia arriba en realidad lo que tenemos es un angulo de -90 grados (o
270). Si queremos dejar todo como esta actualmente, tendremos que
modiicar el calculo de nuestra variable anguloEnRadianes a algo
parecido a esto:

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:

//dibujar la nave espacial del jugador


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);

Controlar la nave del jugador con el teclado

Vamos a añadir dos eventos de teclado y una matriz de objetos que va


a mantener el estado cada vez que se presione una tecla. Esto
permitirá que el jugador mantenga pulsada una tecla y que se repita
sin pausa. Los juegos de arcade requieren este tipo de respuesta al
presionar las teclas.
Una matriz que guarda las pulsaciones de las teclas
Una matriz contendrá el valor verdadero o falso para cada keyCode
asociado con eventos de teclado. El keyCode será el índice de la matriz
que va a recibir el valor verdadero o falso:

var listaTeclasPres = [];

Los Eventos de teclado


Usaremos eventos separados para la tecla de arriba y la tecla de
aabajo. El evento de tecla pulsada (keyDown Event) pondrá un valor
true en la matriz listaTeclasPres en el índice asociado al
keyCode del evento. De lo contrario, el evento de tecla arriba colocará
un valor falso a dicho índice en la matriz:

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; };

Evaluando las teclas pulsadas


Nuestro juego necesitará incluir el código para buscar valores true (o
false) en la matriz listaTeclasPres y utilizar esos valores para
aplicar la lógica del juego:

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.

nombre del archivo - defensaEspacial.js


window.addEventListener(‘load’,
eventWindowLoaded, false); function
eventWindowLoaded() {

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 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);

//Trasladar el canvas original al centro del


jugador
context.translate(x+.5*width,y+.5*height);
context.rotate(anguloEnRadianes);

//dibujar la nave espacial estatica (jugador)


context.strokeStyle = ‘red’;

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;

};
}

Cuando se ejecuta este archivo en un navegador, la aplicación será


capaz de responder cuando tu oprimas las teclas derecha e izquierda
y la nave girará sobre su eje central. Si pulsa la tecla de arriba, la nave
se moverá en la dirección en la que se enfrenta.
Dar a la nave del jugador máxima velocidad

Si has probado el código escrito anteriormente, te darás cuenta de dos


pequeños problemas:
1. La nave del jugador puede ir fuera de los lados de la pantalla y
perderse.
2. La nave del jugador no tiene la velocidad máxima.

Vamos a resolver el primer problema cuando empezemos a codiicar el


juego completo, pero por ahora, vamos a ver cómo aplicar una máxima
velocidad con el coódigo de movimiento actual. Supongamos que le
damos a la nave del jugador una aceleración máxima de dos píxeles
por fotograma. Es fácil calcular la velocidad actual, si nos movemos sólo
en las cuatro direcciones principales: arriba, abajo, derecha, izquierda.
Cuando nos estamos moviendo hacia la izquierda o la derecha, el
valor moverY siempre será 0. Si nos movemos hacia arriba o hacia
abajo, el valor moverX siempre será 0. La velocidad actual en la que
nos estamos moviendo en un eje sería fácil comparar a la velocidad
máxima.
Pero en nuestro juego, estamos casi siempre avanzando en las
direcciones x e y al mismo tiempo. Para calcular la velocidad actual y
compararlo con una velocidad máxima, debemos usar un poco más de
matemáticas.

En primer lugar, supongamos que vamos a añadir una variable de


velocidad máxima a nuestro juego:
var velocidadMax = 2;

Lo siguiente, es que debemos asegurarnos de calcular y comparar el


valor de la variable velocidadMax a la velocidad actual, antes de
calcular los nuevos valores para moverX y moverY. Haremos esto
con variables locales usadas para almacenar los nuevos valores para
las variables moverX y moverY antes de que sean aplicados.

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:

if (velocidadActual < velocidadMax) { moverX


= moverXNuevo;
moverY = moverYNuevo;

Un framework para juegos básico

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.

El grupo estático del juego

Lo que llamo grupo estático del juego, no es más que la construcción


de un pequeño programa que permite a nuestro juego estar en un solo
estado de la aplicación en cualquier momento. Vamos a crear un grupo
estático para nuestro juego, llamado estado de la aplicación, que
incluirá siete estados básicos (vamos a usar constantes para referirnos
a estos estados):

• ESTADO_TITULO_DEL_JUEGO
• ESTADO_NUEVO_JUEGO
• ESTADO_NUEVO_NIVEL
• ESTADO_JUGADOR_INICIAL
• ESTADO_NIVEL_DEL_JUEGO
• ESTADO_JUGADOR_MUERTO
• ESTADO_JUEGO_TERMINADO

Vamos a crear una función para cada estado, dicha función va a


contener la lógica del juego necesarias para cada estado y para
cambiar a un nuevo estado cuando sea apropiado. Al hacer esto,
podemos usar la misma estructura para cada juego que creemos,
simplemente cambiando el contenido de cada función de estado (como
nos referiremos a ellos).
Echemos un vistazo a una versión muy básica de esto en acción. Vamos
a utilizar una variable de referencia a una función llamada
funcionActualEstadoDeJuego, así como una variable
llamada estadoActualDeJuego que almacenará un valor
constante: el estado actual de la aplicación.

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();
}

Veamos el código completo. Vamos a crear algunas funciones shell


para los diferentes estados de la aplicación. Antes de que se inicie la
aplicación, vamos a llamar a la función cambioEstadoApp()y le
pasaremos el valor constante para la nueva función que queremos que
es la funcionActualEstadoDeJuego:

//*** inicio de la aplicación


cambioEstadoApp(ESTADO_TITULO_DEL_JUEGO);
A continuación actualizaremos nuestro código, vamos a utilizar el
estado ESTADO_TITULO_DEL_JUEGO para dibujar una pantalla de
título simple que volvera a dibujarse en cada fotograma.
nombre del archivo - defensaEspacial.js
window.addEventListener(‘load’,
eventWindowLoaded, false); function
eventWindowLoaded() {

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_NIVEL=1; const
ESTADO_JUEGO_TERMINADO=2;

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();
}

//*** inicio de la aplicación


cambioEstadoApp(ESTADO_TITULO_DEL_JUEGO);

//**** aplicación temporizador


const PROM_FOTOGRAMA = 45;
var intervaloTiempo = 1000/PROM_FOTOGRAMA;
temporizador();

function temporizador() {
iniciarJuego();
window.setTimeout(temporizador,intervaloTiempo

}
}
//***** objecto prototypes *****

//*** objeto consoleLog


//llamamos al constructor de la clase
function ConsoleLog(){

}
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

En cualquiera de los estados de la aplicación, es posible que


necesitemos actualizar la animación y la pantalla de juego. Ahora bien,
nosotros nos encargaremos de estos cambios, agregando al código
dos métodos diferentes para ello: actualizar() y
renderizar(). Por ejemplo, como podemos recordar, la nave del
jugador puede moverse alrededor de la pantalla de juego, y cuando el
jugador pulsa la tecla hacia arriba(avance), el fotograma debería
mostrar la nave de jugador avanzando en lugar de la nave estática. En
los ejemplos anteriores, teniamos todo el código que actualiza las
propiedades de la nave, así como el código que construye la nave, en
una única función llamada crearDibujo(). Vamos a revisar a
fondo el código anterior, y vamos a deshacernos de esta función
llamada crearDibujo() y en su lugar usaremos las funciones
separadas: actualizar() y renderizar(). También vamos a
separar el código que comprueba cuando una determinada tecla
especiica ha sido pulsada en el juego agregando a nuestro código
otra función llamada chequearTeclas().

Vamos a reexaminar el contenido de la función crearDibujo(),


pero esta vez, vamos a disolver la función en funciones separadas
para cada conjunto de tareas, como se muestra en el código siguiente.

nombre del archivo - defensaEspacial.js


window.addEventListener(‘load’,
eventWindowLoaded, false); function
eventWindowLoaded() {
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 del sentido de las agujas
del reloj

rotacion-=velocidadDeRotacion;
}
if (listaTeclasPres[39]==true) {

//rotar en direccion de las agujas del reloj


rotacion+=velocidadDeRotacion;
}
}
function actualizar() {

y=y+moverY;
}
function renderizar() {

// dibujar el fondo y el texto 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);//reestable
la identidad

//Trasladar el canvas original al centro del


jugador
context.translate(x+.5*width,y+.5*height);
context.rotate(anguloEnRadianes);

//dibujar la nave espacial estatica (jugador)


context.strokeStyle = ‘red’;

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();

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;
};
}

Dejamos fuera todos los estados de la aplicación solo para ahorrar


espacio, simplemente estamos mostrando lo que la función
estadoNivelJuegoApp() puede hacer.

Profundizaremos mas en los detalles importantes a medida que


comencemos a construir la aplicación completa, mientras tanto es
importante que vayamos concentrandonos en saber de donde
proviene cada función y en el análisis que se hace para crearlas.

El objeto prototype contadorDeFotogramas

Los juegos arcade tales como Asteroids y Defensa Espacial se basan


en el procesado rápido y actualización de la pantalla de juego para
asegurarse de que toda el renderizado del objeto-juego y la lógica de
juego se entregan al jugador a una velocidad segura. Una forma de
saber si su juego está llevando a cabo este trabajo es emplear el uso
de un contador de fotogramas por segundo (FPS). A continuación se
muestra una sencilla función que nos será útil para este in y además
puede ser reutilizado en cualquier juego que se crea en el canvas de
HTML5:

//*** objecto prototype contadorDeFotogramas


function contadorDeFotogramas() {
this.ultimoContadorDeFotogramas = 0;
var tiempo = new Date();
this.ultimoFotograma = tiempo.getTime();
delete tiempo;
this.contadorF = 0;
}
contadorDeFotogramas.prototype.cuentaFotograma
{
var tiempo = new Date();
this.contadorF++;
if (tiempo.getTime()
>=this.ultimoFotograma+1000) {
ConsoleLog.log(“evento fotograma”);
this.ultimoContadorDeFotogramas =
this.contadorF;
this.ultimoFotograma = tiempo.getTime();
this.contadorF = 0;
}
delete tiempo;
}
Nuestro juego va a crear una instancia de este objeto y nuestra función
actualizar() llamará a la función cuentaFotogramas() en cada
fotograma. Vamos a escribir la cantidad de fotogramas actuales en
nuestra función renderizar().
En el siguiente código se muestra estas funciones agregadas al
código. Como alternativa, usted puede colocar este código dentro de
sus propias etiquetas <script> o también puede crear un archivo
javascript separado y hacer referencia a este en el documento
principal. Para simpliicar, nosotros vamos a mantener todo nuestro
código en un solo archivo.

nombre del archivo - defensaEspacial.js


window.addEventListener(‘load’,
eventWindowLoaded, false); function
eventWindowLoaded() {

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

//Trasladar el canvas original al centro del


jugador
context.translate(x+.5*width,y+.5*height);
context.rotate(anguloEnRadianes);

//dibujar la nave espacial estatica (jugador)


context.strokeStyle = ‘red’;

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;

};
}

//*** objecto prototype contadorDeFotogramas


function ContadorDeFotogramas() {
this.ultimoContadorDeFotogramas = 0;
var tiempo = new Date();
this.ultimoFotograma = tiempo.getTime();
delete tiempo;
this.contadorF = 0;
}
ContadorDeFotogramas.prototype.cuentaFotograma
{
var tiempo = new Date();
this.contadorF++;
if (tiempo.getTime()
>=this.ultimoFotograma+1000) {
ConsoleLog.log(‘evento fotograma’);
this.ultimoContadorDeFotogramas =
this.contadorF; this.ultimoFotograma =
tiempo.getTime(); this.contadorF = 0;
}
delete tiempo;
}

//***** objecto prototypes *****

//*** objeto consoleLog


//llamamos al constructor de la clase
function ConsoleLog(){
}
//crear la función que se agregará a la clase
console_log=function(message) {

console.log(message);
}
}
//agregar la función clase/estatica para la
clase por asignación
ConsoleLog.log=console_log;

Crear nuestro juego aplicando todo lo anterior

Ahora estamos listos para empezar a programar nuestro juego. En


primer lugar, vamos a ver la estructura del juego y algunas de las ideas
que sustentan los diversos algoritmos que se emplean para crearlo.
Después de eso, vamos a presentar el código fuente completo de
nuestro juego Defensa Espacial.

La estructura de nuestro juego: Defensa Espacial

La estructura de la aplicación del juego es muy similar a la estructura


que hemos empezamos a construir en este mismo tutorial. Vamos a
echar un vistazo a las funciones de cada uno de los estados de la
aplicación y ver cómo van a trabajar juntas.

Aplicando los estados de juego


Como lo habiamos visto anteriormente el juego contará con siete
estados distintos para la aplicación. Vamos a almacenar estos en
constantes de la siguiente manera:

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;

Las funciones de los estados de la aplicación

Cada estado individual tendrá una función asociada que se llamará en


cada fotograma. vamos a deinir teórica y practicamente cada una de
estas funciones, no se sorprendan si ven algunas variables y funciones
no declaradas, ya que serán declaradas en el transcurso de las
siguientes páginas.

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; }

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++){
var nuevaPiedra={};
nuevaPiedra.scale=1; nuevaPiedra.width=50;
nuevaPiedra.height=50;
nuevaPiedra.widthMedio=25;
nuevaPiedra.heightMedio=25;

//nuevas piedras se inician en la esquina


superior izquierda //para seguridad de la
nave del jugador
//ConsoleLog.log(“nuevaPiedra.x=” +
nuevaPiedra.x);

//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()

muestra la pantalla ‘juego terminado’ e inicia un nuevo juego al pulsar


la barra de espacio.
function estadoJuegoTerminado() {
ConsoleLog.log(“Estado juego terminado”);
rellenarFondo();
renderizarTableroPuntuacion();
70, 180);

}else{ //esperar por click en la barra de


espacio if (listaTeclasPres[32] == true){

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.

function resetearJugador() { jugador.rotacion


= 270;
jugador.x = .5 * xMax;
jugador.y = .5 * yMax;
jugador.avanceX = 0;
jugador.avanceY = 0;
jugador.moverX = 0;
jugador.moverY = 0;
jugador.alpha = 0;
jugador.cuentaMisilesPorFotograma=0;

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);

var moverXNuevo = jugador.moverX +


jugador.aceleracionAvance * jugador.avanceX;
var moverYNuevo = jugador.moverY +
jugador.aceleracionAvance * jugador.avanceY;

var velocidadActual= Math.sqrt


((moverXNuevo*moverXNuevo) +
(moverYNuevo*moverYNuevo));

if (velocidadActual < jugador.velocidadMax) {


jugador.moverX = moverXNuevo;
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.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];

misiles:for (var cuentaMisilesJugador =


cantidadMisilesJugador; cuentaMisilesJugador
>= 0;
cuentaMisilesJugador--){
misilesJugadorTemp =
misilesDelJugador[cuentaMisilesJugador];

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(); }
}

//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];

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; }
}

//jugador contra platillo


if (colisionCuadroDelimitador(platillosTemp,
jugador)){ ConsoleLog.log(“colision
jugador”);
crearExplosion(platillosTemp.x + 16,
platillosTemp.y + 16, 10);
agregarAPuntuacion(platillosTemp.valorPuntuaci

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;

ConsoleLog.log(“disparo del platillo”);


//fuego al jugador desde pequeño platillo
var grados = 360 * radianes / (2 * Math.PI);
nuevoMisilPlatillo.dx = 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
{

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;
}

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);

if (derecha1 < izquierda2) return(false);


if (izquierda1 > derecha2) 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()

Esta función acepta como argumento un valor y lo suma a la puntuación


del jugador.

function agregarAPuntuacion(valor){
puntuacion+=valor;
}

Variables globales de la aplicación


Ahora vamos a ver todo el conjunto de juego de variables de ámbito de
aplicación necesarias para nuestro juego.

Variables que luyen en la pantalla de control


Estas variables se utilizan cuando aparecen por primera vez la pantalla
con el título y el “ Fin de Juego” . Serán establecidas en true después
de que se hayan dibujado en la pantalla. Cuando estas variables han
sido establecidas en true, la pantalla de juego buscará un evento
que muestre que la barra espaciadora ha sido presionada y solo
después de eso pasará al siguiente estado de la aplicación:

//titulos en pantalla
var titulosIniciales=false;

Variables del entorno del juego


Estas variables coniguran los valores predeterminados necesarios
para un nuevo juego. Vamos a discutir elpuntoParaNaveExtra y
naveExtraGanada en la sección “ La concesión de las naves
adicionales del jugador” en las siguientes paginas:
//entorno de juego
var puntuacion=0;
var nivel=0;
var puntosParaNaveExtra=10000; var
naveExtraGanada=0;
var navesJugador=3;

Variables del campo de juego


Estas variables coniguran las coordenadas máximas y mínimas para el
campo del juego y todas sus etapas:

//campo de juego var xMin=0;


var xMax=400; var yMin=0;
var yMax=600;

Variables para los valores de puntuación


Estas variables coniguran los valores para cada uno de los objetos
que el jugador pueda destruir:

//valores de puntuación
var puntosPiedrasGrandes=50; var
puntosPiedrasMedianas=75; var
puntosPiedrasPequenas=100; var
puntosPlatillos=300;

Constantes para los tamaños de las piedras


Estas variables establecen algunos valores legibles para los tres
tamaños de piedra que usaremos en el juego, esto nos permite utilizar
simplemente la constante en lugar de un valor literal. A continuación,
puede cambiar el valor literal, si es necesario:
//constantes para la escala de las piedras
const ROCK_SCALE_LARGE=1;
const ROCK_SCALE_MEDIUM=2;
const ROCK_SCALE_SMALL=3;

Objetos lógicos mostrados


Estas variables establecen el objeto solo jugador y matrices para
almacenar los demás objetos de visualización lógicas para nuestro
juego. Ver en las próximas secciones “ El objeto Jugador” y “ Matrices
de objetos lógicos mostrados en pantalla” para obtener más
información acerca de cada uno de estos:

//crear objetos del juego y las matrices var


jugador={};
var piedras=[];
var platillos=[];
var misilesDelJugador=[];
var particulas=[];
var misilesDelPlatillo=[];

Variables especiicas de nivel


Las variables especiicas de nivel son las encargadas de manejar el
grado de diicultad en cada uno de los niveles del juego cuando este
va aumentando de nivel, Vea la sección ‘Mandos de nivel’ para tener
una mayor información de como usar estas variables:

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

El objeto jugador contiene muchas de las variables que nos


encontramos al principio de este tutorial, cuando hemos hablado de
animación, rotación y movimiento de la nave del jugador en la pantalla
de juego. También hemos añadido tres nuevas variables que no has
visto antes. Todas estas variables son instanciadas en la función
estadoNuevoJuego() que anteriormente habíamos deinido:

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;

Las nuevas variables introducidas en el objeto jugador y que no


hemos visto anteriormente son widthMedio, heightMedio y
retrasoMisilFotograma. widthMedio y heightMedio
simplemente guardan la mitad de los valores del ancho y el alto, por lo
que evitamos que estos sean calculados en cada fotograma en los
múltiples lugares dentro del código, ahorrando para ello muchos
recursos del sistema. La variable retrasoMisilFotograma
contiene el número de fotogramas que debe haber entre cada disparo
de misil del jugador. De esta manera, el jugador no podrá disparar un
lujo constante de misiles destruyendo asi todo lo que este a su alcance
con una diicultad casi nula.

Algoritmos del juego Defensa Espacial

Vamos a tratar de explicar con mas detalle lo que vamos a estudiar en


esta sección ya que implica mucho de javascript y como hemos dicho a
lo largo del libro, no es uno de los objetivos de este libro, profundizar
en el tema del aprendizaje de javascript, si crees que tienes o tendrás
problemas para entender lo que aquío se quiere explicar, dirijete al
capitulo 1 de este libro y lee la sección javascript, también te
recomendamos leas libros especializados del tema para un mayor
aprovechamiento del canvas de HTML5.

Matrices de objetos lógicos mostrados en la pantalla de juego


Hemos utilizado matrices para almacenar todos los objetos de
visualización lógicos, y tenemos una gran variedad para cada tipo de
objeto usado en nuestro juego (piedras, platillos, misilesDelJugador,
misiliesDelPlatillo y partículas). Cada objeto de visualización lógico es
una instancia de un objeto simple. Hemos creado una función
separada para dibujar y actualizar cada uno de los objetos.

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

Las rocas serán simples cuadrados que giran en el sentido de las


agujas del reloj o contrario a este. Las instancias de piedra estarán en
la matriz rocas. Cuando se inicia un nuevo nivel, todos estos serán
creados en la esquina superior izquierda de la pantalla del juego.
Estos son las variables atributos de un objeto piedra:

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;

El tamaño de la piedra estará conigurado en una de las tres constantes


que deinen el tamaño de las piedras discutidas anteriormente en este
tutorial. Los atributos widthMedio y heightMedio se establecen
basados en la escala, y se usarán de la misma forma que la version del
objeto jugador. Los valores de los atributos dx y dy representan los
valores de los ejes x e y en el sistema de coordenadas del canvas
cuando las piedras son actualizadas en cada fotograma.

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

Cuando una piedra, un platillo, o la nave del jugador es destruida, el


objeto explota creando una serie de partículas. La función
crearExplosion() crea lo que llamamos una explosión de
partículas. Las partículas son simplemente objetos de visualización
lógicas individuales con sus propios valores para los atributos life,
dx y dy. La generación aleatoria de estos valores hace que cada
explosión parezca única. Las partículas se almacenan en la matriz de
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

A pesar de que no estamos mostrando el número del nivel del juego al


jugador, estamos ajustando la diicultad del juego cada vez que la
totalidad de las piedras de un nivel son destruidas y renovada la
pantalla de juego. Hacemos esto mediante el aumentar en 1 la variable
nivel y luego volver a calcular estos valores antes de que comience el
nuevo nivel. Nos referimos a la variación en el nivel de diicultad como
mandos que se reiere a las marcas o interruptores. Estas son las
variables que utilizaremos para estos botones:

level ++;

Se reiere al numero de piedras.


nivelAjusteVelocidadMaxPiedra = nivel * .25;
La velocidad máxima de las piedras.

El número de platillos simultaneos.


nivelPromedioAparicionPlatillo = 10 + 3 *
nivel;
El porcentaje de probabilidad de que aparezca un platillo.
nivelVelocidadPlatillo = 1 + .5 * nivel;
La velocidad del platillo.
nivelRetrasoDisparoPlatillo = 120 - 10 *
nivel;
El tiempo entre cada misil del platillo.
nivelPromedioDisparoPlatillo = 20 + 3 *
nivel;

Porcentaje de probabilidades de que el platillo dispare al jugador.


nivelVelocidadMisilPlatillo = 1 + .2*nivel;
La velocidad de los misiles del platillo.

Nivel y Fin de Juego


Tenemos que comprobar el juego y el inal de nivel, así podemos crear
la transición entre un nuevo juego o al siguiente nivel.
Final de nivel
Vamos a chequear el in de nivel en cada fotograma, para llevar esto a
efecto nuestro código deberá parecerse a esto:
function chequearFinDeNivel(){
if (piedras.length==0) {
cambioEstadoApp(ESTADO_NUEVO_NIVEL); }
}
Cuando la cantidad de elementos en la matriz piedras es igual a 0,
cambiamos el estado a ESTADO_NUEVO_NIVEL.
Final de juego
No necesitamos comprobar el inal del juego en cada fotograma.
Tenemos que comprobar cuando el jugador pierde una nave. Esto lo
hacemos dentro de la estadoJugadorMuerto():

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);

}
}
}

Esta es la función que se llama en cada fotograma durante el estado


ESTADO_JUGADOR_MUERTO. En primer lugar, se comprueba que
ya no están las partículas de la explosión en la pantalla de juego. Esto
nos asegura que el juego no terminará hasta que la nave del jugador
ha terminado de explotar. También comprobamos que a todos los
misiles de los jugadores se les han terminado su tiempo de vida.
Hacemos esto para que podamos comprobar si hay colisiones de las
piedras con los misiles del jugador y colisiones de piedras contra los
platillos. De esta manera el jugador puede ganar una nave extra antes
de llamar a la variable navesJugador.
Cuando las partículas de la explosión y los misiles del jugador han
salido de la pantalla de juego, restamos 1 de la variable
navesJugador y luego cambiamos el estado de la aplicación a
ESTADO_JUEGO_TERMINADO si el número de naves del jugador
es menor que 1.

Adjudicar naves extras a los jugadores


Queremos premiar a los jugadores con naves adicionales cada cierto
tiempo basado en su puntuación. Hacemos esto mediante el
establecimiento de una cantidad de puntos que el jugador debe
alcanzar para ganar un nueva nave, esto también ayuda a mantener
un recuento del número de naves ganadas:

function chequearParaNaveExtra() {
navesJugador++; naveExtraGanada++; }
}

Llamamos a esta función en cada fotograma. El jugador gana una nave


adicional si la variable puntuacion/puntosParaNaveExtra
(los decimales se eliminaron) es mayor que el número de naves
ganadas. En nuestro juego, hemos ijado el valor de la variable
puntosParaNaveExtra en 10000. Cuando el juego comienza,la
variable naveExtraGanada es 0. Cuando la puntuación del
jugador es 10000 o más,puntuacion/puntosParaNaveExtra
será igual a 1, la cual es mayor que el valor de la variable
naveExtraGanada que es 0. Una nave adicional se da al jugador,
y el valor naveExtraGanada se incrementa en 1.

Aplicar Detección de Colisiones

Estaremos veriicando la caja alrededor de cada objeto cuando


hacemos nuestra detección de colisiones. Un cuadro delimitador es el
rectángulo más pequeño que abarcará las cuatro esquinas de un
objeto en la lógica del juego. Hemos creado una función para ello:

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);

if (derecha1 < izquierda2) return(false);


if (izquierda1 > derecha2) return(false);

return(true);
}

Podemos pasar dos objetos cualesquiera de los muchos que se


encuentran en la pantalla de juego a esta función como parámetros,
siempre y cuando cada uno contenga los atributos x, y, width y
height. Si los dos objetos se superponen, la función devuelve un
valor true. De lo contrario, devolverá false. La función
chequearColisión() para nuestro juego Defensa Espacial es
un poco complicada de entender a priori, debe examinarse con mucho
cuidado y detenimiento. Al inal de la sección escribiremos el código
completo de nuestra función, per antes, vamos a examinar algunos de
los conceptos básicos. Una cosa que usted notará es el uso de
“ etiquetas” al lado de las construcciones del bucle for. El uso de
etiquetas, como en la siguiente línea, puede ayudar a agilizar la
detección de colisiones:

piedras: for (var cuentaPiedras =


cantidadPiedras; cuentaPiedras >= 0;
cuentaPiedras--){

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:

1. Agregar un atributo Hit booleano a cada uno de los objetos y


conigurarlos como false cuando estos objetos son creados.
2. Pasar un bucle a través de las piedras y chequearlos contra los
otros objetos del juego. Esta vez la dirección (hacia atrás o hacia
adelante) a través del bucle, no es importante.
3. Antes de llamar a la función
colisionCuadroDelimitador(), debes estar seguro de que
cada atributo Hit de los objetos esta conigurado como false. Si no es
así, omite la comprobación de colisión.
4. Si los objetos colisionan, se debe conigurar cada atributo Hit de los
objetos en true. No hay necesidad de eliminar objetos de las
matrices esta vez.
5. Pasar un bucle a través de los misilesDelJugador y
comprobarlos contra los platillos, luego, un bucle a través de los
platillos para comprobarlos contra el jugador.
6. Cuando toda la rutina de detección de colisión esté completada.
Pasar nuevamente un bucle a través de cada objeto del juego (esta
vez hacia atrás) y eliminar todos los objetos con el atributo Hit
conigurado como true.

Hemos usado dos métodos (y sus variaciones) en cada uno. Mientras


que el segundo método es un poco más limpio, también es cierto, que
este último bucle a través de todos los objetos podría añadir una
sobrecarga al procesador, cuando se trata de un gran número de
objetos. Dejaremos la aplicación de este segundo método para usted
como un ejercicio, en caso de que quiera probarlos. A continución
mostraremos el código completo de la aplicación.

Archivo base HTML para el juego


nombre del archivo - defensaEspacial.html <!DOCTYPE html>
<html>

<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’>

Tu navegador no soporta canvas de HTML5


</canvas>
</div>
<script src=’defensaEspacial.js’></script>
</body>
</html>

nombre del archivo - defensaEspacial.js


window.addEventListener(‘load’,
eventWindowLoaded, false); function
eventWindowLoaded() {

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;

//campo de juego var xMin=0;


var xMax=400; var yMin=0;
var yMax=600; var
puntosPiedrasMedianas=75; var
puntosPiedrasPequenas=100; var
puntosPlatillos=300;

//valores de puntuación var


puntosPiedrasGrandes=50;
//constantes para la escala de las
piedras const ROCK_SCALE_LARGE=1;
const ROCK_SCALE_MEDIUM=2;
const ROCK_SCALE_SMALL=3;

//crear objetos del juego y las


matrices var jugador={};
var piedras=[];
var platillos=[];
var misilesDelJugador=[];
var particulas=[];
var misilesDelPlatillo=[];

var nivelAjusteVelocidadMaxPiedra=1;
var nivelPlatilloMax = 1;

var nivelPromedioAparicionPlatillo=25;
var nivelVelocidadPlatillo=1;
var nivelRetrasoDisparoPlatillo=300;
var nivelPromedioDisparoPlatillo=30;
var nivelVelocidadMisilPlatillo=1;

//teclas presionadas o pulsadas var


listaTeclasPres=[];

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);

function estadoNuevoNivel(){ piedras=


[];
platillos=[];
misilesDelJugador=[];
particulas=[];
misilesDelPlatillo=[];
nivel++;
nivelAjusteVelocidadMaxPiedra = nivel
* .25; if
(nivelAjusteVelocidadMaxPiedra > 3){

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;

//todas las nuevas piedras se inician


en la esquina superior izquierda
//para seguridad de la nave del
jugador
//ConsoleLog.log(“nuevaPiedra.x=” +
nuevaPiedra.x);

//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();

}else{ //esperar por click en la barra


de espacio if (listaTeclasPres[32] ==
true){

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;

if (jugador.x > xMax) {


jugador.x =- jugador.width;
}else if (jugador.x <- jugador.width){
jugador.x = xMax;
}

if (jugador.y > yMax) {


jugador.y=-jugador.height;
}else if (jugador.y <- jugador.height)
{ jugador.y = yMax;
}
}

function actualizarMisilesDelJugador()
{
var misilesJugadorTemp={};
var
cantMisilesJugador=misilesDelJugador.le
1;
ConsoleLog.log(“actualizar cantidad de
misiles =” + cantMisilesJugador); for
(var
contarMisilesJugador=cantMisilesJugador

contarMisilesJugador--){

ConsoleLog.log(“actualizar misiles del


jugador” + contarMisilesJugador);
misilesJugadorTemp=misilesDelJugador[co
misilesJugadorTemp.x+=misilesJugadorTem
misilesJugadorTemp.y+=misilesJugadorTem
if (misilesJugadorTemp.x > xMax) {
misilesJugadorTemp.x=-
misilesJugadorTemp.width;
}else if (misilesJugadorTemp.x<-
misilesJugadorTemp.width){
misilesJugadorTemp.x=xMax;
}

if (misilesJugadorTemp.y > yMax) {


misilesJugadorTemp.y=-
misilesJugadorTemp.height;
}else if (misilesJugadorTemp.y<-
misilesJugadorTemp.height){
misilesJugadorTemp.y=yMax;
}

misilesJugadorTemp.lifeCtr++;
if (misilesJugadorTemp.lifeCtr >
misilesJugadorTemp.life){
ConsoleLog.log(“removermisiles del
jugador”);
misilesDelJugador.splice(contarMisilesJ
misilesJugadorTemp=null;
}
}
}

function actualizarPiedras(){ for (var


contarPiedras=cantidadPiedras;contarPie
contarPiedras--){
piedrasTemp=piedras[contarPiedras];
piedrasTemp.x += piedrasTemp.dx;
piedrasTemp.y += piedrasTemp.dy;
piedrasTemp.rotacion+=piedrasTemp.rotac
ConsoleLog.log(“rock rotacionInc=”+
piedrasTemp.rotacionInc)
ConsoleLog.log(“rock rotacion=”+
piedrasTemp.rotacion) if
(piedrasTemp.x > xMax) {

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

}else{ //izquierda a derecha


nuevoPlatillo.x=-50;
nuevoPlatillo.dx=nivelVelocidadPlatillo

nuevoPlatillo.velocidadMisil=
nivelVelocidadMisilPlatillo;
nuevoPlatillo.retrasoDisparo=
nivelRetrasoDisparoPlatillo;
nuevoPlatillo.disparosPromedios=
nivelPromedioDisparoPlatillo;
platillos.push(nuevoPlatillo); }
}

var platilloTemp = {};


var cantidadPlatillos =
platillos.length - 1;
ConsoleLog.log(“actualizar cantidad de
platillos=” +

cantidadPlatillos);
for (var
contarPlatillos=cantidadPlatillos;conta
contarPlatillos--){
platilloTemp =
platillos[contarPlatillos];

//debe disparar el platillo


platilloTemp.cuentaDisparosRetrasos++;

platilloTemp.disparosPromedios &&
platilloTemp.cuentaDisparosRetrasos >
platilloTemp.retrasoDisparo ){
misilDisparadoPorPlatillo(platilloTemp)
platilloTemp.cuentaDisparosRetrasos=
0;

}
var remover = false;
platilloTemp.x += platilloTemp.dx;
platilloTemp.y += platilloTemp.dy;

//remover los platillos en los


extremos izquierdo y derecho if
(platilloTemp.dx > 0 && platilloTemp.x
> xMax){ remover=true;
}else if (platilloTemp.dx < 0 &&
platilloTemp.x < xMin
platilloTemp.width){
remover=true;
}

//recuperar platillos fuera de los


bordes verticales if (platilloTemp.y >
yMax || platilloTemp.y < yMin
platilloTemp.width) {
platilloTemp.dy *= -1; }

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--){

ConsoleLog.log(“actualizar misiles del


platillo” + contarMisilesDelPlatillo);
misilesDelPlatilloTemp=
misilesDelPlatillo[contarMisilesDelPlat
misilesDelPlatilloTemp.x +=
misilesDelPlatilloTemp.dx;
misilesDelPlatilloTemp.y +=
misilesDelPlatilloTemp.dy;
if (misilesDelPlatilloTemp.x > xMax) {
misilesDelPlatilloTemp.x=
misilesDelPlatilloTemp.width;
}else if (misilesDelPlatilloTemp.x <
misilesDelPlatilloTemp.width){
misilesDelPlatilloTemp.x = xMax;
}

if (misilesDelPlatilloTemp.y > yMax) {


misilesDelPlatilloTemp.y =
misilesDelPlatilloTemp.height;
}else if (misilesDelPlatilloTemp.y <
misilesDelPlatilloTemp.height){
misilesDelPlatilloTemp.y = yMax;

}
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;

} else if ((particulasTemp.x > xMax)


|| (particulasTemp.x < xMin) ||
(particulasTemp.y > yMax) ||
(particulasTemp.y < yMin)){

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

//dibujar la nave espacial estatica


(jugador) context.strokeStyle = ‘red’;
context.moveTo(0,0);
context.lineTo(-10,-15);
context.moveTo(-10,-15);

context.beginPath();
context.moveTo(-10,-15);
context.lineTo(15,0);
context.lineTo(-10,15);
context.lineTo(0,0);

if (jugador.avance == true && scale ==


1) {
//comprobar escala == 1 para saber que
la nave no esta en avance
context.moveTo(-4,-2);
context.lineTo(-4,1);
context.moveTo(-5,-1);
context.lineTo(-10,-1);
context.moveTo(-5,0);
context.lineTo(-10,0);
}
context.stroke();

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

//trasladar el canvas original al


centro del jugador
context.translate(misilesJugadorTemp.x+
misilesJugadorTemp.y+1);
context.strokeStyle = ‘cyan’;
context.beginPath();

//dibujar compensado por medio de


todo.Zero relativo 1/2 es de 15
context.moveTo(-1,-1);
context.lineTo(1,-1);
context.lineTo(1,1);
context.lineTo(-1,1);
context.lineTo(-1,-1);

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

//trasladar el canvas original al


centro del jugador
context.translate(piedrasTemp.x +
piedrasTemp.widthMedio, piedrasTemp.y
+ piedrasTemp.heightMedio);

ConsoleLog.log(“renderizar piedra x”+


(piedrasTemp.x+
piedrasTemp.widthMedio));
ConsoleLog.log(“renderizar piedra y”+
(piedrasTemp.y+
piedrasTemp.heightMedio));
context.rotate(angulosEnRadianes);
context.strokeStyle= ‘brown’;

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.save(); //salvar el estado


actual en la pila
context.setTransform(1,0,0,1,0,0); //
resetear la identidad
//trasladar el canvas original al
centro del jugador
//context.translate(this.x+widthMedio,t
context.translate(platilloTemp.x,
platilloTemp.y); context.strokeStyle =
‘green’;
context.beginPath(); //no se movió
hasta el centro, ya que se dibuja en
el espacio exacto

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

//trasladar el canvas original al


centro del jugador
context.translate(misilesDelPlatilloTem
+ 1, misilesDelPlatilloTemp.y+ 1);
context.strokeStyle= ‘green’;

context.beginPath();
context.lineTo(-1,1);
context.lineTo(-1,-1);

//dibujar compensado por medio de


todo. Zero relativa media es de 15
context.moveTo(-1,-1);
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

//translate the canvas origin to the


center of the player
context.translate(particulasTemp.x,
particulasTemp.y);
context.strokeStyle= ‘orange’;

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];

piedras: for (var cuentaPiedras =


cantidadPiedras; cuentaPiedras >= 0;
cuentaPiedras--){
misiles:for (var cuentaMisilesJugador
= cantidadMisilesJugador;
cuentaMisilesJugador >= 0;
cuentaMisilesJugador--){
misilesJugadorTemp =
misilesDelJugador[cuentaMisilesJugador]

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; }
}

platillos:for (var cuentaPlatillos =


cantidadPlatillos; cuentaPlatillos >=
0; cuentaPlatillos--){
platillosTemp=platillos[cuentaPlatillos

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; }
}

//jugador contra platillo


if
(colisionCuadroDelimitador(platillosTem
jugador)){
ConsoleLog.log(“colisionjugador”);
crearExplosion(platillosTemp.x + 16,
platillosTemp.y + 16, 10);
agregarAPuntuacion(platillosTemp.valorP

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) {

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);

if (derecha1 < izquierda2)


return(false);
if (izquierda1 > derecha2)
return(false);

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;
};

//**** application loop


contadorDeFotogramas = new
ContadorDeFotogramas(); const
PROM_FOTOGRAMA = 45;
var intervaloTiempo =
1000/PROM_FOTOGRAMA; temporizador();

function temporizador(){
iniciarJuego();
window.setTimeout(temporizador,
intervaloTiempo);
}
}
//***** objectos prototype *****

//*** objeto consoleLog


//llamamos al constructor de la clase
function ConsoleLog(){

}
console_log=function(message) {

console.log(message);
}
}
//agregar la función clase/estatica
para la clase por asignación
ConsoleLog.log=console_log;

//*** objecto prototype


contadorDeFotogramas
function ContadorDeFotogramas() {
this.ultimoContadorDeFotogramas = 0;
var tiempo = new Date();
this.ultimoFotograma =
tiempo.getTime();
delete tiempo;
this.contadorF = 0;
}
ContadorDeFotogramas.prototype.cuentaFo
{
var tiempo = new Date();
this.contadorF++;
if (tiempo.getTime()
>=this.ultimoFotograma+1000) {
ConsoleLog.log(‘evento fotograma’);
this.ultimoContadorDeFotogramas =
this.contadorF; this.ultimoFotograma =
tiempo.getTime(); this.contadorF = 0;
}
delete tiempo;
}

El resultado se mostrará mas o menos de esta manera:


imagen 3.5
- Vista del juego Defensa Espacial
Ayuda en linea
En este capítulo listaremos una serie de aplicaciones canvas y otras
tantas herramientas útiles para desarrolladores de canvas, que bien
podrían ayudar a adquirir ideas para crear grandes aplicaciones
canvas. El objetivo de este capítulo no es otro mas que ayudar a
desarrollar el conocimiento adquirido y la habilidad para usarlo,
juntamente con su creatividad, estamos seguros que en el futuro, serán
referentes para otros autores e inspiración para muchos que se inician
en este fabuloso mundillo. HTML5 Canvas se esta convirtiendo en una
parte muy importante de la world wide web y esto se debe en gran
medida al apoyo de un creciente grupo a nivel mundial de
desarrolladores.

El número de interesantes y entretenidas aplicaciones canvas crece


continuamente todos los días. Los desarrolladores están conectando el
canvas de HTML5 con toda la actividad en la web, experimentando con
las nuevas técnicas y mezclando el arte con la ciencia.

A continuación listaremos varios de los mejores sitios encontrados en la


web, incluyendo a algunos que son a su vez colección de aplicaciones
canvas y lugares para explorar rápidamente nuevas aplicaciones.

Bomomo(www.bomomo.com)

Descripción Es una aplicación que combina el arte controlado con el


movimiento aleatorio de pinceles de múltiples facetas. Es como si usted
tiene procesadores de pequeños canvas en sus manos. Y realmente es
muy divertido y entretenido. Pruebelo usted mismo!.
imagen 4.1 - aplicación canvas bomomo

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

experimentos para desarrolladores que utilizan el canvas de HTML5 y


las tecnologías relacionadas. Para encontrar experimentos de canvas,
buscar en el sitio de “ canvas” . Tal vez habían incluyen una de sus
obras maestras.
imagen 4.3 - sitio web patrocinado por google chrome

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

Burn Canvas (http://guciek.github.com/burn_canvas.html)

Descripción Se trata de un proyecto de demostración de código abierto


de la modiicación basada en píxeles de un canvas.
imagen 4.5- aplicación de código abierto que muestra la
modiicación de pixeles

Canvas Sketch (www.gartic.uol.com.br/sketch/)

Descripción Esta aplicación muestra cómo se pueden implementar


programas de dibujo tradicionales usando HTML5 Canvas
imagen 4.6- aplicación dedibujo patrocinado por google chrome

Canvas 3D Engine (http://peterned.home.xs4all.nl/3d/)

Descripción Este sitio muestra cómo el contexto Canvas 2D puede


simular ambientes 3D sin el uso de WebGL
imagen 4.7- simulación 3D en un contexto 2D

Canvas Raytracer (http://jupiter909.com/mark/jsrt.html)

Este sitio muestra cómo se puede dibujar supericies soisticadas y


relexiones sobre Descripción
un canvas sin cargas computacionales WebGL o alta. Trazado de
rayos es una técnica para la creación de imágenes relejadas en las
supericies virtuales.
imagen 4.8 - dibujar supericies y relejos en 3D bajo un contexto
2D

Pocket Full of
Canvas(www.nihilogic.dk/labs/pocket_full_of_canvas/#pres

Descripción Esta aplicación muestra una serie de efectos del canvas


junto con el código que las genera. Es una gran demostración y
también es útil para su kit de herramientas de desarrollador.
imagen 4.9 - Excelente herramienta para desarrolladores
En este sección, enumeraremos algunas herramientas que pueden
mejorar su vida como un desarrollador de aplicaciones Canvas.

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)

Descripción Este sitio web proporciona el estado más reciente de la


compatibilidad con exploradores para una amplia variedad de
características y funciones, incluyendo Canvas HTML5

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/)

Descripción Firebug es una herramienta de sotware libre para la


depuración de código JavaScript y HTML. Proporciona la capacidad
para establecer puntos de interrupción, comprueba los valores de
variables, y seguir la ejecución de código. Usted puede incluirlo como
un add-on para la mayoría de los navegadores, incluyendo Firefox,
Chrome, Internet Explorer y Opera.

Gamepad API(https://wiki.mozilla.org/GamepadAPI)

Descripción El API Gamepad es una especiicación para el uso de


dispositivos de juego a través de código JavaScript Canvas

HTML5 Test(http://html5test.com)

Descripción La página web html5test prueba los navegadores de


escritorio y móviles para el estado de su compatibilidad con las
características de HTML5, como el canvas. Este proratea las
características individuales y proporciona una puntuación para el
navegador en todo su conjunto.

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.

Miro Video Converter(www.mirovideoconverter.com)

Descripción Como es en el caso de audio, hasta que todos los


navegadores sean compatibles con todo tipo de archivos de vídeo, es
necesario incluir varias versiones de sus archivos de vídeo en sus
aplicaciones. Miro Video Converter es una herramienta de sotware
libre, de código abierto multiplataforma para convertir un archivo de
vídeo en múltiples formatos, incluyendo MP4, OGG y WebM.

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

Versiones transitorias de HTML y XHTML apoyan coniguraciones de


color para el texto y el color de fondo de los documentos, los bordes de
marcos, tablas o celdas de la tabla, incluso valores individualizados.
Hay 16 nombre de colores conocidos ampliamente y deinidos por
HTML. Estos nombres y sus valores hexadecimales asociado RGB se
muestran en la tabla A1.

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

Nombre de los colores no standard y equivalencias


númericas

La tabla A2 muestra un conjunto de nombres de colores no standard


comunmente soportado por los mayores navegadores. Estos nombres
de colores fueron inicialmente introducidos por netscape y
aparentemente son los colores deinidos por el X11 windowing system
para los sistemas UNIX hace aproximadamente dos décadas.
Recordando su origen, estos colores son documentados en la
especiicación SVG y en la emergente especiicación CSS3; todos ellos
están siendo muy usados y pueden que algún día lleguen a ser
considerados estandar como los otros.
Puede haber algunas situaciones, como en el caso del navegador
Opera, que algunos de los grandes navegadores no soporten estos
valores. Las pruebas actuales muestran que al momento de escribir
este libro, la mayoría de los grandes navegadores sí soportan estos
valores, puede probarlos usted mismo en la página web
http://htmlref.com/AppC/colorchart html. Si existe alguna duda de la
compatibilidad de estos valores, los autores de aplicaciones canvas o
web, deben usar valores hexadecimales en lugar de los nombres de
los colores.
Algunas referencias de color airman que otras variaciones de color se
pueden introducir mediante la adición de los números 1 al 4 para los
nombres de colores. Si esto fuera correcto, cadetblue1, cadetblue2,
cadetblue3 y cadetblue4, se mostrarían como diferentes tonos de un
mismo color. Por lo que particularmente pienso que no es adecuado ni
correcto.

Color Name Hex Equivalent


Black #000000
Silver #C0C0C0
Gray #808080
White #FFFFFF
Maroon #800000
Red #FF0000
Purple #800080
Fuchsia #FF00FF
Green #008000
Lime #00FF00
Olive #808000
Yellow #FFFF00
Navy #000080
Blue #0000FF
Teal #008080
Aqua #00FFFF

tabla A!
- Nombre de colores y sus equivalencias hexadecimales
estandard HTML 4.0

aliceblue #F0F8FF 240,248,255 antiquewhite #FAEBD7 250,235,215


aqua #00FFFF 0,255,255 aquamarine #7FFFD4 127,255,212 azure
#F0FFFF 240,255,255 beige #F5F5DC 245,245,220 bisque #FFE4C4
255,228,196 black #000000 0,0,0 blanchedalmond #FFEBCD
255,235,205 blue #0000FF 0,0,255 blueviolet #8A2BE2 138,43,226
brown #A52A2A 165,42,42 burlywood #DEB887 222,184,135
cadetblue #5F9EA0 95,158,160 chartreuse #7FFF00 127,255,0
chocolate #D2691E 210,105,30 coral #FF7F50 255,127,80
cornlowerblue #6495ED 100,149,237 cornsilk #FFF8DC 255,248,220
crimson #DC143C 220,20,60 cyan #00FFFF 0,255,255 darkblue
#00008B 0,0,139 darkcyan #008B8B 0,139,139 darkgoldenrod
#B8860B 184,134,11 darkgray #A9A9A9 169,169,169 darkgreen
#006400 0,100,0 darkkhaki #BDB76B 189,183,107 darkmagenta
#8B008B 139,0,139 darkolivegreen #556B2F 85,107,47 darkorange
#FF8C00 255,140,0 darkorchid #9932CC 153,50,204 darkred
#8B0000 139,0,0 darksalmon #E9967A 233,150,122 darkseagreen
#8FBC8F 143,188,143 darkslateblue #483D8B 72,61,139
darkslategray #2F4F4F 47,79,79 darkturquoise #00CED1 0,206,209
darkviolet #9400D3 148,0,211 deeppink #FF1493 255,20,147
deepskyblue #00BFFF 0,191,255 dimgray #696969 105,105,105
dodgerblue #1E90FF 30,144,255 irebrick #B22222 178,34,34
loralwhite #FFFAF0 255,250,240 forestgreen #228B22 34,139,34
fuchsia #FF00FF 255,0,255 gainsboro #DCDCDC 220,220,220
ghostwhite #F8F8FF 248,248,255 gold #FFD700 255,215,0 goldenrod
#DAA520 218,165,32 gray #808080 127,127,127 green #008000
0,128,0 greenyellow #ADFF2F 173,255,47 honeydew #F0FFF0
240,255,240 hotpink #FF69B4 255,105,180 indianred #CD5C5C
205,92,92 indigo #4B0082 75,0,130 ivory #FFFFF0 255,255,240 khaki
#F0E68C 240,230,140 lavender #E6E6FA 230,230,250 lavenderblush
#FFF0F5 255,240,245 lawngreen #7CFC00 124,252,0 lemonchifon
#FFFACD 255,250,205 lightblue #ADD8E6 173,216,230 lightcoral
#F08080 240,128,128 lightcyan #E0FFFF 224,255,255
lightgoldenrodyellow #FAFAD2 250,250,210 lightgreen #90EE90
144,238,144 lightgrey #D3D3D3 211,211,211 lightpink #FFB6C1
255,182,193 lightsalmon #FFA07A 255,160,122 lightseagreen
#20B2AA 32,178,170 lightskyblue #87CEFA 135,206,250 lightslategray
#778899 119,136,153 lightsteelblue #B0C4DE 176,196,222 lightyellow
#FFFFE0 255,255,224 lime #00FF00 0,255,0 limegreen #32CD32
50,205,50 linen #FAF0E6 250,240,230 magenta #FF00FF 255,0,255
maroon #800000 128,0,0 mediumaquamarine #66CDAA 102,205,170
mediumblue #0000CD 0,0,205 mediumorchid #BA55D3 186,85,211
mediumpurple #9370DB 147,112,219 mediumseagreen #3CB371
60,179,113 mediumslateblue #7B68EE 123,104,238
mediumspringgreen #00FA9A 0,250,154 mediumturquoise #48D1CC
72,209,204 mediumvioletred #C71585 199,21,133 midnightblue
#191970 25,25,112 mintcream #F5FFFA 245,255,250 mistyrose
#FFE4E1 255,228,225 moccasin #FFE4B5 255,228,181 navajowhite
#FFDEAD 255,222,173 navy #000080 0,0,128 navyblue #9FAFDF
159,175,223 oldlace #FDF5E6 253,245,230 olive #808000 128,128,0
olivedrab #6B8E23 107,142,35 orange #FFA500 255,165,0 orangered
#FF4500 255,69,0 orchid #DA70D6 218,112,214 palegoldenrod
#EEE8AA 238,232,170 palegreen #98FB98 152,251,152 paleturquoise
#AFEEEE 175,238,238 palevioletred #DB7093 219,112,147
papayawhip #FFEFD5 255,239,213 peachpuf #FFDAB9 255,218,185
peru #CD853F 205,133, 63 pink #FFC0CB 255,192,203 plum
#DDA0DD 221,160,221 powderblue #B0E0E6 176,224,230 purple
#800080 128,0,128 red #FF0000 255,0,0 rosybrown #BC8F8F
188,143,143 royalblue #4169E1 65,105,225 saddlebrown #8B4513
139,69,19 salmon #FA8072 250,128,114 sandybrown #F4A460
244,164,96 seagreen #2E8B57 46,139,87 seashell #FFF5EE
255,245,238 sienna #A0522D 160,82,45 silver #C0C0C0 192,192,192
skyblue #87CEEB 135,206,235 slateblue #6A5ACD 106,90,205
slategray #708090 112,128,144 snow #FFFAFA 255,250,250
springgreen #00FF7F 0,255,127 steelblue #4682B4 70,130,180 tan
#D2B48C 210,180,140 teal #008080 0,128,128 thistle #D8BFD8
216,191,216 tomato #FF6347 255,99,71 turquoise #40E0D0
64,224,208 violet #EE82EE 238,130,238 wheat #F5DEB3 245,222,179
white #FFFFFF 255,255,255 whitesmoke #F5F5F5 245,245,245 yellow
#FFFF00 255,255,0 yellowgreen #9ACD32 139,205,50
Desafortunadamente, las pruebas revelan que este esquema de
variación de color no funciona en otros navegadores principales.
En general, los autores de aplicaciones canvas y web deben tener
cuidado al usar nombres de colores no estándar. En algunos casos, los
nombres desconocidos se interpretan como valores, y otros casos, el
navegador simplemente ajustar el color negro.
Teniendo en cuenta todas las posibilidades de error, los autores de
aplicaciones canvas y web deben pensar dos veces antes de emplear
los nombres de colores especiales, aunque los de la Tabla A2 son
seguros en la práctica.

Valores de colores CSS

Teniendo en cuenta todas las posibilidades de error, los autores de


aplicaciones canvas y web deben pensar dos veces antes de emplear
los nombres de colores especiales, aunque los de la Tabla A2 son
seguros en la práctica.

tabla A3- Valores para los colores CSS


Nota destacada!
Las pruebas revelan que en función de los cambios de color de los
sistemas operativos, algunos navegadores no pueden asignar
correctamente estos nombres de color en la interfaz de usuario y con
frecuencia lo hacen por Atento defecto en negro.

Propiedades CSS relacionadas con el color


Son numerosas las propiedades CSS que permiten valores de color. La
tabla A5 muestra en detalle una lista de estas propiedades, un breve
ejemplo de su uso, así como una indicación de cual fué la versión CSS
donde por primera vez apareció esta propiedad. Los lectores pueden
buscar mas información acerca de estas propiedades en textos
especializados, ya que no es el objetivo de este libro, aunque creemos
que lo relativo al color es parte importante en el desarrollo de
aplicaciones canvas.

tabla A4 - Nombre de colores CSS2 para la interface de


usuario(UI) Propiedad Ejemplo Version CSS tabla A5 - Valores de
colores soportados por propiedad CSS

Colores seguros para los navegadores

Al principio de la época, todos los ordenadores contaban con una


gama de 256 colores para todos los navegadores y sistemas
operativos, una paleta especial de sólo 216 colores que eran
“ seguros” se deinió. Este grupo de colores seguros para la Web es a
menudo llamada la paleta de colores para una navegación segura. En
teoría, el uso de otros colores más allá de este conjunto seguro podía
dar lugar a cambios de colores, sobre todo en condiciones de colores
limitados como VGA, que apoya a los colores de 8 bits, proporcionando
una miseria de 256 colores. La realidad de hoy es que la paleta
segura para la Web es más una cuestión histórica que preocupante,
sobre todo teniendo en cuenta cómo algunos dispositivos están
limitadas a una paleta de 8 bits. Sin embargo, muchas herramientas y
muchos diseñadores continúan promoviendo el uso de esta gama de
colores, así que lo presentan y su diseño está completo.
La selección de los 216 colores seguros es bastante comprensible si
se tiene en cuenta la naturaleza aditiva de los colores RGB. Considere
la posibilidad de un color que se compone de diferentes cantidades de
rojo, verde o azul que se podrían establecer mediante el ajuste de una
línea de color imaginario de los extremos de algún color de máxima
saturación de color. Los colores seguros utilizan seis posibles ajustes
de intensidad para cada valor de rojo, verde o azul. Los ajustes son
0%, 20%, 40%, 60%, 80%, y 100%. Un valor de 0%, 0%, 0% en el dial
de color imaginario es equivalente a negro. Un valor de 100%, 100%,
100% indica blanco puro, mientras que un valor de 100%, 0%, 0% es
de color rojo puro, y así sucesivamente. Los colores seguros son los
que tienen un valor RGB sólo en uno de los ajustes de intensidad
fuerte. Las conversiones hexadecimales para la saturación se
muestran en la Tabla A6.
La coniguración de un color seguro es simplemente una cuestión de
seleccionar una combinación de valores hexadecimales seguros. En
este caso, # 9966FF es un color hexagonal segura; # 9370DB no lo es.
La mayoría de las herramientas de edición web como Adobe
Dreamweaver y Microsot Expression contienen selectores de color de
seguridad; al igual que las herramientas de imágenes como Adobe
Photoshop. Sin embargo, la asignación directa de un color “ inseguros”
a su color seguro más cercano es bastante fácil-a la vuelta de cada red
en particular, verde, azul o el valor hacia arriba o hacia abajo al valor
seguro más cercano. Una conversión completa de hexadecimal a los
valores decimales se muestra en la Tabla A7, los valores de seguridad
se indican en negrita.
Intensidad de Color Valor Hex Valor Decimal tabla A6 - Tabla de
conversión para la intensidad de color
tabla A7
- Tabla de conversión de colores RGB a Hexadécimal
Apéndice B
Considerando que en la mayoría de los libros especializados en la
materia solo se nos da las instrucciones básicas o simples para crear
las aplicaciones que queremos, en este libro, hemos querido hacer un
esfuerzo adicional y extender una aplicación básica o muy simple, que
previamente hemos creado en el capitulo 3, a una de mayor
complejidad, con el sólo objetivo didáctico de mostrarles el mundo de
posibilidades dentro del campo de las aplicaciones web y en especial
las aplicaciones para juegos, en este caso, los de arcade.

Extendiendo nuestro juego Defensa Espacial

Previamente en el capitulo 3 creamos una aplicación para juego muy


básico, ahora en esta sección, vamos a dar una aproximación mas real
de lo que es crear aplicaciones de juegos, agregandole imagenes y
sonidos a nuestro juego. Mucha de la lógica de este juego es la misma,
pero al agregar imagenes para reemplazar a nuestros dibujos hechos
con los paths del canvas de HTML5 nos permitirá optimizar el
renderizado del juego. Optimizar el renderizado es muy importante
cuando el objetivo son los dispositivos de procesadores limitados,
como lo son los teléfonos móviles. También vamos a agregarles sonidos
y aplicaremos un grupo de objetos a las partículas usadas para las
explosiones del juego.

Trabajar con mosaicos

En el capítulo 3 dibujamos todos los objetos de nuestro juego con


trazados que luego transformamos sobre la marcha. Vamos a optimizar
el renderizado de nuestro juego básico. Haremos esto, mediante el
renderizado previo de todos los gráicos del juego y los transformamos
como imágenes. Luego usaremos estas imágenes en lugar de los
trazos y los transformamos de modo inmediato como lo habiamos hecho
anteriormente.

La siguiente imagen muestra una de las hojas de mosaicos que hemos


creado para este juego. Esta hoja contiene las 36 rotaciones que
usaremos en nuestra nave espacial. Estamos comprimiendo las
rotaciones en una hoja de mosaicos para evitar la carga excesiva en la
que muchas veces se ve expuesto los procesadores, transformandolos
en cada fotograma tal y como lo sacamos al canvas.

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.

imagen B.2 - Hoja de mosaicos nave2.png que representa la


nave acelerada

imagen B.2 - Hoja de mosaicos nave2.png que representa la


nave acelerada
imagen
B.2 - Hoja de mosaicos nave2.png que representa la nave
acelerada
El platillo enemigo que intenta destuir la nave del jugador esta
contenido en un solo mosaico.
imagen B.2
- Hoja de mosaicos nave2.png que representa la nave acelerada
Finalmente, el archivo partes png es una pequeña hoja de mosaicos
de 8x2, que contiene cuatro particulas de 2x2. Estos son usados para
la animar la explosión de los misiles del jugador y del platillo.

El primer cuadro es verde y será usado para las explosiones de las


piedras pequeñas y la explosión del platillo, el segundo cuadro es
azul claro y representa los misiles del jugador y la explosión del
jugador, el tercer cuadro es color rosa (o salmon si lo preiere), y este
ilustra la explosión de las piedras grandes. El último color purpura es
usado para la explosión de las piedras medianas.

Ahora que tenemos nuestros mosaicos en su lugar, vamos a revisar los


métodos que usaremos para tyransformar el trazo de modo inmediato
del juego renderizandolo para que sea un juego basado en imagenes.

Calcular la localización de los mosaicos


Dado que tenemos una hoja e mosaicos tal como el archivo nave.png,
podemos localizar el cuadro que queramos mostrar con un simple truco
matematico.

El archivo nave.png es una hoja de mosaicos de animación de 36


cuadros con la nave estática del jugador comenzando en una posición
de 0 grados o punto de dirección correcta. Cada uno de los restantes
35 mosaicos muestra la nave con una rotación de 10 grados de
incremento.

Si quisieramos mostrar el mosaico 19 (la nave apuntando a la izquierda


o en un angulo de 190 grados), primero tenemos que encontrar las
coordenadas x e y de la esquina superior izquierda del cuadro
calculando las variables origenX y origenY.

A continuación mostraremos el pseudocódigo para el cálculo de


origenX:
origenX = integer(indice_fotograma_actual
modulo numero_columnas_hoja_
mosaicos)*anchoMosaico
El operador modulo(%) devuelve l resto de una división. El
siguiente es el código real (con variables reemplazadas por valores
literales) que usaremos para este cálculo:
El resultado es x=9*32 = 288;
El calculo para el valos origenY es similar, excepto que dividimos en
lugar del operador modulo:
origenY = integer(indice_fotograma_actual
dividido por
numero_columnas_hoja_mosaicos)*altoMosaico
Aquí el código que usaremos para calcular esto:
El resultado es y= 1*32 = 32;
Por lo tanto la ubicacion de la esquina superior izquierda de
nave.png desde donde comenzaremos a copiar pixeles es 288,32.
Para copiar esto en el canvas usaremos la siguiente sentencia:
context.drawImage(mosaicoNave, origenX,
origenY, 32, 32, jugador.x, jugador.y, 32,
32);

En el capitulo 3, necesitabamos un montón de código para dibujar y


renderizar la nave del jugador a la rotación actual. Cuando se usa una
hoja de mosaico, este código se reduce considerablemente. A
continuación el código que usaremos para renderizar la nave del
jugador:

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;
}

La función renderizarNaveJugador() divide el atributo


jugador.rotacion entre 10 para determinar cual de los 36
mosaicos en la instancia imagen de la nave mostrará en el canvas. Si el
jugador está en modo avance, la imagen nave2 es usada como
instancia de la imagen de la nave.

Esto funciona porque le hemos puesto a la nave que gire 10 grados


cada vez que es presionada la tecla izquierda o derecha del teclado.
En la versión del capítulo 3 girabamos la nave cada 5 grados. Si
hubiéramos creado una hoja de mosaicos con 72 cuadros, con la nave
del jugador rotando 5 grados de incremento, podríamos haber
mantenido el atributo jugador.velocidadRotacion en 5.
Para este tutorial hemos hecho solo 36 cuadros para la nave del
jugador, por lo que usaremos un valor de 10 para la velocidad de
rotación. Desde luego que podríamos usar 72 o incluso 360 mosaicos
para los fotogramas de rotación de la nave del jugador. Esto está
limitado sólo por la creatividad (y paciencia con la herramienta de
dibujo). Vamos a ver la velocidadRotacion asignado
anteriormente a estadoNuevoJuego():

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);

Nuevos atributos para los jugadores

junto con el cambio en la velocidad de rotación, también hemos


modiicado los valores width y height, estos son ahora de 32
para ambos, que es la misma que el ancho y el alto del mosaico. Si nos
ijamos en el primer mosaico de la hoja de mosaicos naves.png, se
ve que la nave del jugador no ocupa el area completa del mosaico. Se
centra en el centro teniendo un cuadro de hasta 24 x 24, lo que deja
suiciente espacio alrededor de los bordes del cuadro para eliminar el
recorte cuando la nave gira. También vamos a usar este mismo
concepto cuando vayamos a crear la rotación de las piedras.

Los pixeles adicionales de relleno añadido para eliminar el recorte


durante la rotación del fotograma, plantean un pequeño problema
para la detección de colisiones. En la versión del capítulo 3 del juego,
se usó los valores width y height para la detección de colisiones en el
cuadro delimitador. No vamos a usar estos valores en esta nueva
versión, ya que hemos creado dos nuevas variables para usar en la
detección de colisiones: hitWidth y hitHeight. En lugar de
conigurar estos valores a 32, estos serán de 24. Este nuevo valor más
pequeño hace que nuestra detección de colisiones sea más preciso
que sí usamos todo el ancho y el alto del cuadro.

El nuevo algoritmo para la funcion colisionCuadroDelimitador()

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) {

var izquierda1 = objeto1.x;


var izquierda2 = objeto2.x;
var derecha1 = objeto1.x + objeto1.hitWidth;
var derecha2 = objeto2.x + objeto2.hitWidth;
var superior1 = objeto1.y;
var superior2 = objeto2.y;
var inferior1 = objeto1.y +
objeto1.hitHeight; var inferior2 = objeto2.y
+ objeto2.hitHeight;

if (inferior1 < superior2) return(false); if


(superior1 > inferior2) return(false);
if (derecha1 < izquierda2) return(false); if
(izquierda1 > derecha2) return(false);
return(true);
}

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.

Renderizar los otros objetos del juego

Las piedras, platillos, misiles y particulas están todos renderizados de


una manera similar al método implementado por la nave del jugador.
Primero, veámos el código de la función para renderizar los platillos.
Renderizar los platillos
Los platillos no tienen una hoja de mosaicos, pero, para ser
coherentes, lo renderizamos como hemos pensado hacerlo. Esto nos
permite añadir mas ichas de animación para el platillo despues:

function renderizarPlatillos() {
var platilloTemp = {};
var cantidadPlatillos = platillos.length-1;
for (var contarPlatillos =
cantidadPlatillos;contarPlatillos>=0;

contarPlatillos--){
//ConsoleLog.log(“platillos: “ +
contarPlatillos); platilloTemp =
platillos[contarPlatillos];

context.save(); //salvar estado actual en la


pila var origenX = 0;
var origenY = 0;
context.drawImage(mosaicoPlatillos, origenX,
origenY, 30,15,
platilloTemp.x,platilloTemp.y,30,15);

context.restore(); //traer el viejo estado a


la pantalla }
}

No hay necesidad de calcular los valores origenX y origenY para


el platillo porque solo es un cuadro en este caso, solo podemos
ajustarlo a 0. hemos ijado las medidas a platillos.width (30) y
platillos.height (15) como un ejemplo, pero con todo el resto
de los objetos del juego, usaremos los atributos width y height en
lugar de literales.

A continuación vamos a ver el renderizado de las piedras, que varía


ligeramente del platillo y de la nave del jugador.

Renderizar las piedras

Los mosaicos de las rocas están dentro de tres hojas de mosaicos


basadas en sus tamaños (grandes, medianas y pequeñas) y hemos
usado solo 5 cuadros para cada tamaño de piedra. Las piedras
cuadradas con un patrón simétrico, así que solo tenemos que crear
previamente un solo cuadro de rotación para cada uno de los tres
tamaños. A continuación la función renderizarPiedras(). Note
que debemos cambiar la escala de las piedras (1 = grandes, 2 =
medianas, 3 = pequeñas) para seleccionar la hoja de mosaicos
correctas para el renderizado.

function renderizarPiedras() {
var piedrasTemp = {};
var cantPiedras = piedras.length-1;

for (var contarPiedras =


cantPiedras;contarPiedras>=0;contarPiedras--)
{
context.save(); //salvar el estado actual en
la pila piedrasTemp = piedras[contarPiedras];

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
} }

En la función renderizarPiedras(), ya no estamos usando el


atributo piedras rotacion como el angulo de giro, así como lo hicimos en
el desarrollo del juego del capítulo 3, en cambio hemos reutilizado el
atributo rotacion para representar el id de los mosaicos (0-4) en las
hojas de mosaicos para renderizar.
En la versión del capitulo 3, podíamos simular velocidades más rapidas
o mas lentas para las rotaciones de las piedras, simplemente dando a
cada piedra un valor al azar para el atributo rotationInc. Este valor,
negativo para la dirección contraria a las agujas del reloj o positivas
para ir a favor de las agujas del reloj, era agregado al atributo rotacion
en cada fotograma. En esta nueva versión basada en hojas de
mosaicos, solo tenemos cinco fotogramas de animación, por lo que no
queremos saltar cuadros porque se vería muy agitado. En su lugar
vamos a añadir dos nuevos atributos a cada piedra:
cuentaAnimacion y retrasoAnimacion.

El atributo retrasoAnimacion representa el número de


fotogramas entre cada cambio de mosaico para la piedra dada. La
variable cuentaAnimacion se reinicia a 0 despues de cada
cambio de mosaico y aumenta en 1 en cada fotograma siguiente.
Cuando cuentaAnimacion es mayor que retrasoAnimacion, el valor de
piedra.rotacion se incrementa (en el sentido de las agujas del
reloj) o disminuye (en el sentido contrario de las agujas del reloj). A
continuación el nuevo código en nuestra función
actualizarPiedras().

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; }
}

Observe que hemos tenido que incluir en el código los valores


maximos y minimos para los ID de los mosaicos, 4 y 0 . Podríamos haber
usado fácilmente una constante o dos variables para este propósito.

Renderizar los misiles

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.

Vamos a revisar rápidamente estas dos funciones:

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

context.restore(); //recuperar el antiguo


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 “ + //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

context.restore(); //restaurar el viejo


estado en la pantalla
} }

La explosion de las partículas también es renderizada usando las


imagenes de las hojas de mosaico, y su código es muy similar al código
de los misiles. A continuación examinaremos las partículas.

Renderizando las partículas

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.

La nueva función crearExplosion() manejará un nuevo


parámetro: tipo, que será agregado a la lista de sus parámetros.
Vamos a revisar este código:
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.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);

}
}
}

Como los objetos particulas son creados en la función


crearExplosion(), agregamos tipo como un nuevo atributo.
Cuando una nueva explosión es activada en la función
chequearColision(), la llamada a crearExplosion()
ahora incluira el valor tipo basado en el objeto que será destruido.
Cada piedra alrededor tiene un parametro scale que varia desde los
valores 1 hasta 3 basados en el tamaño. Vamos a usar estos mismos
valores para para el nuevo atributo tipo. Ahora solo necesitamos los
valores tipo para el jugador y para el platillo. Dado que es una buena
idea, tener valores continuos, para un mejor control de este atributo,
usaremos para el platillo un valor de 0 y para el jugador, usaremos un
valor de 4. Deinidos ya, todos estos valores para este nuevo atributo, el
desglose del atributo tipo sería como sigue a continuación:

platillos: tipo = 0
piedras grandes: tipo = 1 piedras medianas: tipo = 2 piedras
pequeñas: tipo = 3 jugador: tipo = 4

Estos valores tipo, vamos a necesitar usarlo en una sentencia switch


dentro de la función renderizarParticulas() para
determinar cual de los cuatro cuadros dentro de nuestra hoja de
mosaicos renderiza la partícula dad. Vamos a examinar como quedaría
esta función despues de agregarles los cambios mencionados
anteriormente:

function renderizarParticulas() {

var particulasTemp = {};


var cantidadParticulas = particulas.length-1;
for (var
contarParticulas=cantidadParticulas;contarPar

;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

context.restore(); //restaurar el viejo


estado en la pantalla
} }

En chequearColisiones(), necesitamos pasar el nuevo


parámetro tipo a la función crearExplosion() para que
pueda ser asignadas a las particulas en la explosión. A continuación
un ejemplo del llamado a la funcion crearExplosion() en la
instancia del objeto piedra:

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

Los juegos de arcade necesitan reproducir muchos sonido


simultáneamente, y en algunos casos, esos sonidos se reproducen
rapidamente en sucesión.
Los sonidos para nuestro juego

Nosotros agregaremos tres sonidos para el desarrollo de nuestro


juego:
a) Un sonido para cuando el jugador dispare un misil
(shoot1.mp3, .ogg, .wav) b) Un sonido para explosiones
(explode1.mp3, .ogg, .wav)
c) Un sonido para cuando el platillo dispare un misil
(saucershoot.mp3, .ogg, .wav)

En los archivos descargables de este libro, hemos agregado cada uno


de los archivos de los tres sonidos en los tres formatos: .wav,
.ogg, .mp3.

Agregando instancias de sonido y administrando las variables para el


juego
En la sección deinición de variables de nuestro código, creamos las
variables para administrar el sonido. crearemostres instancias de cada
sonido que ira dentro de nuestro grupo:

var sonidoExplosion;
var sonidoExplosion2;
var sonidoExplosion3;
var sonidoDisparo;
var sonidoDisparo2;
var sonidoDisparo3;
var sonidoDisparoPlatillo; var
sonidoDisparoPlatillo2; var
sonidoDisparoPlatillo3;

También necesitamos crear una matriz para guardar nuestro grupo de


sonidos:
var grupoSonido = new Array();

Para controlar el sonido que queremos reproducir, asignamos una


cadena constante para cada uno, y para reproducir el sonido, solo
necesitamos usar la constante. De esta forma, podemos cambiar el
nombre del sonido fácilmente, la cual ayudará en la refactorización del
código si queremos modiicar los sonidos mas adelante.

var SONIDO_EXPLOSION = “explode1”;


var SONIDO_DISPARO = “shoot1”;
var SONIDO_DISPARO_PLATILLO = “saucershoot”;

Finalmente, necesitaremos una variable tipoDeAudio, la cual usaremos


para referenciar el tipo de archivo de sonido actual (.ogg, .wav,
.mp3) para el código que administra el sonido.
var tipoDeAudio;

Carga de nuestros sonidos y de nuestras hojas de mosaicos

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);

mosaicoNave = new Image();


mosaicoNave.src = “ship_tiles.png”;
mosaicoNave.onload = elementoCargado;

mosaicoNave2 = new Image();


mosaicoNave2.src = “ship_tiles2.png”;
mosaicoNave2.onload = elementoCargado;

mosaicoPlatillos= new Image();


mosaicoPlatillos.src = “saucer.png”;
mosaicoPlatillos.onload = elementoCargado;
mosaicoPiedrasGrandes = new Image();
mosaicoPiedrasGrandes.src = “largerocks.png”;
mosaicoPiedrasGrandes.onload =
elementoCargado;

mosaicoPiedrasMedianas = new Image();


mosaicoPiedrasMedianas.src =
“mediumrocks.png”;
mosaicoPiedrasMedianas.onload =
elementoCargado;

mosaicoPiedrasPeque = new Image();


mosaicoPiedrasPeque.src = “smallrocks.png”;
mosaicoPiedrasPeque.onload = elementoCargado;

mosaicoParticulas = new Image();


mosaicoParticulas.src = “parts.png”;
mosaicoParticulas.onload = elementoCargado;

cambioEstadoApp(ESTADO_ESPERA_CARGUE_JUEGO);
}

Note que debemos crear y precargar tres instancias de cada sonido, a


pesar de que comparten el mismo archivo de sonido (o archivos). En
esta función también cargaremos nuestras hojas de mosaicos. La
aplicación chequea el alcance de los elementos a cargar a traves de la
variable elementosACargar contra el alcance de elementos
cargados a traves de la variable cantElemCarga, en los eventos
de carga llamados a traves de la función elementoCargado(),
que es compartida por todos los elementos que han de cargarse. Esto
hace que sea fácil para que la aplicación cambie de estado y se pueda
comenzar a jugar el juego siempre y cuando todos los elementos se
hayan cargado satisfactoriamente. Vamos a examinar brevemente la
función elementoCargado() ahora:

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);
} }

En esta función primero removemos todos los listener de los eventos de


cada uno de los elementos cargados y luego agregamos el sonido a
nuestro grupo de sonidos. Finalmente podemos llamar a la función
cambioEstadoApp() para que envie la pantalla con los titulos y
créditos del juego.

Reproducir sonidos

Para reproducir el sonido que necesitamos en nuestro juego,


usaremos la función reproducirSonido(). Esta función es
llamada en varias instancias de nuestro código para que reproduzca
exactamente el sonido que necesitamos. Por ejemplo, la función
crearExplosion() presentada con anterioridad en esta sección
incluye esta linea:

reproducirSonido(SONIDO_EXPLOSION,.5);

Cuando queremos reproducir la instancia de un sonido desde nuestro


grupo, llamamos a la función reproducirSonido() y
pasamos las constantes que representan el sonido y el volumen del
sonido. Si una instancia del sonido esta disponible en el grupo, esta es
usada, y el sonido se reproducirá. Vamos a revisar como esta
constituida esta función:

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();

} else if (grupoSonido.length < VOLUMEN_MAX){


ConsoleLog.log(“sonido no encontrado”);
sonidoTemp = document.createElement(“audio”);
sonidoTemp.setAttribute(“src”, sonido + “.” +
tipoDeAudio); sonidoTemp.volume = volume;
sonidoTemp.play();
grupoSonido.push({nombre:sonido,
elemento:sonidoTemp,

tipo:tipoDeAudio,juega:true});
}
}
Ahora vamos a movernos dentro de otro tipo de contenedor de
aplicación, el objeto contenedor.

Instancias del objeto contenedor

Hemos visto como los objetos contenedores se relacionan con los


sonidos, pero no hemos aplicado este concepto a los objetos de
nuestro juego. La agrupación de objetos es una técnica diseñada para
ahorrar tiempo de procesado, por lo que es útil y aplicable a las
aplicaciones de juegos arcade, como la que estamos construyendo.
Mediante la combinación de instancias de objetos, se evita el uso
intensivo del procesador muchas veces al crear instancias de objetos
sobre la marcha durante la ejecución del juego. Esto es especialmente
aplicable a las explosiones de particulas, ya que creamos varios
objetos en el mismo fotograma. En una plataforma de baja potencia,
como un dispositivo manual, la agrupación de objetos puede ayudar a
incrementar la velocidad de los fotogramas.

El Objeto conetenedor en nuestro juego


En nuestro juego, aplicamos el concepto de agrupar a las explosiones
de partículas. Por supuesto, podemos extender este concepto a las
piedras, misiles, platillos y a cualquier tipo de objeto que requiera
múltiples instancias.

Para este ejemplo, pensamos enfocarnos en las partículas. Como


veremos, agregar contenedores en javascript es relativamente simple,
pero una técnica muy poderosa.

Agregando variables contenedor a nuesto juego


Necesitamos agregar cuatro variables de ámbito a nuestra aplicación
para usar cuatro contenedores de partículas en nuestro juego.

var contenedorDeParticulas = []; var


particulasMax = 200;
var nuevaParticula; var particulasTemp;

La matriz contenedorDeParticulas almacena la lista de objetos


particulas instanciadas que están esperando ser usadas. Cuando la
función crearExplosion() necesita usar una particula, este ve
primeramente si hay alguna disponible. Si alguna está disponible, esta
la extrae de la parte superior de la pila de la matriz
contenedorDeParticulas y la coloca en la aplicación al alcance de la
variable nuevaParticula, la cual es una referencia a la partícula
agrupada. La función crearExplosion() conigura las propiedades de
nuevaParticula y luego lo empuja al inal de la matriz de las particulas
existentes.

Cuando la vida de una partícula llega a su in, la función


actualizarParticulas() une la particula desde la matriz particulas y la
coloca en lo último de la matriz contenedorDeParticula. Hemos creado
la referencia a la variable particulaTemp para aliviar a la función
actualizarParticulas(), necesaria para crear esta instancia en cada
fotograma.

El valor de la variable particulasMax es usada en una nueva función


llamada crearObjetosContenedores(). Llamamos a esta función en la
función estadoInicial(), antes de crear los sonidos y los eventos de
carga de las hojas de mosaico. Vamos a revisar la función
crearObjetosContenedores():

function crearObjetosContenedores(){
for (var ctr = 0; ctr < particulasMax; ctr++){
var nuevaParticula = {};
contenedorDeParticulas push(nuevaParticula);

}
console.log(“ contenedorDeParticulas=” +
contenedorDeParticulas length); }

Cómo podrás ver, simplemente iterar 0 a 1 menos que el valor de


particulasMax y colocar una instancia de objeto genérico en cada
elememnto del contenedor. Cuando se necesita una partícula, la
función crearExplosion() ve si contenedorDeParticulas lenght es mayor
que 0. Si una partícula esta disponible, se añade a la amtriz de las
aprtículas después de haber conigurado sus atributos. Si ninguna
partícula está disponible no se usa.

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);
}
} }

La función actualizarParticulas() pasará un bucle a traves


de las instancias particulas, actualiza los atributos de cada una, y luego
chequea cuando la vida de las partículas ha terminado. Si es así, la
función coloca las partículas en la parte inal del contenedor. A
continuación se muestra el código que agregamos a la función
actualizarParticulas():

if (remover) {
contenedorDeParticulas.push(particulasTemp);
particulas.splice(contarParticulas,1);

Agregando un temporizador de paso

En nuestra aplicación del capítulo 3, creamos un simple objeto


prototype ContadorDeFotogramas que mostraba la cantidad de
fotogramas actuales mientras el juego estaba en plena acción. Ahora
vamos a extender la funcionalidad de nuestro contador agregando un
temporizador de paso. El temporizador de paso usa la diferencia de
tiempo calculada entre los fotogramas para crear un factor paso. Este
factor paso es usado cuando se actualiza la posición de los objetos en
el canvas. El resultado será el renderizado mas suave de los objetos
del juego cuando hay caídas en la velocidad de fotogramas y el juego
es relativamente constante en los navegadores y sistemas que no
pueden mantener la velocidad de fotogramas necesarios para jugar
eicazmente.

Actualizamos el constructor de la función ContadorDeFotogramas para


que acepte un único parametro al que llamaremos fps. Este valor
representa los fotogramas por segundo que queremos que nuestro
juego tenga.
function ContadorDeFotogramas(fps) { if (fps == undeined){

this.fps = 40; }else{


this.fps = fps; }

Si el valor del parámetro fps no es pasado junto con la función,


entonces será usado el valor de 40.
También agregamos dos nuevas variables de nivel al objeto, para
calcular el paso en nuestro temporizador de paso:
this ultimaTiempo = tiempo.getTime(); this.paso = 1;

La variable ultimaTiempo contiene el tiempo en la cual el anterior


fotograma completado trabajó. Calculamos el paso comparando el
valor del tiempo actual con el valor de la variable ultimaTiempo en cada
uno de los fotogramas. Este calculo ocurre en la funcion
ContadorDeFotogramas cuentaFotogramas():

ContadorDeFotogramas prototype.cuentaFotogramas = function() {

var tiempo = new Date();


var diferenciaTiempo = tiempo.getTime()-this ultimaTiempo; this.paso =
(diferenciaTiempo/1000)*this.fps;
this ultimaTiempo = tiempo.getTime();

El valor local de la variable diferenciaTiempo es calcualda restando el


valor de la variable ultimaTiempo de la hora actual (representada por
el valor devuelto por tiempo.getTime()).

Para calcular el valor del paso, se divide el valor de la variable


diferenciaTiempo entre 1000 (la cantidad de milisegundos en un
segundo) y el resultado se múltiplica por la cantidad de fotogramas
deseado. Si el juego está rodando sin superavit o deicit de tiempo
entre los fotogramas, el valor del paso será 1. Si el actual fotograma
toma más tiempo que un fotograma para terminar, el valor es mayor que
uno (deicit). Si el actual fotograma toma menos tiempo que un
fotograma, el valor del paso es menor que 1 (superavit).

Por ejemplo si el último fotograma tomó demasiado tiempo para


procesar, el actual fotograma compensará moviendo cada objeto un
poco más que el valor del paso, que en este caso es igual a 1. Vamos a
ilustrar esto con un simple objeto. Digamos que queremos que el
platillo se mueva 5 pixeles a la derecha en cada fotograma, esto sería
un valor para la variable dx igual a 5. Para este mismo ejemplo decimos
que deseamos una cantidad de fotogramas igual a 40fps. Esto signiica
que queremos que cada fotograma use 26 milisegundos (1000/40 =
25).

Vamos también a suponer que la variable diferenciaTiempo entre el


actual fotograma y el último fotograma es de 25 milisegundos. Nuestro
juego esta corriendo con un deicit de 1 milisegundo por fotograma - Lo
que signiica que el procesar el juego esta tomando mas tiempo del que
queremos.

Para calcular el valor del paso, dividimos la variable diferenciaTiempo


entre 1000 : 26/1000 = 26. Múltiplicamos este valor por la cantidad de
fotogramas que deseamos para nuestro juego: .26*40=1.04. Nuestro
paso tiene un valor de 1.04 para el actual fotograma. Debido al deicit
en el tiempo de procesado, queremos mover todos los objetos del
juego algo mas de un fotograma de lo que tengamos.En el caso de
deicit, el valor de incremento será 1. Si hay superavit el valor del paso
sería menor que 1.

El valor del paso es múltiplicado para los cambios de movimientos en


cada uno de los vectores de los objetos dentro de la funcion
actualizar(). Esto le permite al juego tener una suavidad relativa,
siempre y cuando existan luctuaciones en la cantidad de fotogramas.
Ademas, el juego actualiza la pantalla de una manera relativamente
constante a través de los diversos navegadores y sistemas, lo que
resulta en que el juego es relativamente constante para cada usuario.
A continuación los nuevos cálculos de vectores de movimiento para
cada objeto dentro de la función actualizar():

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.

Crear rutinas para hoja de mosaicos dinámicas

También podría gustarte