HTML5 (HyperText Markup Language, versión 5) es la quinta revisión importante del lenguaje básico de la World Wide Web, HTML. Es el lenguaje de marcado predominante para la elaboración de páginas web que describe y traduce la estructura e información en forma de texto, y complementa el texto con objetos tales como imágenes. HTML5 aún se encuentra en modo experimental, lo cual indica la misma W3C, aunque ya es usado por múltiples desarrolladores web debido a sus avances, mejoras y ventajas. Al no ser reconocido en viejas versiones de navegadores por sus nuevas etiquetas, se le recomienda al usuario común actualizar a la versión más nueva, para poder disfrutar de todo el potencial que provee HTML5.

HTML5 establece una serie de nuevos elementos y atributos que reflejan el uso típico de los sitios web modernos. Algunos de ellos son técnicamente similares a las etiquetas <div> y <span>, pero tienen un significado semántico, como por ejemplo <nav> (bloque de navegación del sitio web) y <footer>. Otros elementos proporcionan nuevas funcionalidades a través de una interfaz estandarizada, como los elementos <audio> y <video>. Algunos elementos de HTML 4.01 han quedado obsoletos, incluyendo elementos puramente de presentación, como <font> y <center>, cuyos efectos son manejados por el CSS. También hay un renovado énfasis en la importancia del scripting DOM para el comportamiento de la Web 2.0.

HTML5 Incorpora etiquetas (canvas 2D y 3D, audio, video) con codecs para mostrar los contenidos multimedia capaces de renderizar en los navegadores más importantes (Mozilla, Chrome, Opera, Safari e IE). Actualmente hay una lucha entre imponer codecs libres (WebM + VP8) o privados (H.264/MPEG-4 AVC). También incorpora etiquetas para manejar grandes conjuntos de datos: Datagrid, Details, Menu y Command (permiten generar tablas dinámicas que pueden filtrar, ordenar y ocultar contenido en el cliente); nuevos tipos de datos en los formularios (email, number, url, datetime, etc.) y facilidades para validar el contenido sin JavaScript; visores MathML (fórmulas matemáticas) y SVG (gráficos vectoriales), aunque se deja abierto a poder interpretar otros lenguajes XML; y funcionalidades "Drag & Drop" para arrastrar objetos.

Entre sus novedades, lo que para mi gusto más se destaca son las etiquetas <canvas>. Utilizando canvas es posible crear gráficos 2D y 3D a partir de código JavaScript. Esta funcionalidad permite reemplazar imágenes PNG o JPG por gráficos y dibujos creados en tiempo de ejecución. Esto es, desde PHP (o cualquier otro lenguaje CGI en el servidor Web) es posible crear código JavaScript que será ejecutado en el cliente (Web browser) para crear un gráfico dinámico. A su vez, este código JavaScript para dibujar el gráfico puede ser alterado para modificar el gráfico ante un determinado evento, sin necesidad de recargar la página.

Una ventaja adicional que tienen las etiquetas canvas, es que reducen el tamaño en disco y tiempo de descarga de las páginas Web. Esto se debe a que el código fuente (PHP/JavaScript) es texto plano y ocupa mucho menor espacio que las imágenes, lo cual mejora notablemente los tiempos de transferencia (a cambio de un mayor costo de ejecución en el Web browser cliente, ya que debe encargarse del proceso de renderizado o dibujado).

Manos a la obra

Mas allá de sus ventajas y desventajas, vale la pena probarlo, y no hay mejor forma de aprender que a través de un ejemplo.

Para ello voy a plantear el problema de dibujar la siguiente serie de datos (tal como lo haríamos utilizando Calc/Excel):

ClaveValor
5121
13150
20144
2797
36108

En el eje de las abcisas se ubican las claves y en las ordenadas el valor de cada una. El código HTML de ésta tabla es el siguiente:

<table id="datos" border="1">
<tr><th>Clave</th><th>Valor</th></tr>
<tr><td>5</td><td>121</td></tr>
<tr><td>13</td><td>150</td></tr>
<tr><td>20</td><td>144</td></tr>
<tr><td>27</td><td>97</td></tr>
<tr><td>36</td><td>108</td></tr>
</table>

Trabajando con canvas

Para crear gráficos o dibujos en dos dimensiones, podemos pensar un canvas como un lienzo formado por píxeles. Su dimensión se define mediante un ancho (width) y alto (height) que se mide en píxeles, por ejemplo:

<canvas id="lienzo" width="500" height="250">
Su navegador no permite utilizar canvas.
</canvas>

El texto encerrado entre las etiquetas <canvas> y </canvas> se utiliza para mostrar un mensaje en caso de que el browser cliente no soporte el uso de etiquetas canvas.

Las coordenadas del lienzo incrementan desde izquierda hacia derecha en dirección horizontal y desde arriba hacia abajo en dirección vertical, tal como se observa en la siguiente figura:

Luego de definir el lienzo, se debe crear el dibujo mediante el siguiente código JavaScript:

<script type="text/javascript">

function retornarLienzo(x) {
  var canvas = document.getElementById(x);
  if (canvas.getContext) {
    var lienzo = canvas.getContext("2d");   
    return lienzo;
  } else
    return false;
}

function dibujar() {

  var lienzo=retornarLienzo("lienzo");
  if (lienzo) {

    // Defino límites en píxeles
    var minx = 50;
    var maxx = 450;
    var miny = 30;
    var maxy = 170;

    // Dimensiones para el gráfico
    var ancho = maxx - minx;
    var alto = maxy - miny;

    // Obtener valores mínimo y máximo
    var minv = "nulo";
    var maxv = 0;

    var valor = 0;

    // Recorrer la tabla para obtener los valores mínimo y máximo
    var tabla = document.getElementById("datos");
    for (var i=1, fila; fila=tabla.rows[i]; i++) {
        valor = parseFloat(fila.cells[1].innerHTML);
        if (minv == "nulo" || minv > valor) {
            minv = valor;
        }
        if (maxv < valor) {
            maxv = valor;
        }
    }

    // Escala
    /*
        Se utiliza para adaptar los valores de la tabla
        en las dimensiones del lienzo 
    */
    var escala = maxv - minv;
    
    // Cantidad de filas
    var cant = i;

    // Dibujar abcisas
    lienzo.beginPath();
    lienzo.font = '10px Calibri';
    lienzo.lineWidth = 1;
    lienzo.fillStyle = 'blue';
    x = minx;
    for (var i=1, row; row=tabla.rows[i]; i++) {    
        lienzo.fillText(row.cells[0].innerHTML, x, maxy+40);
        x += ancho/(cant-2); // avanzo las abcisas
    }
    lienzo.stroke();

    // Dibujar ordenadas
    lienzo.beginPath();
    lienzo.font = '10px Calibri';
    lienzo.lineWidth = 1;
    lienzo.fillStyle = 'blue';
    for (var i=1, row; row=tabla.rows[i]; i++) { 
        valor = parseFloat(row.cells[1].innerHTML); // obtengo el siguiente valor
        y = maxy - (valor-minv) * (alto/escala); // calculo el siguiente punto   
        lienzo.fillText(row.cells[1].innerHTML, minx-20, y);
    }
    lienzo.stroke();
    lienzo.beginPath();
    lienzo.lineWidth = 1;
    lienzo.setLineDash([5,2]);
    lienzo.strokeStyle="rgb(230,230,180)";
    for (var i=1, row; row=tabla.rows[i]; i++) { 
        valor = parseFloat(row.cells[1].innerHTML); // obtengo el siguiente valor
        y = maxy - (valor-minv) * (alto/escala); // calculo el siguiente punto   
        lienzo.moveTo(minx,y);
        lienzo.lineTo(maxx,y);
    }
    lienzo.stroke();

    // Dibujar recuadro
    lienzo.beginPath();
    lienzo.strokeStyle="rgb(180,180,180)";
    lienzo.setLineDash([0,0]);
    lienzo.lineWidth=1;
    lienzo.strokeRect(minx,miny-20,maxx-minx,maxy-miny+40);

    // Dibujar líneas
    lienzo.strokeStyle="rgb(100,100,100)";

    // Calculo el primer valor
    valor = parseFloat(tabla.rows[1].cells[1].innerHTML);
    var x = minx;
    var y = maxy - (valor-minv) * (alto/escala);
    // Dibujo el primer valor (punto)
    lienzo.fillRect(x-2,y-2,4,4);
    lienzo.moveTo(x,y);

    // Desde el segundo hasta el último valor
    for (var i=2, row; row=tabla.rows[i]; i++) {
        x += ancho/(cant-2); // avanzo las abcisas
        valor = parseFloat(row.cells[1].innerHTML); // obtengo el siguiente valor
        y = maxy - (valor-minv) * (alto/escala); // calculo el siguiente punto
        lienzo.lineTo(x,y); // dibujo una línea entre ambos puntos
        lienzo.fillRect(x-2,y-2,4,4); // dibujo el nuevo punto
    }
    lienzo.stroke();

  }
}

dibujar();

</script>

La función retornarLienzo prepara el lienzo para crear un dibujo 2D. Cada trazo dentro del lienzo comienza con una llamada al método beginPath() y se aplica una vez que se llama al método strokePath(). Un trazo puede ser por ejemplo una línea, una figura (rectángulos, círculos, etc.), texto, etc. Como se observa en el código, la figura se elabora con diferentes trazos.

El resultado es el siguiente gráfico:

Su navegador no permite utilizar canvas.

En el sitio de desarrollo de Mozilla y en el sitio oficial del consorcio W3C se encuentran tutoriales completos sobre canvas:

Canvas tutorial

HTML Canvas 2D Context, Level 2 Nightly

Ambos tutoriales son excelentes, aunque se encuentran en idioma inglés. Espero que este artículo al menos les sirva como introducción al uso y funcionamiento básico de las etiquetas <canvas> de HTML5.

Para finalizar dejo el mismo dibujo del ejemplo, pero esta vez utilizando un gráfico de barras en lugar de una curva, con unos leves cambios en el código JavaScript anterior:

<p>
<center>
<canvas id="lienzo2" width="500" height="250">
Su navegador no permite utilizar canvas.
</canvas>
</center>
</p>

<script type="text/javascript">

function retornarLienzo2(x) {
  var canvas = document.getElementById(x);
  if (canvas.getContext) {
    var lienzo = canvas.getContext("2d");   
    return lienzo;
  } else
    return false;
}

function dibujar2() {

  var lienzo=retornarLienzo2("lienzo2");
  if (lienzo) {

    // Defino límites en píxeles
    var minx = 50;
    var maxx = 450;
    var miny = 30;
    var maxy = 170;

    // Dimensiones para el gráfico
    var ancho = maxx - minx;
    var alto = maxy - miny;

    // Obtener valores mínimo y máximo
    var minv = "nulo";
    var maxv = 0;

    var valor = 0;

    // Recorrer la tabla para obtener los valores mínimo y máximo
    var tabla = document.getElementById("datos");
    for (var i=1, fila; fila=tabla.rows[i]; i++) {
        valor = parseFloat(fila.cells[1].innerHTML);
        if (minv == "nulo" || minv > valor) {
            minv = valor;
        }
        if (maxv < valor) {
            maxv = valor;
        }
    }

    // Escala
    /*
        Se utiliza para adaptar los valores de la tabla
        en las dimensiones del lienzo 
    */
    var escala = maxv - minv;
    
    // Cantidad de filas
    var cant = i;

    // Dibujar abcisas
    lienzo.beginPath();
    lienzo.font = '10px Calibri';
    lienzo.lineWidth = 1;
    lienzo.fillStyle = 'blue';
    x = minx;
    for (var i=1, row; row=tabla.rows[i]; i++) {    
        lienzo.fillText(row.cells[0].innerHTML, x+10, maxy+40);
        x += ((ancho-30)/(cant-2)); // avanzo las abcisas
    }
    lienzo.stroke();

    // Dibujar ordenadas
    lienzo.beginPath();
    lienzo.font = '10px Calibri';
    lienzo.lineWidth = 1;
    lienzo.fillStyle = 'blue';
    for (var i=1, row; row=tabla.rows[i]; i++) { 
        valor = parseFloat(row.cells[1].innerHTML); // obtengo el siguiente valor
        y = maxy - (valor-minv) * (alto/escala); // calculo el siguiente punto   
        lienzo.fillText(row.cells[1].innerHTML, minx-20, y);
    }
    lienzo.stroke();
    lienzo.beginPath();
    lienzo.lineWidth = 1;
    lienzo.setLineDash([5,2]);
    lienzo.strokeStyle="rgb(230,230,180)";
    for (var i=1, row; row=tabla.rows[i]; i++) { 
        valor = parseFloat(row.cells[1].innerHTML); // obtengo el siguiente valor
        y = maxy - (valor-minv) * (alto/escala); // calculo el siguiente punto   
        lienzo.moveTo(minx,y);
        lienzo.lineTo(maxx,y);
    }
    lienzo.stroke();

    // Dibujar recuadro
    lienzo.beginPath();
    lienzo.strokeStyle="rgb(180,180,180)";
    lienzo.setLineDash([0,0]);
    lienzo.lineWidth=1;
    lienzo.strokeRect(minx,miny-20,maxx-minx,maxy-miny+40);

    // Dibujar barras
    lienzo.fillStyle="rgb(75,85,220)";

    // Calculo el primer valor
    valor = parseFloat(tabla.rows[1].cells[1].innerHTML);
    var x = minx;
    var y = maxy - (valor-minv) * (alto/escala);
    // Dibujo la primera barra
    lienzo.fillRect(x,y,30,maxy-y+20);

    // Desde el segundo hasta el último valor
    for (var i=2, row; row=tabla.rows[i]; i++) {
        x += ((ancho-30)/(cant-2)); // avanzo las abcisas
        valor = parseFloat(row.cells[1].innerHTML); // obtengo el siguiente valor
        y = maxy - (valor-minv) * (alto/escala); // calculo el siguiente punto
        lienzo.fillRect(x,y,30,maxy-y+20); // dibujo la siguiente barra
    }
    lienzo.stroke();

  }
}

dibujar2();

</script>

Su navegador no permite utilizar canvas.


Tal vez pueda interesarte


Compartí este artículo