Principes du Canvas HTML5
Le canvas est une une zone de dessin incluse dans la page HTML sur laquelle on peut dessiner des formes à l'aide de Javascript.Page HTML
L’élément de dessin est déclaré dans le code HTML avec la balise <canvas>. En général, 3 attributs doivent être renseignés:
- id: identifiant de l’élément servant à l'appel en js,
- width: largeur de la zone de dessin,
- height: hauteur de la zone de dessin.
<!DOCTYPE html>
<html>
<head>
<title>Exemple de graph avec canvas</title>
</head>
<body>
<canvas id="myGraph" width="300" height="200"></canvas>
</body>
</html>
Le canvas se compose de 2 parties: objet canvas que l'on peut comparer à un Div par exemple et le context qui est la zone de dessin à proprement parler. Pour ajouter des formes ou une animation sur le canvas, les fonctions essentielles sont les suivantes:
- getContext('2d'): retourne l'objet de dessin comparable à handle sous Windows,
- beginPath() et stroke(): prépare puis dessine une nouvelle forme sur le canvas,
- fillRect(), moveTo(), lineTo(), arc(), ...: fonctions classiques de dessins sur un canvas.
Dessiner dynamiquement un graph dans une page HTML
Les présentations étant faites, revenons au but initiale: dessiner un graphique de type ligne pour une serie de donnée.Le graphique ci-contre a été généré à partir directement avec l'objet Canvas en HTML5.
Pour simplifier les développements futurs, l'ensemble des fonctions de générations de graphique a été regroupée dans un objet Javascript. La structure de l'objet a été prévu pour ajouter les données à partir d'un résultat en JSON.
Class js LineGraph
function LineGraph(canvasId) {
/**
* Dessiner un graphique sur un Canvas HTML5
* @author: http://www.fobec.com/tuto/1153/dessiner-graph-sur-canvas-html5.html
*/
//Interne - ne pas modifier ces valeurs
var canvas_ctx = null;
var canvas_obj = null;
var dataset = {serie: []};
var xLabelStep;
var yMax = 0;
//const: marge gauche et droite
var marginX = 30;
//const: marge en haut et en bas
var marginY = 20;
//nombre de valeur a afficher sur axe X
var yStep = 5;
//Constructor
var canvas_obj = document.getElementById(canvasId);
canvas_ctx = canvas_obj.getContext('2d');
//Dessiner un fond blanc
canvas_ctx.beginPath();
canvas_ctx.fillStyle = '#fff';
canvas_ctx.fillRect(0, 0, canvas_obj.width, canvas_obj.height);
canvas_ctx.stroke();
/**
* Dessiner les axes verticaux
* @returns {undefined}
*/
this.drawAxis = function() {
canvas_ctx.lineWidth = 2;
canvas_ctx.strokeStyle = '#333';
canvas_ctx.fillStyle = '#333';
canvas_ctx.beginPath();
canvas_ctx.textAlign = "right"
canvas_ctx.textBaseline = "middle";
//label de l'axe Y
var yVal;
for (var i = 0; i <= yStep; i++) {
yVal = (yMax / yStep) * i;
canvas_ctx.fillText(Math.round(yVal), marginX - 10, yValuetoGraph(yVal));
}
canvas_ctx.stroke();
//Barre verticales
canvas_ctx.beginPath();
canvas_ctx.strokeStyle = '#666';
canvas_ctx.lineWidth = .5;
for (var i = 0; i <= yStep; i++) {
yVal = (yMax / yStep) * i;
canvas_ctx.moveTo(marginX, yValuetoGraph(yVal) + 2);
canvas_ctx.lineTo(canvas_obj.width - marginX, yValuetoGraph(yVal) + 2);
}
canvas_ctx.stroke();
calcXStep();
}
/**
* Ajouter une valeur X,Y à la serie
* @param {string} x
* @param {int} y
*/
this.addPoint = function(x, y) {
dataset.serie.push({X: x, Y: y});
if (y > yMax) {
yMax = y;
}
}
/**
* Dessiner la courbe
*/
this.drawLine = function() {
canvas_ctx.beginPath();
canvas_ctx.strokeStyle = '#DF4926';
canvas_ctx.lineWidth = 3;
canvas_ctx.moveTo(xValuetoGraph(0), yValuetoGraph(dataset.serie[0].Y));
//line
for (var i = 1; i < dataset.serie.length; i++) {
canvas_ctx.lineTo(xValuetoGraph(i), yValuetoGraph(dataset.serie[i].Y));
}
canvas_ctx.stroke();
//point
canvas_ctx.fillStyle = '#333';
for (var i = 0; i < dataset.serie.length; i++) {
/* canvas_ctx.beginPath();
canvas_ctx.arc(xValuetoGraph(i), yValuetoGraph(dataset.serie[i].Y), 4, 0, Math.PI * 2, true);
canvas_ctx.fill();*/
}
//label x
canvas_ctx.beginPath();
var ilabelstep = xLabelStep;
for (var i = 0; i < dataset.serie.length; i++) {
//Bidouillage pour eviter la superposition de label sur l'axe X
if (xLabelStep > 1) {
if (ilabelstep >= xLabelStep) {
canvas_ctx.fillText(dataset.serie[i].X, xValuetoGraph(i) + (marginX / 2), yValuetoGraph(0) + (marginY / 2) + 5);
ilabelstep = 0;
}
ilabelstep++;
} else {
canvas_ctx.fillText(dataset.serie[i].X, xValuetoGraph(i) + (marginX / 2), yValuetoGraph(0) + (marginY / 2) + 5);
}
}
canvas_ctx.stroke();
}
/**
* Regle par 3 pour X
* @param {type} xIdx
*/
function xValuetoGraph(xIdx) {
var serielen = dataset.serie.length;
var xArea = canvas_obj.width - (marginX * 2);
var xPos = ((xArea / serielen) * xIdx) + marginX;
return xPos;
}
/**
* Regle par 3 pour Y
* @param {type} xIdx
*/
function yValuetoGraph(yVal) {
var yArea = canvas_obj.height - (marginY * 2);
var yPos = canvas_obj.height - marginY - ((yVal * yArea) / yMax);
return yPos;
}
/**
* Définir le nombre de label sur l'axe X
* @returns {undefined}
*/
function calcXStep() {
var text;
var xArea = canvas_obj.width - (marginX * 2);
for (var i = 0; i < dataset.serie.length; i++) {
text += ' ' + dataset.serie[i].X + ' ';
}
var metrics = canvas_ctx.measureText(text);
var width = metrics.width;
xLabelStep = Math.round(width / xArea);
}
}
Comment générer un graphique dynamiquement
Le premier exemple est une serie de nombre aléatoire que l'on souhaite affiche sous forme de graphique. La limite de la class js est de toujours commencer par ajouter les données dans le dataset interne, puis de dessiner les axes verticaux pour finir par dessiner la courbe.var lineGraph = new LineGraph("graph");
lineGraph.addPoint("janv", Math.floor((Math.random() * 100) + 1));
lineGraph.addPoint("fevr", Math.floor((Math.random() * 100) + 1));
lineGraph.addPoint("mars", Math.floor((Math.random() * 100) + 1));
lineGraph.addPoint("avr", Math.floor((Math.random() * 100) + 1));
lineGraph.addPoint("mai", Math.floor((Math.random() * 100) + 1));
lineGraph.addPoint("juin", Math.floor((Math.random() * 100) + 1));
lineGraph.addPoint("juil", Math.floor((Math.random() * 100) + 1));
lineGraph.addPoint("aout", Math.floor((Math.random() * 100) + 1));
lineGraph.addPoint("sept", Math.floor((Math.random() * 100) + 1));
lineGraph.addPoint("oct", Math.floor((Math.random() * 100) + 1));
lineGraph.addPoint("nov", Math.floor((Math.random() * 100) + 1));
lineGraph.addPoint("dec", Math.floor((Math.random() * 100) + 1));
lineGraph.drawAxis();
lineGraph.drawLine();
Le deuxième exemple est l'utilisation de données issues d'un tableau JSON. Le cas classique est une requête en Ajax vers un serveur MySql, les valeurs sont parsées en javascript puis le graph est généré dynamiquement.
new ajax().request('http://xxx.com',
{
request: 'GET',
onresponse: function(response, success) {
var n = JSON.parse(response);
var lineGraph = new LineGraph("myGraph");
for (var i = n.length - 1; i >= 0; i--) {
lineGraph.addPoint(n[i]['DATE'], parseInt(n[i]['POSITION']));
}
lineGraph.drawAxis();
lineGraph.drawLine();
}
});
Générer des graphiques sans librairies js
Pour la petite histoire, j'étais à la recherche d'une librairie js pour dessiner une courbe à partir de résultats JSON. Sur le web, on trouve de multiples class qui bien souvent sont surdimensionnées par rapport à mon besoin. C'est ainsi qu'est venu l'idée d'utiliser la canvas HTML5 pour afficher une représentation graphique des données.La class est en place dans notre projet, je partage en espérant qu'elle vous sera utile dans les projets où l'on souhaite afficher une courbe simple, sans librairie ou api externe et rapidement sans développement supplémentaire.