Cómo Dibujar Gráficas Usando JavaScript y Canvas HTML5
() translation by (you can also view the original English article)



En este tutorial te mostraré cómo usar JavaScript y el canvas como medios para mostrar información numérica en la forma de gráficas de pastel y gráficas de dona.
Hay maneras más fáciles de crear gráficas que codificar una desde cero, por ejemplo esta librería completa de graficación de CodeCanyon.



Pero si quieres saber que sucede tras bambalinas en una librería como esta, sigue leyendo.
¿Qué Es una Gráfica de Pastel?
Una gráfica es una herramienta estadística usada para representar gráficamente datos numéricos. Una gráfica de pastel muestra datos numéricos como un círculo dividido en rebanadas. EL tamaño de cada rebanada es proporcional al valor numérico que representa.
¿Qué Es una Gráfica de Dona?
Para ponerlo en términos simples, una gráfica de dona es una variación de la gráfica de pastel. La diferencia es que las rebanadas son cortadas en el centro del pastel de manera que solo el borde es visible. De esta manera, la gráfica se ve como una dona y de ahí el nombre.
Comenzando a Dibujar con el Canvas
Antes de dibujar la gráfica de pastel, echaremos un vistazo a dibujar sus partes. Veremos cómo podemos usar el componente canvas y JavaScript para dibujar:
- un línea
- un arco (una parte de un círculo)
- una figura con relleno de color
Para comenzar usando el canvas de HTML5, necesitaremos crear unas cuantas cosas:
- Una carpeta para contener los archivos del proyecto; llamemos a este folder
piechart-tutorial
. - Un archivo HTML
index.html
dentro de la carpetapiechart-tutorial
. Este archivo contendrá el código HTML. - Un archivo JS
script.js
dentro de la carpetapiechart-tutorial
. Este archivo contendrá nuestro código JavaScript.
Mantendremos las cosas bastante simples y agregaremos el siguiente código dentro de index.html
:
1 |
<html>
|
2 |
|
3 |
<body>
|
4 |
|
5 |
<canvas id="myCanvas"></canvas> |
6 |
|
7 |
<script type="text/javascript" src="script.js"></script> |
8 |
|
9 |
</body>
|
10 |
|
11 |
</html>
|
Tenemos el elemento <canvas>
con el ID myCanvas
para que podamos referenciarlo en nuestro código JS. Entonces podemos cargar nuestro código JS por medio de la etiqueta <script>
.
Dentro de script.js
, el código JS primero obtendrá una referencia al canvas y después establecerá su anchura y altura. Para dibujar en el canvas, solo necesitamos una referencia a su context 2D el cuál contiene todos los métodos de dibujo.
1 |
var myCanvas = document.getElementById("myCanvas"); |
2 |
|
3 |
myCanvas.width = 300; |
4 |
|
5 |
myCanvas.height = 300; |
6 |
|
7 |
|
8 |
|
9 |
var ctx = myCanvas.getContext("2d"); |
Ahora que tenemos el canvas configurado y también una referencia al canvas de dibujo, definamos unas cuantas fuciones JavaScript que podremos reusar cuando dibujemos la gráfica de pastel. Agregaremos las funciones en nuestro archivo script.js.
1 |
function drawLine(ctx, startX, startY, endX, endY){ |
2 |
|
3 |
ctx.beginPath(); |
4 |
|
5 |
ctx.moveTo(startX,startY); |
6 |
|
7 |
ctx.lineTo(endX,endY); |
8 |
|
9 |
ctx.stroke(); |
10 |
|
11 |
}
|
La función drawLine
toma cinco parámetros:
-
ctx
: referencia al contexto de dibujo -
startX
: la coordenada X de la línea de punto de inicio -
startY
: la coordenada Y de la línea de punto de inicio
-
endX
: la coordenada X de la línea de punto de inicio
-
endY
: la coordenada Y de la línea de punto de inicio
Comenzamos dibujando la línea llamando beginPath()
. Esto informa al contexto de dibujo que estamos comenzando a dibujar algo nuevo en el canvas. Usamos moveTo()
para establecer el punto de partida, llmamos lineTo()
para indicar el punto final y después hacemos el dibujo en cuestión llamando a stroke()
.
Veamos ahora cómo podemos dibujar una parte de un círculo, también llamado arco.
1 |
function drawArc(ctx, centerX, centerY, radius, startAngle, endAngle){ |
2 |
|
3 |
ctx.beginPath(); |
4 |
|
5 |
ctx.arc(centerX, centerY, radius, startAngle, endAngle); |
6 |
|
7 |
ctx.stroke(); |
8 |
|
9 |
}
|
La función drawArc
toma seis parámetros:
-
ctx
: referencia al contexto del dibujo -
centerX
: la coordenada X del centro del círculo -
centerY
: la coordenada Y del centro del círculo -
radio
: la coordenada X de la línea del punto final -
startAngle
: el ángulo inicial en radianes en donde comienza la porción del círculo -
endAngle
: el ángulo final en radianes en donde termina la porción del círculo
Hemos visto como dibujar una línea y cómo dibujar un arco, así que veamos ahora cómo dibujar una figura coloreada. Ya que nuestra meta es dibujar una gráfica de pastel que está hecha de rebanadas, creemos una función que dibuja una rebanada del pastel.
1 |
function drawPieSlice(ctx,centerX, centerY, radius, startAngle, endAngle, color ){ |
2 |
|
3 |
ctx.fillStyle = color; |
4 |
|
5 |
ctx.beginPath(); |
6 |
|
7 |
ctx.moveTo(centerX,centerY); |
8 |
|
9 |
ctx.arc(centerX, centerY, radius, startAngle, endAngle); |
10 |
|
11 |
ctx.closePath(); |
12 |
|
13 |
ctx.fill(); |
14 |
|
15 |
}
|
La función dwarPieSlice
toma siete parámetros:
-
ctx
: referencia al contexto del dibujo -
centerX
: la coordenada X del centro del círculo -
centerY
: la coordenada Y del centro del círculo -
radio
: la coordenada X de la línea del punto final -
startAngle
: el ángulo inicial en radianes en donde comienza la porción del círculo -
endAngle
: el ángulo final en radianes en donde termina la porción final del círculo -
color
: el color usado para rellenar la rebanada
Aquí hay un ejemplo para llamar tres funciones:
1 |
drawLine(_ctx,100,100,200,200); |
2 |
|
3 |
drawArc(_ctx, 150,150,150, 0, Math.PI/3); |
4 |
|
5 |
drawPieSlice(_ctx, 150,150,150, Math.PI/2, Math.PI/2 + Math.PI/4, '#ff0000'); |
Producirá el resultado:



Ahora tenemos todas las herramientas necesarias para dibujar la gráfica de pastel, así que veamos como usarlas juntas.
Dibujando la Gráfica de Pastel
Conceptualmente, cualquier gráfica tiene dos partes principales:
- El modelo de datosc contiene la información numérica a ser representada. Esta está estructurada en un formato específico al tipo de gráfica.
- La representación gráfica es cómo la información numérica en el modelo de datos es representada por elementos visuales de acuerdo a ciertas reglas en la forma de fórmulas matemáticas.
El Modelo de Datos de Gráfica de Pastel
La forma más común de estructurar modelos de datos para gráficas de pastel es una serie de categorías y valores correspondientes, en donde cada una de las categorías y valores son asociados a una rebanada del pastel.
Como ejemplo, el modelo de datos de una gráfica de pastel que muestra el número de vinilos que tengo agrupados por género se vería como:
- Música clásica: 10
- Rock alternativo: 14
- Pop: 2
- Jazz: 12
Podemos agregar un objeto de JS al archivo script.js
para almacenar el modelo de datos como esto:
1 |
var myVinyls = { |
2 |
|
3 |
"Classical music": 10, |
4 |
|
5 |
"Alternative rock": 14, |
6 |
|
7 |
"Pop": 2, |
8 |
|
9 |
"Jazz": 12 |
10 |
|
11 |
};
|
La Representación Gráfica de Gráfica de Pastel
La gráfica de pastel usa un círculo para mostrar la información en el modelo de datos dividiéndolo en rebanadas. Cada rebanada corresponde a una categoría del modelo de datos y el tamaño de la rebanada es proporcional al valor de la categoría.
Mi pequeña colección de 38 vinilos tiene cuatro categorías. Cada categoría tendrá una rebanada de la gráfica de pastel proporcional al número de vinilos en esa categoría.
¿Pero cómo medimos el tamaño de la rebanada? Es sencillo--lo hacemos con el ángulo en la punta de la rebanada. Todo lo que tenemos que saber es que el círculo completo corresponde a un ángulo de 360 grados
o 2 * PI
. Así que la mitad del círculo sería 180 grados
o PI
, un cuarto 90 grados
o PI/2
y así sucesívamente.
Para determinar el ángulo para cada rebanada de categoría, usamos la fórmula:
ángulo de rebanada = 2 * PI * valor de categoría / valor total
De acuerdo a esta fórmula, los diez vinilos de música clásica tendrán un ángulo de aprox. 0.526 * PI ó 94 grados.
Comencemos a dibujar. Para este usaremos una clase javaScript la cuál nombraremos Piechart
. El contructor recibirá un argumento opciones, un objeto conteniendo lo siguiente:
- canvas: referencia al canvas en donde queremos dibujar la gráfica de pastel
- data: referencia de un objeto conteniendo el modelo de datos
- colors: un arreglo conteniendo los colores que queremos usar para cada rebanada
La clase Piechart
también contiene un método draw()
el cuál hace el dibujo de la gráfica.
1 |
var Piechart = function(options){ |
2 |
|
3 |
this.options = options; |
4 |
|
5 |
this.canvas = options.canvas; |
6 |
|
7 |
this.ctx = this.canvas.getContext("2d"); |
8 |
|
9 |
this.colors = options.colors; |
10 |
|
11 |
|
12 |
|
13 |
this.draw = function(){ |
14 |
|
15 |
var total_value = 0; |
16 |
|
17 |
var color_index = 0; |
18 |
|
19 |
for (var categ in this.options.data){ |
20 |
|
21 |
var val = this.options.data[categ]; |
22 |
|
23 |
total_value += val; |
24 |
|
25 |
}
|
26 |
|
27 |
|
28 |
|
29 |
var start_angle = 0; |
30 |
|
31 |
for (categ in this.options.data){ |
32 |
|
33 |
val = this.options.data[categ]; |
34 |
|
35 |
var slice_angle = 2 * Math.PI * val / total_value; |
36 |
|
37 |
|
38 |
|
39 |
drawPieSlice( |
40 |
|
41 |
this.ctx, |
42 |
|
43 |
this.canvas.width/2, |
44 |
|
45 |
this.canvas.height/2, |
46 |
|
47 |
Math.min(this.canvas.width/2,this.canvas.height/2), |
48 |
|
49 |
start_angle, |
50 |
|
51 |
start_angle+slice_angle, |
52 |
|
53 |
this.colors[color_index%this.colors.length] |
54 |
|
55 |
);
|
56 |
|
57 |
|
58 |
|
59 |
start_angle += slice_angle; |
60 |
|
61 |
color_index++; |
62 |
|
63 |
}
|
64 |
|
65 |
|
66 |
|
67 |
}
|
68 |
|
69 |
}
|
La clase comienza almacenando las options
pasadas como argumentos. Esta almacena la referencia de canvas
y crea un contexto de dibujo también almacenado como un miembro de la clase. Después almacena el arreglo colors
pasado como opciones.
La siguiente parte es la más consistente, la función draw()
. Esto dibujará los datos del modelo de datos. Primero calcula la suma de todos los valores en el modelo de datos. Después, para cada categoría en el modelo de datos aplicamos la fórmula mencionada arriba para calcular el ángulo de la rebanada. Finalmente usamos la función drawPieSlice()
usando el centro del canvas como el centro de la rebanada. Como radio usamos el valor mínimo entre la mitad del ancho del canvas y la mitad del alto ya que no queremos que nuestro pastel salga del canvas.
También compensamos el inicio y el final de las rebanadas cada vez que dibujamos una categoría, de otro modo las rebanadas se encimarían.
Para usar esta clase, tenemos que crear una instancia y después llamar el método draw()
en el objeto creado.
1 |
var myPiechart = new Piechart( |
2 |
|
3 |
{
|
4 |
|
5 |
canvas:myCanvas, |
6 |
|
7 |
data:myVinyls, |
8 |
|
9 |
colors:["#fde23e","#f16e23", "#57d9ff","#937e88"] |
10 |
|
11 |
}
|
12 |
|
13 |
);
|
14 |
|
15 |
myPiechart.draw(); |
Y el resultado se ve así



Dibujando la Gráfica de Dona
Hemos visto cómo dibujar la gráfica de pastel También sabemos que una gráfica de dona difiere solo por tener un agujero en medio de la gráfica. ¿Cómo dibujamos el agujero? Podemos dibujar un círculo blanco sobre la gráfica de pastel.
Modifiquemos el código de la clase Piechart
para hacer eso.
1 |
var Piechart = function(options){ |
2 |
|
3 |
this.options = options; |
4 |
|
5 |
this.canvas = options.canvas; |
6 |
|
7 |
this.ctx = this.canvas.getContext("2d"); |
8 |
|
9 |
this.colors = options.colors; |
10 |
|
11 |
|
12 |
|
13 |
this.draw = function(){ |
14 |
|
15 |
var total_value = 0; |
16 |
|
17 |
var color_index = 0; |
18 |
|
19 |
for (var categ in this.options.data){ |
20 |
|
21 |
var val = this.options.data[categ]; |
22 |
|
23 |
total_value += val; |
24 |
|
25 |
}
|
26 |
|
27 |
|
28 |
|
29 |
var start_angle = 0; |
30 |
|
31 |
for (categ in this.options.data){ |
32 |
|
33 |
val = this.options.data[categ]; |
34 |
|
35 |
var slice_angle = 2 * Math.PI * val / total_value; |
36 |
|
37 |
|
38 |
|
39 |
drawPieSlice( |
40 |
|
41 |
this.ctx, |
42 |
|
43 |
this.canvas.width/2, |
44 |
|
45 |
this.canvas.height/2, |
46 |
|
47 |
Math.min(this.canvas.width/2,this.canvas.height/2), |
48 |
|
49 |
start_angle, |
50 |
|
51 |
start_angle+slice_angle, |
52 |
|
53 |
this.colors[color_index%this.colors.length] |
54 |
|
55 |
);
|
56 |
|
57 |
|
58 |
|
59 |
start_angle += slice_angle; |
60 |
|
61 |
color_index++; |
62 |
|
63 |
}
|
64 |
|
65 |
|
66 |
|
67 |
//drawing a white circle over the chart
|
68 |
|
69 |
//to create the doughnut chart
|
70 |
|
71 |
if (this.options.doughnutHoleSize){ |
72 |
|
73 |
drawPieSlice( |
74 |
|
75 |
this.ctx, |
76 |
|
77 |
this.canvas.width/2, |
78 |
|
79 |
this.canvas.height/2, |
80 |
|
81 |
this.options.doughnutHoleSize * Math.min(this.canvas.width/2,this.canvas.height/2), |
82 |
|
83 |
0, |
84 |
|
85 |
2 * Math.PI, |
86 |
|
87 |
"#ff0000" |
88 |
|
89 |
);
|
90 |
|
91 |
}
|
92 |
|
93 |
|
94 |
|
95 |
}
|
96 |
|
97 |
}
|
El código agregado busca en el parámetro options
por un miembro variable doughnutHoleSize
. Si este no existe en las opciones entonces el código dibujará la gráfica de pastel como antes, pero si existe entonces se dibujará un círculo blanco con el mism centro que la gráfica de pastel.
El radio del círculo es determinado por multiplicar el radio de la gráfica de pastel y el valor de doughnutHoleSize
. Esto debería ser un numero entre 0 y 1, en donde 0 será el resultado en una gráfica de pastel y cualquier valor más alto que 0 resultaría en una dona con el agujero más grande y más grande, 1 haciendo la gráfica invisible.
Para dibujar una gráfica de dona con un agujero del tamaño de la tabla, necesitaríamos usar un doughnutHoleSize
de 0.5 y hacer las siguientes llamadas.
1 |
var myDougnutChart = new Piechart( |
2 |
|
3 |
{
|
4 |
|
5 |
canvas:myCanvas, |
6 |
|
7 |
data:myVinyls, |
8 |
|
9 |
colors:["#fde23e","#f16e23", "#57d9ff","#937e88"], |
10 |
|
11 |
doughnutHoleSize:0.5 |
12 |
|
13 |
}
|
14 |
|
15 |
);
|
16 |
|
17 |
myDougnutChart.draw(); |
Y aquí está el resultado:



Agregando Etiquetas y Leyenda de Gráfica
Nuestra gráfica de pastel y dona se ven muy bien, pero podemos hacerlas mucho mejor agregando dos cosas:
- etiquetas de valor: mostrando el porcentaje correspondiente a cada rebanada
- una leyenda de gráfica: mostrando las categorías y sus colores correspondientes en la gráfica
Usualmente, valoes asociados con las rebanadas son representadas como valores porcentuales calculados como 100* valor asociado a la rebanada / valor total
, con el círculo completo representando 100%
.
Por ejemplo, en el caso de nuestros datos de muestra, vinilos con música clásica representaría aproximádamente 26%
. Sería agradable poder escribir ese valor justo en la rebanada correspondiente. Para hacer eso, usaremos la función fillText(text,x,y)
del contexto del diibujo. Esta función toma tres parámetros: el texto y las coordenadas x
y y
.
¿Cómo calculamos las coordenadas x
y y
en las cuáles colocar el texto? Tenemos que hacer uso de un poco de conocimiento de geometría y algo llamado coordenadas polares. Básicamente, las coordenadas polares usan un radio y un ángulo para definir la posición de un punto. Las dos fórmulas que usaremos son:
x = R * cos(ángulo)
y = R * sin(ángulo)
Aplicaremos estas dos fórmulas para colocar el texto a la mitad del radio de la gráfica de pastel y a la mitad del ángulo para cada rebanada del pastel. Para hacer esto, necesitamos modificar nuestra clase Piechart
y agregar el siguiente código justo después del bloque (this.options.doughnutHoleSize){...}
:
1 |
...
|
2 |
|
3 |
start_angle = 0; |
4 |
|
5 |
for (categ in this.options.data){ |
6 |
|
7 |
val = this.options.data[categ]; |
8 |
|
9 |
slice_angle = 2 * Math.PI * val / total_value; |
10 |
|
11 |
var pieRadius = Math.min(this.canvas.width/2,this.canvas.height/2); |
12 |
|
13 |
var labelX = this.canvas.width/2 + (pieRadius / 2) * Math.cos(start_angle + slice_angle/2); |
14 |
|
15 |
var labelY = this.canvas.height/2 + (pieRadius / 2) * Math.sin(start_angle + slice_angle/2); |
16 |
|
17 |
|
18 |
|
19 |
if (this.options.doughnutHoleSize){ |
20 |
|
21 |
var offset = (pieRadius * this.options.doughnutHoleSize ) / 2; |
22 |
|
23 |
labelX = this.canvas.width/2 + (offset + pieRadius / 2) * Math.cos(start_angle + slice_angle/2); |
24 |
|
25 |
labelY = this.canvas.height/2 + (offset + pieRadius / 2) * Math.sin(start_angle + slice_angle/2); |
26 |
|
27 |
}
|
28 |
|
29 |
|
30 |
|
31 |
var labelText = Math.round(100 * val / total_value); |
32 |
|
33 |
this.ctx.fillStyle = "white"; |
34 |
|
35 |
this.ctx.font = "bold 20px Arial"; |
36 |
|
37 |
this.ctx.fillText(labelText+"%", labelX,labelY); |
38 |
|
39 |
start_angle += slice_angle; |
40 |
|
41 |
}
|
42 |
|
43 |
...
|
El código recorre cada rebanada, calcula el porcentaje, calcula la posición y usa el método fillText()
para dibujarla en la gráfica. Hemos usado la propiedad fillStyle
para establecer el color dle texto a blanco y la propiedad font
para establecer el tamaño, estilo y familia tipográfica de la etiqueta. También es importante notar que si la gráfica es una gráfica de dona y el doughnutHoleSize
está establecido, entonces la etiqueta será empujada hacia el borde de la gráfica para centrarla en la rebanada de la dona.
Y aquí está cómo se ven las gráficas resultantes con las etiquetas de valor:



Para completar nuestra gráfica, la última cosa que agregaremos es la leyenda de la gráfica. Nuestra leyenda mostrará las categorías de nuestro modelo de datos y el color usado para la rebanada correspondiente. Primero tenemos que hacer algunas modificaciones a nuestro archivo index.html
agregando una etiqueta <div>
que almacenará nuestro elemento leyenda.
1 |
<html>
|
2 |
|
3 |
<body>
|
4 |
|
5 |
<canvas id="myCanvas"></canvas> |
6 |
|
7 |
<div id="myLegend"></div> |
8 |
|
9 |
<script type="text/javascript" src="script.js"></script> |
10 |
|
11 |
</body>
|
12 |
|
13 |
</html>
|
Después en script.js
agregamos el código que crea el contenido del elemento de leyenda. Agregamos este código al final de la función draw()
en la clase Piechart
.
1 |
...
|
2 |
|
3 |
if (this.options.legend){ |
4 |
|
5 |
color_index = 0; |
6 |
|
7 |
var legendHTML = ""; |
8 |
|
9 |
for (categ in this.options.data){ |
10 |
|
11 |
legendHTML += "<div><span style='display:inline-block;width:20px;background-color:"+this.colors[color_index++]+";'> </span> "+categ+"</div>"; |
12 |
|
13 |
}
|
14 |
|
15 |
this.options.legend.innerHTML = legendHTML; |
16 |
|
17 |
}
|
18 |
|
19 |
...
|
El código busca el elemeto de legend
pasado por medio del parámetro de options
. Si uno es proporcionado, este elemento es llenado con el código HTML conteniendo una caja coloreada y el nombre de la categoría del modelo de datos.
También necesitamos hacer un cambio a la manera en que cambiamos el dibujo en un nuestra gráfica de pastel como esto:
1 |
var myLegend = document.getElementById("myLegend"); |
2 |
|
3 |
|
4 |
|
5 |
var myDougnutChart = new Piechart( |
6 |
|
7 |
{
|
8 |
|
9 |
canvas:myCanvas, |
10 |
|
11 |
data:myVinyls, |
12 |
|
13 |
colors:["#fde23e","#f16e23", "#57d9ff","#937e88"], |
14 |
|
15 |
legend:myLegend |
16 |
|
17 |
}
|
18 |
|
19 |
);
|
20 |
|
21 |
myDougnutChart.draw(); |
Y aquí están la gráfica y leyenda de gráfica resultantes:



Felicidades
Hemos visto que dibujar gráficas usando el canvas HTML5 en realidad no es tan difícil. Solo se requiere un poco de matemáticas y un poco de conocimiento de JavaScript. Ahora tienes todo lo que necesitas para dibujar tu propia gráfica de pastel y dona.
Si quieres una solución rápida y sencilla para crear no solo gráficas de pastel y dona sino montones de otros tipos de gráficas, puedes descargar la Infographic Charts and Graphics HTML Tags Library o su contraparte de plugin para WordPress Charts and Graphs WordPress Visual Designer.