Publié le 20/10/2014
Auteur fobec
Réseaux sociaux
0 partages
0 tweets
0 plus
3 commentaires

Dessiner un graph sur un Canvas HTML5

Le format HTML5 a introduit une nouvelle fonctionnalité: le canvas qui permet de dessiner sur la page HTML. Comment utiliser cette propriété pour crée un graphique directement sur la page web ?

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>
Code javascript
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

1153_html5_canvasLes 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();
Et oui, c'est le code source qui a servi à générer le graph exemple !

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.

Ajouter un commentaire

Les champs marqués d'un * sont obligatoires, les adresses emails se sont pas publiées.
avatar Vincent

Publié par Vincent

le 08/04/2016 à 07:49:39

Sympa votre petit script :) Oui il existe des bibliotheques deja toutes faites mais elles sont en general 50x plus lourdes que ce petit bout de code qui fait exactement ce qu'il faut. En plus votre code est bien construit, bien commente, tres comprehensible. Merci beaucoup pour ce partage ;)

avatar Michael

Publié par Michael

le 10/02/2017 à 14:38:58

Merci pour ce partage, ca m'a ete tres utile pour une application hta que j'ai developper pour mon travail.
Peut etre modifier le script afin de creer des graphiques differentes (en formage, en colonnes, etc...), egalement changer les points par les valeur en chiffres.

avatar chuchunain

Publié par chuchunain

le 17/02/2017 à 11:45:43

Petite correction dans votre code. Le premier graphe genere avec 15 valeurs aleatoires devrait faire appel au graphe "myGraph" et non "graph" sinon l'objet canvas ne recoit rien. merci pour le code ca marche !

A lire aussi

Réseaux sociaux
Présentation de l'article
Catégorie
tuto - webmaster
Mise a jour
20/10/2014
Visualisation
vu 7207 fois
Public
Internaute
Auteur de la publication
Fobec
Admin
Auteur de 267 articles
|BIO_PSEUDO|
Commentaires récents

Publié par Franzi dans java

Bonjour,
Le code est simple et compile, après être entré dans public void connect, il n'effectue pas this.session.get.transport et s'arrête. Sous eclipse avec Tomcat, j'ob...

Publié par Daxentuut dans java

Avec les imports c'est mieux :

import java.awt.AWTException
import java.awt.Robot
import java.awt.event.KeyEvent
import java.io.IOException
import java.util.logging.Level
import...

Publié par ZINEDDINE dans CMS

SaLUT Monsieur
tu peux avec ce deux mot pass ouvrir tous les tables paradox
1-jIGGAe
2-cupcdvum
respecter le majjuscul
bonne chance .

Publié par Altair dans tuto

Bonjour, cette solution fonctionne-t-elle également sur les serveurs mutualisés Amen svp ?

Publié par Axel dans java

la position sur mouseDragged a ete corrigee dans l'article.
Merci Alpcom !