HTML5

Las alternativas a Android

Dejando de lado el ecosistema privativo de Apple, Microsoft o RIM, algunos pensaran que Android está solo dentro del mundo de sistemas operativos móviles Open Source. Nada mas lejos de la realidad, diferentes alternativas están siendo desarrolladas y alcanzando niveles de madurez aceptables para a corto medio plazo competir con Android.

Algunos, seguramente penséis, que interés existe en segmentar mas el mercado cuando ya existe una alternativa Open Source que actualmente es conocida y extensamente implantada. Pues la razones son diversas:

  1. Android para muchos no es totalmente Open Source (+pdf)
  2. Las operadoras quieren “pillar cacho” en el mercado de las mobile stores (WAC)
  3. Estrategias multidispositivo diferentes (móvil+escritorio+tv)
  4. Competir con Android y robarle una porción de mercado (o todo, google no fue el primer buscador y existían buscadores previos dominando el mercado. También existen otros ejemplos como Symbian o Internet Explorer)

En ese sentido quiero hacer un repaso al ecosistema actual de sistemas operativos móviles “libres”.

FirefoxOS (boot2gecko)

Una de las alternativas que puede que se materialice mas a corto plazo, gracias o de la mano de Telefonica. FirefoxOs (antes conocido como boot2gecko) es la apuesta de Mozilla por un sistema operativo basado en la plataforma Open Web/HTML5 no solo de cara a las aplicaciones sino que todo su entorno de usuario (Gaia) esta desarrollado también en tecnologías Open Web.

Uno de sus principales socios es Telefónica, que lo ha adoptado como su principal caballo de Troya de cara a competir en la guerra por la distribución de aplicaciones y contenidos en dispositivos móviles. En esta apuesta, Telefónica ha llegado a un acuerdo con la empresa española GeeksPhone para el suministro de dispositivo móviles de bajo coste con este sistema operativo integrado, lo cual también pretende romper el mercado de la telefonía en cuanto a la subvención de terminales se refiere, después de la perdida de clientes sufrida, vamos, lo que se viene a decir, dos pájaros de un tiro.

Tizen

De las cenizas de MeeGo (que a su vez fue una combinación de los sistemas operativos móviles Moblin, creado por Intel, y Maemo, creado por Nokia) resurge Tizen, como la alternativa apoyada por la Linux Foundation o empresas como Intel o la propia Samsung.

La apuesta es similar a la de FirefoxOs, es decir, una plataforma basada en tecnología Open Web/HTML5, pero en este caso inciden mas que el sistema de Mozilla en que sea un sistema operativo orientado a ejecutarse en múltiples dispositivos, ya sean televisores, tablets u ordenadores a bordo de vehículos. Es quizá esto ultimo y el apoyo de la Linux Foundation su gran baza, ya que la apuesta actual de Samsung parece que claramente es Android.

Ubuntu

Parece que Canonical apuesta firmemente por que Ubuntu funcione en múltiples dispositivos. La apuesta por el entorno de escritorio Unity estaba totalmente orientada a que la interfaz de acceso a los diferentes dispositivos, como Pcs, televisores o tablets, fuera lo mas común y homogénea posible, y en el caso de los teléfonos móviles también ha sido así según la presentación realizada hace pocos días.

Ademas de la popularización de Ubuntu en el PCs, una de las principales bazas de este sistema operativo es la posibilidad de que gracias a un “docking” o adaptador, nuestro móvil se convierta en una estación de trabajo, conectándolo a una pantalla o teclado. Esta posibilidad ya existía con la solución Ubuntu for Android también desarrollada por canonical, pero es con su propio sistema operativo para móviles con lo que cobra mayor sentido e integración.

Ubuntu apuesta por HTML5 como principal plataforma para el desarrollo de aplicaciones, aunque de cara a aplicaciones de alto rendimiento o requerimientos gráficos altos como juegos ofrece una plataforma de desarrollo basada en QML.

Open WebOS

Tras la decisión de HP de cesar el soporte y desarrollo de WebOs, inicio Open WebOs como un proyecto Open Source, y a mediados de Septiembre de 2012 vio la luz una primera versión alpha del sistema operativo. Además hace pocos días salio la noticia de que había sido posible ejecutar Open WebOs 1.0 en un tablet Nexus 7 de google.

Como su propio nombre indica, es un sistema basado en Web. Hay que tener en cuenta que WebOs fue uno de los principales precursores de la utilización de la plataformas Open Web como entorno multiplataforma y sobre todo orientado a dispositivos moviles, pero por desgracia, no es suficiente para asegurar que este sistema podrá competir con FirefoxOs, Tizen o Ubuntu.

Bada

Aunque el proyecto no es Open Source gran parte de sus componentes si lo son y en muchas ocasiones se ha rumoreado con su liberación, y es por eso que se ha añadido a esta lista.

En principio la apuesta personal de Samsung, pero como hemos visto, la marca coreana también esta detrás de Tizen y actualmente apuesta por Android para los terminales de gama alta. Es por eso que en la actualidad, Samsung, ha orientado este sistema operativo a dotar de “inteligencia” a sus terminales de gama mas baja, gracias a que este sistema consume menos recursos que Android. Su potencial únicamente radica en que es una solución desarrollada por uno de los principales fabricantes de terminales móviles, pero aun así, la posibilidades de que este sistema tenga algún tipo de éxito son remotas por diversas razones; APIs cerradas, limitación de instalación de aplicaciones, limitación de utilización de aplicaciones VoIP o el hecho de que es un sistema que no permite ejecutar aplicaciones en paralelo.

ArinOs

ArinOs es un proyecto que empecé hace bastante tiempo, cuando apareció por aquel entonces ChromeOS. La idea de ChromeOs me parecía interesante pero también tenia mis dudas sobre las soluciones “todo en la nube” en cuento a privacidad y libertad. Además me parecía difícil de meter mano en su interior y mas aun en caso de querer integrarlo en nuevos dispositivos.

Es por eso que el objetivo de ArinOs era ofrecer una plataforma sencilla en su concepto y software que permitiera poder crear un Sistema Operativo Web de forma sencilla sobre todo orientado a poder ser adaptado y extendido fácilmente a diferentes dispositivos, como puede ser un punto de información, una maquina dispensadora de billetes de transporte o un televisor.

ArinOs Logo

La idea era simple, y en cierta medida existían aproximaciones similares en el caso de los terminales móviles como era PhoneGap: Extender la API de JavaScript para poder conectarnos con un “core” externo que extendiera las funcionalidades de nuestro navegador y así poder acceder mediante JavaScript a código de alto nivel que nos permitiera por ejemplo acceder a dispositivos industriales o funcionalidad especifica no común.

Por tanto podemos decir que ArinOs se basa en un navegador extendido que permite comunicarnos con un servidor (en este caso en Python) que interpreta los comandos, los ejecuta y nos devuelve el resultado. Por tanto podemos crear nuestra librería en Python, por un lado, y en JavaScript, por otro, y comunicarlas a través de ArinOS.

ArinOs schema

La elección de Python como entrono para el servidor, se justificaba, basándonos en la posibilidad de cargar código de forma dinámica y que además nos permite acceder a librerías externas mediante ctypes.

La intención de ArinOs, por tanto no es competir con B2G (FirefoxOs) o ChromeOs, ni mucho menos, sino la de ofrecer una herramienta que permita introducir dispositivos marginales al mundo de las aplicaciones y sistemas operativos HTML5/WEB.

Todavía este proyecto esta en una fase muy temprana y solo tengo partes parciales de la funcionalidad. Espero sacar algún rato entre todos mis proyectos personales para seguir avanzando ya que queda mucho trabajo, como implementar diferentes métodos de comunicación entre el cliente y el servidor (síncrona o asíncrona), establecer una estructura del servidor correcta donde sea sencillo incluir librerías tanto en Python como en código nativo o definir una estructura para aplicaciones y que incluso, en un futuro, que tanto las aplicaciones de Chrome como de FirefoxOs puedan funcionar en este sistema.

De momento podéis acceder a su repositorio de GitHub en la siguiente dirección.

https://github.com/ikeralbeniz/ArinOs

Firefox TV: (Firefox OS + Raspberry Pi) * Set-Top-Box

Cuando llegó mi Raspberry Pi, me surgieron miles de ideas de como aprovechar este mini-ordenador que cuesta al rededor de 35€. Por un lado se me ocurrió compilar un receptor software de DVB-T2 en el que estamos trabajando en el laboratorio y también se me ocurrió utilizarlo como centro multimedia con un cliente web de Bittorrent para añadir mis torrents desde el trabajo. Pero finalmente la idea que mas me motivaba y que mezclaba varios proyectos que tenia entre manos era la de hacer correr el sistema operativo B2G (Boot To Gecko) o también llamado Firefox OS en mi Raspberry Pi. Firefox Os es un nuevo sistema operativo WEB basado en en el motor de Firefox (Gecko), que tiene como intención competir con Android y IOs en el mundo de los terminales móviles inteligentes. En este caso la apuesta de Firefox OS es que todas las aplicaciones e incluso el sistema operativo (o por lo menos la parte de interfaz) sea Web, estrategia mas consolidada en los sistemas operativos de los televisores inteligentes y que viene impulsada por las capacidades que ofrece HTML5.

Firefox OS

Teniendo en cuenta todo esto ¿Por que no desarrollar un pequeño Set-Top-Box utilizando una Raspberry Pi en el que corra Firefox Os adaptado a las características (dimensión e interfaces de entrada) de un televisor?. La idea es ofrecer una alternativa low-cost y lo mas OpenSource posible a soluciones del estilo smart-hub de Samsung, con todas las posibilidades que ofrecería en cuanto a explotación de nuestros televisores como centro de ocio, trabajo o incluso servicios de tele-asistencia.

En siguiente posts explicaré los pasos que estoy siguiendo para configurar mi Firefox TV ya que por ahora tengo una pequeña aproximación que le faltan algunos retoques como se puede ver en el siguiente vídeo.

Desproteger Web-Fonts con Google Docs

Como comente en un articulo anterior, la ultima versión de CSS (CSS3) permite utilizar fuentes propias en nuestras paginas Web gracias al elemento @fotn-face. Por tanto si disponemos de una fuente MiFuente.ttf podremos utilizarla en nuestra página Web de la siguiente manera.

@font-face {
	font-family:Mifuente;
	src: url(‘MiFuente.ttf’);
}

@font-face {
	font-family: Mifuente;
	font-weight: bold; 
	src: url('MiFuente.ttf’);
}

Que luego podremos usarla de igual manera que otras fuentes

body{
	font-family: Mifuente;
}

Al igual que en las imágenes es posible utilizar la etiqueta data y adjuntar la fuente en base64.

@font-face {
	font-family: ' Mifuente ';
	src:          url(data:font/truetype;charset=utf-8;base64,AAEAAAAQAQAABAAARkZUTVtQ+h4AAAEMAA………..AMrRFmwFCs=) format('truetype');
}

Por eso muchos diseñadores Web utilizan esta ultima formular para intentar dificultar el acceso a la fuente, en caso de que esta haya sido desarrollada por el. Aun así es relativamente sencillo poder copiar la fuente y usarla en nuestra Web, pero.. ¿y usarla en nuestro PC? En este caso seria tan sencillo como coger todo el stream en base64 y decodificarlo. Existen librerías para todos los lenguajes de programación que permiten convertir de/a base64, pero si queréis ir a los sencillo, tenéis un montón de conversores online disponibles. Yo recomiendo el siguiente ya que permite convertir a un binario.

http://www.motobit.com/util/base64-decoder-encoder.asp

En la imagen siguiente podemos ver como podemos convertir una fuente en base64 a un binario TTF.

Una vez tenemos la fuente podremos usarla con todas nuestra aplicaciones (Si estamos en Windows deberemos arrástrala a la carpeta FONTS). Aun así, algunos diseñadores de fuentes, utilizan técnicas de ofuscación que permiten que una fuente pueda ser utilizada en nuestra Web pero no en nuestro PC. Y esto es posible porque existe software capaz de alterar la estructura interna de las fuentes de tal forma que puede ser reenderizada por los navegadores pero no por los PCs.

En estos casos deberemos realizar los siguientes pasos:

  1. Usando Gmail nos enviaremos la fuente (con extensión TTF) a nosotros mismos
  2. Gmail nos mostrara nuestro adjunto con las típicas opciones de VER o DESCARGAR
  3. Pulsaremos VER y se nos mostrara la fuente con el visualizador de fuentes de Google Docs
  4. En el menú FILE pulsaremos en PRINT (PDF)
  5. Cancelaremos la impresión y guardaremos el PDF

Ya tenemos nuestra fuente en formato PDF, por lo que ahora necesitaremos convertirla de PDF a otros formatos que nos sean mas útiles. Para ello usaremos otro servicio online:

http://onlinefontconverter.com/myfonts.php

Este servicio nos permitirá convertir nuestra fuente en PDF a una gran variedad de fuentes, entre las cuales nos interesa:

  • OTF: Formato Open Source de nuestra fuente, 100% compatible con sistema GNU/Linux
  • TTF: Formato típico de fuente que solemos tener instalado en sistemas Windows
  • DFONT: Formato de fuente para MAC
  • SVG: Formato de fuente VECTORIAL propuesto por W3C, muy útil si utilizas inkscape ya que es posible modificarla.

Espero que este truco os sea de gran utilidad ;)

Iconic Stroke: una fuente de iconos muy interesante

En las ultima semanas tengo el blog un poco parado ya que estoy a tope con otros proyectos. Entre ellos, algunos relacionados, como no, con aplicaciones móviles. Uno de estos proyectos lo estoy realizando junto a Javier Jimenez que es un experto en HTML5, JavaScript y todo lo relacionado con UX en general.

Diseñando una aplicación, a Javi se le ocurrió la idea de usar una fuente de Iconos en vez de imágenes para aligerar la aplicación y que fuera mas fácil de diseñar y trabajar con transparencias etc… La fuente en cuestión es la Iconic Stroke y las posibilidades que ofrece creo que son muy interesantes, no solo de cara a no tener que usar imagenes, ya que CSS3 nos permite usar fuente propias, sino que, de cara a diseñar iconos también nos puede ser de gran ayuda. Por eso os dejo aquí un link de donde podéis descargar esta fuente y un ejemplo de los iconos, cargando la fuente con CSS3.

http://googlefontdirectory.googlecode.com/hg/iconic/IconicStroke.ttf

Solo veréis los iconos si usáis un navegador que soporte CSS3 como Firefox, Crome o Safari.
! # $ % & ( ) *
! # $ % & ( ) *
+ , - . / 0 1 2 3 4
+ , - . / 0 1 2 3 4
5 6 7 8 9 : ; < = >
5 6 7 8 9 : ; < = >
? @ A B C D E F G H
? @ A B C D E F G H
I J K L M N O P Q R
I J K L M N O P Q R
S T U V W X Y Z [ \
S T U V W X Y Z [ \
] ^ _ ` a b c d e f
] ^ _ ` a b c d e f
g h i j k l m n o p
g h i j k l m n o p
q r s t u v w x y z
q r s t u v w x y z
{ | } ~
{ | } ~

Haciendo hablar a Twitter

Hace tiempo que tenia pendiente hacer un mashup de la librería javascript de Twitter, Google Translate y las nuevas oportunidades de HTML5. Pues al final me he animado y no ha sido muy complicado: por un lado detectar el idioma del tweet, por otro lanzar la ejecución del el audio, y por ultimo integrar estas dos funciones el la librería widget.js que ofrece Twitter.

Esta es una primera aproximación, que puede ser mejorada considerablemente. A continuación el código y ejemplo (esta probado que funciona en chrome).

El widget.js modificado se puede descargar aqui.

[html]
<html>
<head>
</head>
<body>
<script type="text/javascript" src="http://www.google.com/jsapi?key=AIzaSyBMhwGvMwDbGgXIVU4SI_aVy7AjeGFnorw"></script>
<script type="text/javascript">
google.load("language", "1");

var auxtest = "";
var elapsedtww = 0;

function cleanHtml(html){
var result = html.replace(/<.*?>(.*?)<\/.*?>/g, function(a,s){return s;});
var result2 = result.replace(/#(.*?)/g, function(a,s){return s;});
var result3 = result2.replace(/@(.*?)/g, function(a,s){return s;});
return cleanLinks(result3);
}

function cleanLinks(text){
var exp = /(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig;
return text.replace(exp,"");
}

function sleep(milliSeconds){
var startTime = new Date().getTime(); // get the current time
while (new Date().getTime() < startTime + milliSeconds); // hog cpu
}

function submitGtext(text) {
while (elapsedtww > 0){
sleep(1000);
}
elapsedtww = 1;
auxtest = cleanHtml(text);
google.language.detect(auxtest, playVoice);
return false;
}

function playVoice(result){
var langcode = ‘en’;
if (result.language) {
var language = ‘unnamed’;
for (l in google.language.Languages) {
if (google.language.Languages[l] == result.language) {
language = l;
break;
}
}

if(language == "SPANISH"){
langcode = ‘es’;
}
if(language == "PORTUGUESE"){
langcode = ‘pt’;
}
if(language == "GERMAN"){
langcode = ‘de’;
}
}
var audio_file = document.getElementById(‘voice’);
while(audio_file.currentTime < audio_file.duration && audio_file.currentTime != 0){
sleep(100);
}
audio_file.src = ‘http://translate.google.com/translate_tts?q=’+escape(html_entity_decode(auxtest))+’&tl=’+langcode;
document.getElementById(‘audiopaths’).value = document.getElementById(‘audiopaths’).value +"\r\n\r\n"+html_entity_decode(auxtest);
audio_file.load();
audio_file.play();
elapsedtww = 0;
}

function html_entity_decode(str)
{
try
{
var tarea=document.createElement(‘textarea’);
tarea.innerHTML = str; return tarea.value;
tarea.parentNode.removeChild(tarea);
}
catch(e)
{
//for IE add <div id="htmlconverter" style="display:none;"></div> to the page
document.getElementById("htmlconverter").innerHTML = ‘<textarea id="innerConverter">’ + str + ‘</textarea>’;
var content = document.getElementById("innerConverter").value;
document.getElementById("htmlconverter").innerHTML = "";
return content;
}
}
</script>
<script src="http://www.ikeralbeniz.net/wp-content/uploads/2010/12/widget.js"></script>
<script>
new TWTR.Widget({
version: 2,
type: ‘search’,
search: ‘meneame_net’,
interval: 9000,
title: ‘Test’,
subject: ‘Test de GoogleTranslate + twiiter’,
width: 250,
height: 300,
theme: {
shell: {
background: ‘#8ec1da’,
color: ‘#ffffff’
},
tweets: {
background: ‘#ffffff’,
color: ‘#444444′,
links: ‘#1985b5′
}
},
features: {
scrollbar: false,
loop: true,
live: true,
hashtags: true,
timestamp: true,
avatars: true,
toptweets: true,
behavior: ‘default’
}
}).render().start();
</script>
<audio id="voice" src="">
<h3>Este ejemplo solo funciona con navegadores que soportan HTML5 como CHROME</h3>
</audio>
<body>
</html>
[/html]




Este ejemplo solo funciona con navegadores que soportan HTML5 como CHROME

Jugando con HTML5: Animaciones

Seguimos con HTML5, pero en este caso vamos a jugar con animaciones. En este caso vamos a hacer una animación simple controlada mediante el teclado. La idea es establecer las bases para hacer un juego sencillo (tipo RPG). Como no podía ser de otra forma, nos hemos basado en el juego Zelda para hacer nuestra demostración (con un poco de suerte alguno se anima y se curra un Zelda en HTML5..).

La forma en la que vamos a hacer la animación es muy sencilla: vamos a dibujar una imagen de Zelda en un canvas con un fondo determinado. Y vamos a capturar los eventos Keydown. En función de la tecla seleccionada (arriba, abajo, derecha e izquierda) cambiaremos la imagen de Zelda y la moveremos. Para generar el efecto de animación, lo que haremos es refrescar el canvas borrándolo y redibujandolo cada vez que pulsemos una tecla.

Una aproximación mas optimizada seria solo borrara el área a redibujar, pero en nuestro caso de cara a simplificar el código, borraremos y dibujaremos el canvas completo.

Además en nuestro caso solo redibujaremos el canvas cada vez que se pulse una tecla ya que solo tenemos un elemento móvil que se controla mediante teclado. En caso de tener otros elementos móviles no controlables (los enemigos.. hacer que el mar se mueva..) deberemos redibujar el canvas automáticamente con un timer: setInterval(funcion tiempo);

Inicializar la Animación

Inicializaremos las variables globales y dibujaremos la animación por primera vez. En el caso de que el redibujado sea automático, como hemos explicado antes, sera aquí donde lancemos el timer.

[code lang="javascript"]
var x = 0;
var y = 0;
var iangle = 0;
var pravangle = 0;
var step = 0;
var imgsrc = "zd_dwn.png";
window.addEventListener('keydown',doKeyDown,true);
window.onload = function() {
drawZelda();
//return setInterval(drawZelda, 500);
}
[/code]

Dibujado de la Animación

Basicamente lo que hacemos es definir el area del canvas y en función de la dirección de las teclas que pulsemos, y cantas veces seguidas se pulse, se nos mostrara una imagen u otra en una posición definida por x e y (que controlaremos con el teclado). Es decir, cuando se cambia de dirección de avance se muestra un icono, y si esta dirección se mantiene, se intercalan diferentes imagenes para generar la animación de avance.

[code lang="javascript"]
unction drawZelda(){
var drawingCanvas = document.getElementById('myDrawing');
drawingCanvas.setAttribute('width', 500);
drawingCanvas.setAttribute('height', 500);
// Check the element is in the DOM and the browser supports canvas
if(drawingCanvas.getContext) {
// Initaliase a 2-dimensional drawing context
var ctx = drawingCanvas.getContext('2d');
ctx.clearRect(0, 0, 500, 500);
ctx.beginPath();
switch(iangle){
case 0:
if(iangle != pravangle){
imgsrc = "zd_dwn.png";
pravangle = iangle;
step = 0;
}else{
if(step > 0){
if(step == 1){
imgsrc = "zd_dwn1.png";
}else if(step == 2){
imgsrc = "zd_dwn.png";
}else{
imgsrc = "zd_dwn2.png";
}
}
}
break;
case 0.5:
if(iangle != pravangle){
imgsrc = "zd_rgt.png";
pravangle = iangle;
step = 0;
}else{
if(step > 0){
if(step == 1){
imgsrc = "zd_rgt1.png";
}else if(step == 2){
imgsrc = "zd_rgt.png";
}else{
imgsrc = "zd_rgt2.png";
}
}
}
break;
case 1:
if(iangle != pravangle){
imgsrc = "zd_up.png";
pravangle = iangle;
step = 0;
}else{
if(step > 0){
if(step == 1){
imgsrc = "zd_up1.png";
}else if(step == 2){
imgsrc = "zd_up.png";
}else{
imgsrc = "zd_up2.png";
}
}else{
imgsrc = "zd_up1.png";
}
}
break;
case 1.5:
if(iangle != pravangle){
imgsrc = "zd_lft.png";
pravangle = iangle;
step = 0;
}else{
if(step > 0){
if(step == 1){
imgsrc = "zd_lft1.png";
}else if(step == 2){
imgsrc = "zd_lft.png";
}else{
imgsrc = "zd_lft2.png";
}
}
}
break;
}
ctx.fillStyle = "#ff0000";
var myImage = new Image();
myImage.onload = function() {
ctx.drawImage(myImage, 238+x , 238+y, 24, 24);
}
myImage.src = imgsrc;
ctx.stroke();
ctx.fill();
ctx.closePath();
}
}
[/code]

Capturando el teclado

Esta función lo que hace basicamente es cambiar la posición de Zelda, en función de las teclas que se pulsan. La forma de cambiar la posición de incrementando o decrementando x e y, que son variables globales que se usan en drawZelda para dibujar a nuestros personaje.

Después de pulsar una tecla se regenera la imagen para que se agan efectivos los cambios. Esto no haría falta si hubiéramos definido un timer para el redibujado.

[code lang="javascript"]
function doKeyDown(evt){
if(evt.keyCode >= 37 && evt.keyCode <= 40){
switch (evt.keyCode) {
case 38: /* Up arrow was pressed */
if(y > -239){
y--;
iangle = 1;
}
break;
case 40: /* Down arrow was pressed */
if(y < 237){
y++;
iangle = 0;
}
break;
case 37: /* Left arrow was pressed */
if(x > -239){
x--;
iangle = 1.5;
}
break;
case 39: /* Right arrow was pressed */
if(x < 237){
x++;
iangle = 0.5;
}
break;
}
if(step > 0){
if(step == 1){
step = 2;
}else if(step == 2){
step = 3;
}else{
step = 1;
}
}else{
step = 1;
}
drawZelda();
}
}
[/code]

El Ejemplo

Aquí tenemos un ejemplo funcional de lo explicado en este articulo. espero que os sea de gran utilidad y podáis hacer juegos que nos hagan disfrutar a todos. ;)

NOTA: Para que el navegador no haga scroll en algunos navegadores se puede evitar pulsando Ctrl.



Your browser doesn’t support canvas.


Jugando con HTML5: Canvas y SVG (II)

En el artículo anterior analizábamos el formato SVG con lo que ya tenemos nuestras primeras pistas de como parsear una imagen vectorial y dibujarla en un canvas. Por tanto en este artículo vamos a explicar como recorrer el formato SVG con un parser de XML e iremos dibujando cada elemento en el canvas. En la siguientes indicaciones se dará por hecho que el lector tiene conocimientos básicos en HTML y JavaScript.

Lo primero que vamos a hacer es definir una estructura HTML básica sobre la que dibujar:

[code lang="html"]








[/code]

Básicamente disponemos de un documento HTML con un elemento canvas (html5) con el identificador “canvas”. Además le hemos dado estilo añadiéndole un borde negro. La etiqueta BODY tiene definido un método onLoad que hará que se ejecute la función JavaScript draw() al cargarse la pagina. Es en este método draw es donde parsearemos el SVG y dibujaremos en el canvas.

Por tanto pasaremos a explicar cada parte del código de la función draw().

Parser XML

Lo primero que hay que hacer es abrir el SVG y recorrerlo con el DOM XML que soporte nuestro navegador, para ello usamos el siguiente código:

[code lang="javascript"]
if (window.XMLHttpRequest)
{ // code for IE7+, Firefox, Chrome, Opera, Safari
xmlhttp=new XMLHttpRequest();
}
else
{ // code for IE6, IE5
xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.open("GET","test.svg",false);
xmlhttp.send();
xmlDoc=xmlhttp.responseXML;
[/code]

Por tanto a partir de ahora trabajaremos con el objeto xmlDoc que dispone de métodos que nos permitirá recorre la estructura XML de forma sencilla.

Elemento CANVAS

El otro elemento importante de nuestra aplicación es el canvas donde dibujaremos. Por tanto buscaremos nuestro canvas a partir del ID(“canvas”) que definimos y de el obtendremos el contexto “2D” que contiene los métodos para poder dibujar sobre el.

[code lang="javascript"]
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
[/code]

Empezamos a recorrer el XML

Como hemos dicho en la estructura SVG disponemos de una única etiqueta SVG, por tanto podemos obtener dicho elemento mediante el método getElementsByTagName que nos dará un array de elemento SVG. Al haber solo uno, solo trabajaremos con la posición 0 del array. Por tanto podremos acceder a todos los métodos y nodos hijos de la etiqueta SVG a través del elemento xmlDoc.getElementsByTagName("svg")[0]. La etiqueta SVG tiene dos atributos que nos interesan a la hora de inicializar el canvas, y son la anchura y la altura. Por tanto, a través de la función setAttribute definiremos al anchura y altura del canvas:

[code lang="javascript"]
canvas.setAttribute('width',xmlDoc.getElementsByTagName("svg")[0].getAttribute("width"));
canvas.setAttribute('height',xmlDoc.getElementsByTagName("svg")[0].getAttribute("height"));
[/code]

Una vez configurada el área de trabajo, pasaremos a recorrer todas las capas G del documento y dibujaremos todas las figuras de cada capa:

[code lang="javascript"]
var layers = xmlDoc.getElementsByTagName("g");
for(var l = 0; l < layers.length; l++){
var cchildn = xmlDoc.getElementsByTagName("g")[l].childNodes.length;
for(var g = 0; g < cchildn; g++){

….

}
}
[/code]

Es decir, buscamos todos lo elemento G y por cada elemento recorremos todos su subelementos. Para eso obtendremos el numero de nodos hijos y analizaremos cada uno de ellos para saber que tipo de nodos son y como actuar en función de ello.

En el artículo anterior comentamos que solo nos íbamos a centrar en dos tipos de elementos o nodos: los RECT y los PATH, por tanto usando la propiedad nodeName comprobaremos que tipo de nodo es cada nodo hijo y en función de ello ejecutaremos una rutina u otra para dibujar el elemento.

Nodo RECT

En caso de que el nodo sea un rectángulo, podemos comprobarlo de la siguiente manera:

[code lang="javascript"]
if(xmlDoc.getElementsByTagName("g")[l].childNodes[g].nodeName == "rect"){
ctx.beginPath();

ctx.stroke();
ctx.fill();
}
[/code]

Iniciamos con beginPath para indicar que queremos crear una nueva figura (todos los estilos y demás propiedades definidas a partir de ahora solo afectan a este path). Y terminamos con stroke, que dibuja los trazos definidos en el canvas y fill que los rellena.

Como comentamos en el articulo anterior, tanto RECT como PATH tienen un atributo estilo que define el color de fondo de la figura, color de borde, grosor.. por tanto antes que nada deberemos parsear dicho atributo y aplicar los estilos correspondientes:

[code lang="javascript"]
var rstyle = xmlDoc.getElementsByTagName("g")[l].childNodes[g].getAttribute("style").split(';');
for(var j=0; j < rstyle.length; j++){
if(rstyle[j].split(':')[0] == "fill"){
ctx.fillStyle = rstyle[j].split(':')[1];
}

if(rstyle[j].split(':')[0] == "stroke"){
ctx.strokeStyle = rstyle[j].split(':')[1];
}

if(rstyle[j].split(':')[0] == "stroke-width"){
ctx.lineWidth = rstyle[j].split(':')[1];
}
}
[/code]

Por tanto recogeremos los valores fill,stroke y stroke-width del atributo style y por cada uno de ellos definiremos el estilo del path con fillstyle,strokestyle y linewidth respectivamente.

Ya solo nos quedará recoger los datos del rectángulo y dibujarlo.

[code lang="javascript"]
var rwidth = parseInt(xmlDoc.getElementsByTagName("g")[l].childNodes[g].getAttribute("width"));
var rheight = parseInt(xmlDoc.getElementsByTagName("g")[l].childNodes[g].getAttribute("height"));
var rx = parseInt(xmlDoc.getElementsByTagName("g")[l].childNodes[g].getAttribute("x"));
var ry = parseInt(xmlDoc.getElementsByTagName("g")[l].childNodes[g].getAttribute("y"));
ctx.moveTo(rx,ry);
ctx.lineTo(rx+rwidth,ry);
ctx.lineTo(rx+rwidth,ry+rheight);
ctx.lineTo(rx,ry+rheight);
ctx.lineTo(rx,ry-lw);
[/code]

A pesar de que se pueden dibujar rectángulos de muchas formas, nosotros los dibujaremos mediante cuatro líneas, ya que conocemos la posición del vértice superior izquierdo y la anchura y altura del rectángulo. Además esta aproximación permite integrar la posibilidad de bordes redondeados solo con definir un radio de borde y la función quadraticCurveTo (no confundir con la propiedad "lineJoin =round" que redondea la unión entre líneas, aunque en algún caso, de radios muy pequeños, nos puedan dar el mismo resultado).

Nodo PATH

El nodo PATH al igual que el RECT tiene un atributo estilo que lo recorreremos de igual manera, pero en el caso de PATH, como comentamos en el articulo anterior, la estructura del trazo esta descrita en el atributo d, bajo una estructura conocida. Por tanto deberemos recorrer dicha estructura y dibujar los diferentes segmentos en los que se divide el trazo.

[code lang="javascript"]
var datribute = xmlDoc.getElementsByTagName("g")[l].childNodes[g].getAttribute("d");
var datributedata = ""+datribute.replace(/\,/g,' ')+"";
var darray = datributedata.split(" ");
var arc = 0;
for(var j = 0; j < darray.length; j++){
if(darray[j] == "M"){
ctx.moveTo(darray[j+1],darray[j+2]);
j = j+2;
}
if(darray[j] == "L"){
ctx.lineTo(darray[j+1],darray[j+2]);
j = j+2;
}
if(darray[j] == "C"){
ctx.bezierCurveTo(darray[j+1],darray[j+2],darray[j+3],darray[j+4],darray[j+5],darray[j+6]);
j = j+6;
}
if(darray[j] == "A"){
if(arc == 0){
ctx.scale(1, 1);
ctx.arc(parseInt(darray[j+1]), parseInt(darray[j+2]), parseInt(darray[j+6]/2), 0, Math.PI*2, false);
addJsText("ctx.arc("+parseInt(darray[j+1])+","+parseInt(darray[j+2])+","+parseInt(darray[j+6]/2)+", 0, Math.PI*2, false);");
j = j+7;
ctx.stroke();
ctx.fill();
addJsText("ctx.stroke();");
addJsText("ctx.fill();");
ctx.beginPath();
addJsText("ctx.beginPath();");
arc = arc + 1;
}
}
}
[/code]

Por tanto en el caso de estructuras M realizaremos un moveTo, en el caso de L dibujaremos una línea con lineTo, en el caso de C una curva con bezierCurveTo y en el caso de A un arco con arc (en el caos de A la aproximación está simplificada, pendiente de un mejor análisis)

Ejemplo

A continuación tenemos un ejemplo funcional de lo aquí descrito, donde podéis ver todo el javascript completo y con el que podéis hacer diferentes pruebas. Este script no pretende ser una solución para convertir lo archivos svg a canvas, sino una explicación de como se podría crear una librería para hacerlo o como base en caso de que se quisiera hacer un desarrollo a medida para analizar algún tipo de dibujo vectorial concreto.

Jugando con HTML5: Canvas y SVG (I)

Una de las cosas que mas me llamó la atención cuando vi los primeros ejemplos de dibujos hechos con canvas era el posible paralelismo entre el formato SVG y las llamadas JavaScript para dibujar sobre un elemento canvas. Buscando en Internet hay muchos intentos – que la mayoría no funcionan – de parsear imágenes vectoriales en formato SVG (que es un XML) y dibujarlas sobre un elemento canvas. Examinando alguno de esos intentos fallidos he intentado hacer una prueba de parsear un dibujo vectorial sencillo y dibujarlo en un canvas.

El ejemplo con el que he hecho las pruebas es el siguiente:

Como veis se trata de un rectángulo y sobre el una figura amorfa. Por tanto lo que tenemos que hacer primero es analizar el formato SVG para identificar las diferentes figuras y sus propiedades.

La estructura simplificada del SVG que tenemos es la siguiente:

[code lang="xml"]




..
..



[/code]

Es decir, dentro de la etiqueta SVG tenemos por un lado la etiqueta DEFS y diferentes etiquetas G (cada una de ellas será una capa). Dentro de la etiqueta DEFS normalmente encontramos información como la definición de los gradientes que luego se usaran para rellenar las figuras o la perspectiva del dibujo. En este primer ejemplo no lo tendremos en cuenta para no complicar las cosas.

Las etiquetas G son las que en nuestra primera aproximación mas nos interesa. Tendremos tantos elemento G como capas tenga el dibujo. Y dentro de cada capa tendremos diferentes elementos como rectángulos (RECT) u otro tipo de trazo (PATH). Por tanto vamos a pasar a analizar los dos elementos que componen nuestro SVG de ejemplo.

RECT

[code lang="xml"]
style="opacity:1;fill:#7fd518;fill-opacity:1;fill-rule:nonzero;stroke:#bc6081;stroke-width:15;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
id="rect3155"
width="225.71428"
height="160"
x="100"
y="78.076485"
ry="0" />
[/code]

Comos e puede ver nuestra etiqueta RECT dispone de un atributo style del que podemos destacar las propiedades fill que es el color de relleno (en este caso #7fd518), stroke; que es el color del trazo o borde en este caso y stroke-width; que es el grosor de trazo o borde. Otros de los atributos que tiene esta etiqueta es la anchura del rectángulo (width), la altura (height), y la posición de la esquina superior izquierda (x,y). es decir, recogiendo estos atributos podremos dibujar un rectángulo en nuestro canvas.

PATH

[code lang="xml"]

style="fill:#fd500f;fill-opacity:1;fill-rule:evenodd;stroke:#3a57df;stroke-width:8.30000019;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 18.571429,154.42858 C 35.714285,154.42858 112.85715,165.85715 95.714285,128.71429 C 78.571428,91.571429 30,77.285715 81.428571,63.000001 C 132.85714,48.714286 112.85715,23 121.42858,20.142857 C 129.99999,17.285715 267.14285,103 187.14285,97.285716 C 107.14286,91.571429 158.57142,131.57144 149.99999,160.14287 C 141.42857,188.71429 -4.2857147,217.28572 15.714286,197.28572 C 35.714285,177.28572 24.285715,154.42858 18.571429,154.42858 z"
id="path2381" />
[/code]

La etiqueta PATH identifica un trazo por lo que se compone de diferente tipo de trazos: líneas, curvas,… al igual que RECT dispone de un atributo style con las mismas características. Pero en el caso de PATH, las indicaciones del trazado están indicadas en el atributo D.

El trazado se define de la siguiente manera:

[code lang="xml"]
….
[/code]

Y los tipos pueden ser:

  • M : Moverse a una posición
  • A: Arco
  • L: Línea
  • C: Curva

Los argumentos pueden estar separados por espacios o por comas. Por tanto dividiremos la cadena del atributo d en estructuras y en función del tipo dibujaremos un arco, una línea, una curva o nos situaremos en una posición.

Con esta primera aproximación ya nos hacemos una idea de cómo deberemos recorrer el xml del svg para dibujar en un canvas.

 Scroll to top