Plataforma

Métodos de dibujo mediante Canvas HTML5. Introducción y ejemplo de gráfica cartesiana con jQuery.

Métodos de dibujo mediante Canvas HTML5. Introducción y ejemplo de gráfica cartesiana con jQuery.

HTML5 nos ofrece el elemento canvas, el cual nos sirve como lienzo para dibujar gráficos en tiempo real mediante scripts, generando imágenes dinámicas en el área del elemento. Canvas tiene infinidad de aplicaciones y puede resultarnos muy útil para mostrar gráficas a través de los datos obtenidos en nuestras aplicaciones, como pueden ser informes contables, datos estadísticos, relaciones de indicadores, variables o cualquier otro tipo de información que podamos representar visualmente.

Para utilizar canvas solo es necesario incluir en nuestro documento HTML una etiqueta similar a la de un div, en la cual hay que especificar una anchura “width” y una altura “height” (por defecto, estas dimensiones son de 300px de anchura y 150px de altura).

HTML:

<canvas id="grafica" width="300" height="150">Tu navegador no soporta CANVAS HTML5.</canvas>

Hemos incluido un id=”grafica” que nos servirá para identificar el elemento, y entre las etiquetas de apertura y cierre la frase “Tu navegador no soporta CANVAS HTML5“. Esto es lo que verá el usuario cuyo navegador no sea compatible con canvas. El hecho de tener que especificar las dimensiones directamente sobre el propio elemento, puede dificultarnos su adaptación en desarrollos “responsive“, así que para hacer algo diferente a los múltiples ejemplos que podéis encontrar por la red, en este artículo vamos a generar una gráfica cartesiana adaptable al tamaño del navegador y a generarlo mediante jQuery en vez de utilizar javascript directamente, representando supuestos valores de ingresos y gastos de una empresa.

Inicializando el gráfico.

Más allá del resto de estilos que utilicemos en nuestro documento, vamos a aplicar diferentes dimensiones al canvas de nuestro documento mediante media queries, dependiendo de los diferentes tamaños que puede tener nuestro navegador/dispositivo. Además también le aplicamos un color de fondo y lo centramos en nuestro documento:

CSS:

#grafica{
    width:300px;
    height: 400px;
    background-color: #f6f6f6;
    display: block;
    margin:0px auto;
}
@media screen and (min-width: 740px) {
    #grafica{ width:640px; }
}
@media screen and (min-width: 1000px) {
    #grafica{ width:920px; }
}
@media screen and (min-width: 1260px) {
    #grafica{ width:1120px;  height:500px; }
}
@media screen and (min-width: 1580px) {
    #grafica{ width:1320px; }
}

Es importante destacar que nuestro gráfico se construye con las dimensiones especificadas en los atributos “width” y “height” implícitos en la etiqueta canvas (en este caso width=”300″ y height=”150″), por lo que estas dimensiones especificadas en los estilos solo sirven para que se adapte el tamaño del canvas pero no el gráfico contenido. También es importante destacar que cuando adaptamos el canvas las dimensiones del gráfico se modifican para ocupar lo mismo, por lo que se deforma al dejar de mantener la relación de aspecto. Para que esto no suceda, lo que haremos será generar el gráfico cada vez que cambian las dimensiones de nuestro canvas. Esto nos servirá para que nuestro gráfico siempre se visualice correctamente y también para comprobar que la respuesta y generación del gráfico es inmediata.

Pasamos a jQuery y a realizar la comprobación de tamaño de nuestro canvas. Para ello, guardamos en la variable “w_adapt” su tamaño de anchura y mediante la función resize() (que se ejecutará cada vez que se modifiquen las dimensiones del navegador), comprobamos si sus dimensiones son diferentes (en este caso con comprobar la anchura es suficiente). Si ha cambiado, llamamos a la función draw_graph() para que genere de nuevo el gráfico, y guardamos el nuevo valor de anchura de nuestro canvas para futuras comprobaciones:

Js/jQuery:

var w_adapt=$("#grafica").width();
$(window).resize(function() {
    if(w_adapt!=$("#grafica").width()){
        w_adapt=$("#grafica").width();
        draw_graph();
    }
});

Ahora vamos a lo que nos interesa, dibujar en nuestro canvas. Dentro de nuestra función draw_graph(), llamaremos al método de dibujo referenciando el objeto canvas al contexto en el que vamos a utilizarlo, en este caso un plano en dos dimensiones. De esta forma tendremos acceso a todas las funciones de dibujo y el “lienzo” queda preparado. Existen otros métodos de dibujo que podéis comprobar en el siguiente enlace: getContext()

Nota: El condicional “if” se coloca para comprobar si nuestro navegador soporta este método. Todas las instrucciones de dibujo las colocaremos dentro de este condicional. Es importante recalcar que mediante jQuery debemos hacer referencia al elemento del DOM mediante índice numérico.

Js/jQuery:

function draw_graph(){
    if ($("#grafica")[0].getContext) { // Si el navegador lo soporta
        var c_graph = $("#grafica")[0].getContext("2d"); // Llamada al método de dibujo
    }
}

Nuestro canvas ya está preparado y podemos comenzar a dibujar en él. Las instrucciones para dibujar en canvas por lo general hacen referencia a formas geométricas rectangulares y circulares (rellenas o trazos), y líneas rectas o curvas cerradas/abiertas. A continuación vemos algunas instrucciones básicas:

  • fillRect(x, y, width, height)
    Dibuja un rectángulo relleno desde la posición especificada de nuestro canvas y con las dimensiones establecidas.
  • strokeRect(x, y, width, height)
    Dibuja el borde de un rectángulo desde la posición especificada de nuestro canvas y con las dimensiones establecidas.
  • moveTo(x, y)
    Mueve el cursor/puntero de dibujo a un punto determinado de nuestro canvas.
  • lineTo(100, 75);
    Dibuja una línea hasta el punto especificado de nuestro canvas.

Podéis consultar múltiples instrucciones para dibujar en canvas en el enlace Dibujando formas con canvas, pero nosotros vamos a centrarnos en la construcción de nuestro ejemplo, la construcción de una gráfica de ingresos y gastos. Para ello, primero vamos a crear variables que nos servirán de apoyo para la construcción del gráfico. Primero obtenemos las dimensiones de nuestro canvas y se las asignamos a sus propios atributos “width” y “height”, ya que el gráfico toma en cuenta estos atributos para sus dimensiones:

Js/jQuery:

var w_graph = $("#grafica").width(); // Ancho del canvas
var h_graph = $("#grafica").height(); // Alto del canvas
$("#grafica").attr("width",$("#grafica").width()); // Aplico la anchura del canvas a su atributo width
$("#grafica").attr("height",$("#grafica").height()); // Aplico la altura del canvas a su atributo width

En el eje X de la gráfica representaremos los meses del año, y en el eje Y valores de 0 a 10000 (euros, dólares o cualquier otra divisa de ejemplo). Por otro lado, obtenemos los valores de gastos e ingresos. Para nuestro ejemplo creamos arrays en los que colocamos todos estos datos. Utilizaremos la variable “show_values” para mostrar u ocultar los valores de las gráficas de gastos e ingresos si la resolución es de 300 píxeles de anchura, y mostraremos los textos de los meses del año completos si la resolución es mayor a 640 píxeles de anchura. Si es igual o inferior, mostraremos solamente los tres primeros caracteres del mes:

Js/jQuery:

var valores = [0, 2500, 5000, 7500, 10000]; // Valores eje Y
var gastos = [5000, 2500, 3690, 7500, 10000, 8888, 7500, 5000, 6000, 3000, 8000, 9600];
var ingresos = [7500, 7000, 5000, 3400, 6000, 8200, 9000, 7700, 9600, 6000, 10000, 8500];

var show_values=true;
if(w_graph<=640){
    var meses = ["Ene", "Feb", "Mar", "Abr", "May", "Jun", "Jul", "Ago", "Sep", "Oct", "Nov", "Dic"];
    if(w_graph==300){
        var show_values=false;
    }
}else{
    var meses = ["Enero", "Febrero", "Marzo", "Abril", "Mayo", "Junio", "Julio", "Agosto", "Septiembre", "Octubre", "Noviembre", "Diciembre"];
}

Otros datos que guardaremos en variables para la generación de nuestra gráfica serán la cantidad de meses a representar, la cantidad de valores del eje Y, la medida de separación entre los meses y los valores según la anchura/altura de nuestro canvas y el valor máximo que puede llegar a tener nuestro eje Y:

Js/jQuery:

var months = meses.length; // Cantidad de meses
var amounts = valores.length; // Cantidad de valores
var tam_w=w_graph/months; //Medida de separación de meses
var tam_h=h_graph/amounts; // Medida de separación de cantidades
var amounts_max_value=10000;

Primeros trazos de dibujo en canvas.

Ya está todo preparado y ahora vamos a dibujar nuestro gráfico. Lo primero que haremos será borrar el área de dibujo por si ya hemos dibujado anteriormente mediante clearRect(). Similar a los datos requeridos por fillRect() para dibujar un área rellena, debemos especificar tanto el punto de partida en el eje X e Y como el tamaño del área a borrar:

clearRect(x, y, width, height);

En nuestro caso, hacemos referencia a nuestro objeto “c_graph” y aplicamos los siguientes valores:

c_graph.clearRect(0, 0, w_graph, h_graph);

Esta instrucción borra todo el área de dimensiones “w_graph” y “h_graph” comprendida entre el punto “cero” (0) del eje X y el punto “cero” (0) del eje Y, es decir, todo el área de nuestro canvas. A continuación vamos a dibujar las líneas de fondo para nuestra gráfica cartesiana, líneas horizontales y verticales distribuidas en relación a los datos obtenidos (en este caso, contenidos en nuestros arrays), pero primero establecemos una anchura y color de línea a los trazos de dibujo que vamos a generar, también la alineación izquierda de los textos a crear y luego llamamos a las funciones que dibujarán dichas líneas. Primero vamos a dibujar las líneas horizontales mediante draw_horizontal_lines():

Js/jQuery:

c_graph.lineWidth = 1;
c_graph.strokeStyle = "#cccccc";
c_graph.textAlign="left";
draw_horizontal_lines();
function draw_horizontal_lines(){
    for (i=0; i<amounts; i++){
        c_graph.beginPath();
        c_graph.moveTo(0, h_graph-(tam_h*i));
        c_graph.lineTo(w_graph, h_graph-(tam_h*i));
        c_graph.stroke();
        c_graph.textAlign="left";
        if (i!=amounts){
            c_graph.fillText(valores[i], 0, h_graph-(tam_h*i)-4);
        }
    }
}

Vamos a repasar la función draw_horizontal_lines() paso a paso para comprender su funcionamiento. Para crear nuestro gráfico hay que tener en cuenta que el eje X tanto en canvas como en el gráfico a dibujar comienza en el mismo lugar (parte inferior izquierda), pero sin embargo el eje Y en nuestro gráfico comenzará en la parte inferior izquierda y sin embargo en nuestro documento comienza en la parte superior izquierda del canvas. Debido a esta diferencia, debemos tener en cuenta que nuestras medidas en el eje Y deben calcularse en orden inverso. Existen otras instrucciones que nos permiten cambiar la orientación de los ejes, pero en este caso no vamos a utilizarlas.

Canvas HTML5 - Orientación de los ejes

Primero creamos un bucle que dibujará tantas líneas horizontales como valores existan en el array “valores” (ya que anteriormente guardamos este dato mediante var amounts = valores.length;) Comenzamos creando una nueva “trayectoria” o “ruta” mediante beginPath(). A continuación, mediante moveTo() colocamos el puntero o cursor de dibujo en el valor “cero” (0) del eje X y calculamos la posición a la que colocamos el puntero en el eje Y mediante:

h_graph-(tam_h*i); Altura del gráfico – (medida de separación de cantidades * i);

No es necesario incluir moveTo() en el bucle y estar posicionando el puntero cada vez que trazamos una linea, ya que todas parten desde su última posición, pero para el ejemplo nos viene bien para comprender cómo se realizan los trazos y desde donde parten. Seguidamente, trazamos una línea mediante lineTo(), pero ésta no se dibuja hasta que ejecutamos stroke(); Como podemos ver, en el eje Y no realizamos modificaciones respecto a la sentencia anterior, ya que para hacer una línea horizontal realizamos el trazo en el eje X. Tras stroke(), la línea se dibuja en nuestro canvas. A continuación dibujamos (excepto en el valor “cero” del eje Y para que no se superponga con el texto que colocaremos en horizontal) los números del valor al que corresponde cada línea con fillText(), que dibuja un texto en la posición X e Y (opcionalmente con un ancho máximo):

fillText(text, x, y [, maxWidth]);

En nuestro caso: c_graph.fillText(valores[i], 0, h_graph-(tam_h*i)-4); Le restamos 4 para dejar un pequeño espacio de separación entre el texto y la línea dibujada.

EJEMPLO 1:

Tras dibujar las líneas horizontales, dibujamos las líneas verticales. Se puede ver que el código es similar, excepto que en este caso obtenemos los valores de los meses mediante la función draw_vertical_lines() y que colocamos el texto en todos los trazos:

Js/jQuery:

draw_vertical_lines();
function draw_vertical_lines(){
    for (i=0; i<months; i++){
        c_graph.beginPath();
        c_graph.moveTo(tam_w*i, 0);
        c_graph.lineTo(tam_w*i, h_graph);
        c_graph.stroke();
        c_graph.fillText(meses[i], (tam_w*i)+4, h_graph-4);
    }
}
EJEMPLO 2:

Dibujando los valores en nuestra gráfica.

Ya tenemos el fondo de nuestra gráfica cartesiana de ingresos y gastos preparado. Ahora vamos a pasar a dibujar los resultados. Llamaremos a la función draw_expenses() para dibujar primero las cantidades de los gastos, pero antes cambiamos el estilo del trazo aumentando su grosor y color. También asignamos un estilo de “final de línea” o unión mediante lineCap(), que permite tres valores:

  • butt: Los extremos de las líneas acaban de forma exacta en los puntos finales.
  • round: Los extremos de las líneas se redondean.
  • square: Los extremos de las líneas terminan añadiendo un sobrante con un ancho igual a la mitad de la altura del grosor de la línea.

Js/jQuery:

c_graph.lineWidth = 4;
c_graph.lineCap = "round";
c_graph.strokeStyle = "rgb(244,64,128)";
draw_expenses();
function draw_expenses(){
    for (i=0; i<gastos.length; i++){
        c_graph.beginPath();
        c_graph.moveTo(w_graph/months*i, h_graph-(gastos[i]*(h_graph-tam_h)/amounts_max_value));
        c_graph.lineTo(w_graph/months*(i+1), h_graph-(gastos[i+1]*(h_graph-tam_h)/amounts_max_value));
        c_graph.stroke();
        c_graph.closePath();

        c_graph.beginPath();
        c_graph.arc(w_graph/months*(i+1), h_graph-(gastos[i+1]*(h_graph-tam_h)/amounts_max_value), 6, 0, 2*Math.PI);
        c_graph.fillStyle = '#2b1861';
        c_graph.fill();
	if (show_values==true){
            c_graph.textAlign="end";
            c_graph.fillText(gastos[i+1], (w_graph/months*(i+1))-8, h_graph-(gastos[i+1]*(h_graph-tam_h)/amounts_max_value)+4);
        }
        c_graph.closePath();
    }
}

La primera parte del código es similar a lo que ya hemos visto anteriormente para dibujar el fondo del gráfico. Creamos un nuevo beginPath() por cada trazo y calculamos su posición extrayendo los datos del array “gastos”. Utilizamos closePath() para cerrar el “trayecto” o “ruta” de cada trazo y comenzar una nueva. Tras esto, generamos un punto circular mediante la instrucción arc():

arc(x, y, radius, startAngle, endAngle, anticlockwise);

c_graph.arc(w_graph/months*(i+1), h_graph-(gastos[i+1]*(h_graph-tam_h)/amounts_max_value), 6, 0, 2*Math.PI);

Los puntos X e Y ya los tenemos calculados al haber generado el trazo anterior, le colocamos una anchura de radio de 6 píxeles, el ángulo comienza en “cero” (calculado en radianes) y termina en 2*Pi radianes, que sería una circunferencia completa. Por defecto, el círculo se genera en el sentido de las agujas del reloj, pero podemos hacer que se genere en sentido inverso mediante “anticlockwise” (true o false). Para aquellos que tengáis lagunas en geometría (como un servidor), os recuerdo que podéis calcular las circunferencias en javascript de la siguiente forma:

  • 0.5*Math.PI -> ¼ circunferencia.
  • Math.PI -> semicircunferencia.
  • 1.5* Math.PI -> ¾ circunferencia.
  • 2*Math.PI -> circunferencia completa.

Tras generar el arco, solo debemos rellenarlo de color mediante fill(), aunque antes le aplicamos un color de relleno mediante fillStyle:

c_graph.fillStyle = ‘#2b1861’;
c_graph.fill();

Para finalizar, alineamos el texto de los valores a presentar mediante textAlign=”end” (que nos coloca el texto a la derecha del punto) y para ajustar la posición con una correcta visualización le restamos 8 en su eje X y sumamos 4 en su eje Y (esto no es necesario, es solo para separar y centrar en altura ligeramente el texto respecto al punto). Como comentábamos anteriormente, estos datos numéricos solo se dibujarán si la resolución es mayor a 300 píxeles de anchura, es decir, si show_values es igual a “true“.

EJEMPLO 3:

Para finalizar nuestra gráfica solo nos queda mostrar los datos correspondientes a los ingresos. Para ello utilizamos un código similar al anterior, leyendo de su correspondiente array. En este caso aplicamos un color diferente al trazo modificando ligeramente su opacidad para que permita ver los cruces de línea con la gráfica de gastos.

Js/jQuery:

c_graph.strokeStyle = "rgba(90,181,29,0.7)";
draw_income();
function draw_income(){
    for (i=0; i<ingresos.length; i++){
        c_graph.beginPath();
        c_graph.moveTo(w_graph/months*i, h_graph-(ingresos[i]*(h_graph-tam_h)/amounts_max_value));
        c_graph.lineTo(w_graph/months*(i+1), h_graph-(ingresos[i+1]*(h_graph-tam_h)/amounts_max_value));
        c_graph.stroke();
        c_graph.closePath();

        c_graph.beginPath();
        c_graph.arc(w_graph/months*(i+1), h_graph-(ingresos[i+1]*(h_graph-tam_h)/amounts_max_value), 6, 0, 2*Math.PI);
        c_graph.fillStyle = '#2b1861';
        c_graph.fill();
        if (show_values==true){
            c_graph.textAlign="end";
            c_graph.fillText(ingresos[i+1], (w_graph/months*(i+1))-8, h_graph-(ingresos[i+1]*(h_graph-tam_h)/amounts_max_value)+4);
        }
        c_graph.closePath();
    }
}
EJEMPLO 4:

Con este ejemplo podemos hacernos una idea de las posibilidades de canvas y sus implementaciones. Además aquí hemos realizado un breve repaso de algunas de sus funciones y características más comunes, pero existen muchísimas funciones para la generación de imágenes y gráficos mediante este elemento (variación de la escala, curvas bezier, transformaciones, composición, animaciones, etc.). Espero que os sirva de ayuda y sea útil. ¡Saludos! 😉

Valora el artículo:

1 Estrella2 Estrellas3 Estrellas4 Estrellas5 Estrellas (4 valoraciones, media: 4,75 sobre 5)
Cargando...
Avatar photo Juan C. Martínez Director de Arte Ver más artículos de Juan C. Martínez

Otros artículos de la categoría Diseño