Flot – Une librairie de graphes pour JQuery

JQuery prouve une fois de plus ses capacités avec cette librairie dédiée « Flot ». Elle permet à l’aide d’une série de données (chiffres / dates, chiffres / chiffres…) de réaliser des graphiques paramétrables : Affichage de la données d’abcisse ou d’ordonnées au survol, sélection d

e zones, colorations, affichage en colonnes ligne ou traits verticaux, zooms, navigation… Mode d’emploi.

En prérequis, nous allons télécharger Flot ici. A noter qu’il inclut déjà JQuery donc nul besoin d’aller le chercher. L’archive que vous allez télécharger contient moult exemples de cas d’utilisation de graphiques. Nous allons essayer ici d’en faire un mini mix que vous pouvez voir ici si vous êtes impatients.

Vous verrez que les exemples ne sont pas tous implémentés de la même façon. Nous essayerons également de faire ça aussi proprement que possible.

Structure de la page

Pour commencer, donc, analysons l’en-tête de la page. Dans notre exemple, nous allons tenter de faire quelque chose de zoomable par sélection, draggable et dont les données s’affichent au survol de la courbe. Il nous faut pour cela les plugins navigate, selection et crosshair (non, il n’y a pas de subtilités, c’est juste ce que j’ai pu constater au travers des exemples 🙂 ) :

<script language="javascript" type="text/javascript" src="../jquery.js"></script>
<script language="javascript" type="text/javascript" src="../jquery.flot.js"></script>
<script language="javascript" type="text/javascript" src="../jquery.flot.navigate.js"></script>
<script language="javascript" type="text/javascript" src="../jquery.flot.selection.js"></script>
<script language="javascript" type="text/javascript" src="../jquery.flot.crosshair.js"></script>

Voilà, ça, c’est fait. Maintenant un peu de css :

#placeholder .button {
	position: absolute;
	cursor: pointer; margin-right: -98px;
}
#placeholder div.button {
	font-size: smaller; color: #999;
	background-color: #eee; padding: 3px;
	box-shadow: 0px 0px 5px #777;
	-moz-box-shadow: 0px 0px 5px #777;
	-webkit-box-shadow: 0px 0px 5px #777;
}
#placeholder div.button:active {
	padding: 2px;  margin: 1px -97px 1px 1px;
	box-shadow: 0px 0px 7px #777;
	-moz-box-shadow: 0px 0px 7px #777;
	-webkit-box-shadow: 0px 0px 7px #777;
}
#placeholder{
	width:410px; height: 250px; padding: 5px; margin: 5px;
	box-shadow: 1px 1px 5px #777;
	-moz-box-shadow: 1px 1px 5px #777;
	-webkit-box-shadow: 1px 1px 5px #777;
}
#page{ width:510px; height: 270px; margin: 0 auto; }
#miniature{
	float: left; width:400px;
	height:100px; margin: 0 0 0 20px;
}
#overview{ float: left; width:400px; height:100px; }

Il s’agit juste de mise en forme. C’est indiqué ici à titre d’indication uniquement. Enfin, la structure de la page html :

<div id="page">
	<div id="miniature">
		<p id="overviewLegend"></p>
	</div>
	<p id="hoverdata"></p>
</div>

Elle est simple, pas besoin de plus. Attaquons-nous aux choses sérieuses : Le javascript. On va caser par commodité dans notre exemple le javascript dans le corps de la page, mais on peut le faire avec un js à part sans problème.

Définition des données

Cette partie est simple : Nous avons un tableau indexé dans lequel nous rangeons des informations. Par exemple :

var donnees = [ [30, 0], [40, 1], [50, 2] ];

Mais bien entendu, cela peut se compliquer. Imaginons que l’on veuille donner un label à la courbe, pourindiquer une légende au graphique :

var donnees= [
        {
            label: "United States",
            data: [ [1990, 18.9], [1991, 18.7] ]
        }

L’idée est ici de définir un label pour chaque élément de « donnees ». Mais à quoi ça sert? Et bien imaginons maintenant plusieurs courbes :

var donnees= [
        {
            label: "United States",
            data: [ [1990, 18.9], [1991, 18.7] ]
        },{
            label: "France",
            data: [ [1990, 14.2], [1991, 8.7] ]
        },{
            label: "Spain",
            data: [ [1990, 10.1], [1991, 15.7] ]
        }

Ce qu’il se passe, c’est que chaque entrées de « donnees » va contenir un jeu de données (United States, france et Spain ici). Il est également possible de ranger dedans le résultat d’un opération mathématique : c’est le choix que fait dans notre exemple. Pour chaque « set » de données, on va pouvoir également définir des options : couleur, ombrage, points visibles, tableau de données… En pratique :

var A = []; //données de ma première courbe
for (var i = 50; i < 90; i += 0.2)
	A.push([i, Math.cos(i)]);
var B = []; //données de ma seconde courbe
for (var i = 50; i < 90; i += 0.2)
	B.push([i, Math.sin(i)]);
var data = [
	{
		label: "Cos(x) = -0.00",
		yaxis: 1,//je définis ici que c'est la première
		data : A,//le jeu de données
		color: "#33CCCC", //couleur de la ligne
		points: { show: true }//je vais affiche des points pour chaque donnée
	},
	{
		label: "Sin(x) = -0.00",
		yaxis: 2,//je définis ici que c'est la seconde
		data : B,//le jeu de données
		color : "#FF8000", //couleur de la ligne
		points: { show: true }//je vais affiche des points pour chaque donnée
	}
];

Notez bien le « = -0.00 » à la fin des labels, c’est ce qui conditionnera l’une des fonctionnalités que nous verrons plus loin. Nous avons donc défini les données à afficher sur les graphiques. Passons maintenant au tracé du graphique.

Tracé du graphique

regardons ce que nous avons fait ici :

var placeholder = $("#placeholder");
var options = {
	xaxis: { show: true, ticks: 5, min: 70, max:80 },
	yaxis: { show: true, ticks: 3 },
	series: { lines: { show: true },
			shadowSize: 5},
	zoom: {
		interactive: true
	},
	pan: {
		interactive: true
	},
	crosshair: { mode: "x" },
	grid: { color: "#67523F",
			backgroundColor: { colors: ["#fff", "#e9e9e9"] },
			hoverable: true,
			autoHighlight: true
	},
	legend: { position: 'sw' }
};
var plot = $.plot(placeholder, donnees, options);

Explications : Nous définissions le fait de montrer les axes X et Y, et pour l’axe X nous disons 5 barres verticales affichées, et ne voyons que les données entre 70 et 80.

Ensuite nous définissons une ombre pour toutes les séries présentes, et activons les options de zoom (s’il est à true, ecla signifie que le double clique pourra zoomer) et de navigabilité (pan).

« Grid » représente les options de la grille : couleur des traites, arrière plan (dégradé), et surtout : « hoverable » et « autohighlight », nécessaire à la fonction de sélection.

Enfin, « legend » est définit à position: sw pour south west. Vous verrez plus loin!

Options : De-zoom, déplacement, sélection et affichage des valeurs au survol

Ensuite nous rajoutons le code dont nous avons besoin pour le zoom et la navigation :

// add zoom out button
$('<div class="button" style="right:20px;top:20px">zoom out</div>').appendTo(placeholder).click(function (e) {
	e.preventDefault();
	plot.zoomOut();
});
// And pan buttons little helper for taking the repetitive work out of placing
// panning arrows
function addArrow(dir, right, top, offset) {
	$('<img class="button" src="arrow-' + dir + '.gif" style="right:' + right + 'px;top:' + top + 'px">').appendTo(placeholder).click(function (e) {
		e.preventDefault();
		plot.pan(offset);
	});
}
addArrow('left', 55, 60, { left: -10 });
addArrow('right', 25, 60, { left: 10 });
addArrow('up', 40, 45, { top: -10 });
addArrow('down', 40, 75, { top: 10 });

Pas grand chose à dire ici, le code est explicite : ajout des boutons au code HTML, ajout de listeners dessus. Le reste se passe dans le plugin, trop complexe à détailler.

Nous pouvons juste parler des quatre dernière lignes, left et top définissant le degré de déplacement (plus c’est petit, moins on va loin). Le bouton zoomOut permettra de dézoomer. Les flèches de se déplacer.

maintenant, intéressons-nous au code permettant d’afficher une miniature dans laquelle on pourra sélectionner des zones sur lesquelles zoomer

var overview = $.plot($("#overview"), data, {
	legend: { show: false },
	series: {
		lines: { show: true, lineWidth: 1 },
		shadowSize: 0
	},
	xaxis: { ticks: 3 },
	yaxis: { ticks: 2, min: -2, max: 2 },
	grid: { color: "#67523F", backgroundColor: { colors: ["#e9e9e9", "#fff"] } },
	selection: { mode: "xy" }
});
// now connect the two
$("#placeholder").bind("plotselected", function (event, ranges) {
	// clamp the zooming to prevent eternal zoom
	if (ranges.xaxis.to - ranges.xaxis.from < 0.00001)
		ranges.xaxis.to = ranges.xaxis.from + 0.00001;
	if (ranges.yaxis.to - ranges.yaxis.from < 0.00001)
		ranges.yaxis.to = ranges.yaxis.from + 0.00001;
	// do the zooming
	plot = $.plot($("#placeholder"), donnees,
				  $.extend(true, {}, options, {
					  xaxis: { min: ranges.xaxis.from, max: ranges.xaxis.to },
					  yaxis: { min: ranges.yaxis.from, max: ranges.yaxis.to },
				  }));
	// don't fire event on the overview to prevent eternal loop
	overview.setSelection(ranges, true);
	// add zoom out button
	$('<div class="button" style="right:20px;top:20px">zoom out</div>').appendTo(placeholder).click(function (e) {
		e.preventDefault();
		plot.zoomOut();
	});
	addArrow('left', 55, 60, { left: -10 });
	addArrow('right', 25, 60, { left: 10 });
	addArrow('up', 40, 45, { top: -10 });
	addArrow('down', 40, 75, { top: 10 });
});
$("#overview").bind("plotselected", function (event, ranges) {
	plot.setSelection(ranges);
});

Vous noterez que sont rajoutées les lignes permettant l’affichage des boutons zoomOut et navigation. Ceci car lors du zoom, le cadre est réinitialisé. Il faut donc les ré-afficher sinon on les perd.

Pour le reste, rien à dire, ce que vous voyez là est un copier coller des exemple, nul besoin de le modifier dans notre exemple à une exception près : après le commentaire //do the zooming j’ai du modifier l’exemple pour qu’il me prenne bien mon tableau « donnees » du début.

Enfin, voyons l’affichage des valeurs au survol :

var legends = $("#placeholder .legendLabel");
legends.each(function () {
	// fix the widths so they don't jump around
	$(this).css('width', $(this).width());
});
var updateLegendTimeout = null;
var latestPosition = null;
function updateLegend() {
	updateLegendTimeout = null;
	var pos = latestPosition;
	var axes = plot.getAxes();
	if (pos.x < axes.xaxis.min || pos.x > axes.xaxis.max ||
		pos.y < axes.yaxis.min || pos.y > axes.yaxis.max)
		return;
	var i, j, dataset = plot.getData();
	for (i = 0; i < dataset.length; ++i) {
		var series = dataset[i];
		// find the nearest points, x-wise
		for (j = 0; j < series.data.length; ++j)
			if (series.data[j][0] > pos.x)
				break;
		// now interpolate
		var y, p1 = series.data[j - 1], p2 = series.data[j];
		if (p1 == null)
			y = p2[1];
		else if (p2 == null)
			y = p1[1];
		else
			y = p1[1] + (p2[1] - p1[1]) * (pos.x - p1[0]) / (p2[0] - p1[0]);
		legends.eq(i).text(series.label.replace(/=.*/, "= " + y.toFixed(2)));
	}
}
$("#placeholder").bind("plothover",  function (event, pos, item) {
	latestPosition = pos;
	if (!updateLegendTimeout)
		updateLegendTimeout = setTimeout(updateLegend, 50);
});

pas grand chose à dire ici non plus. Il a juste fallu faire attention à bien définir les options du graphe et du grid (cf. début de page).

Voilà ce que tout cela nous donne, de façon plus concrète. Vous pouvez utiliser la miniature pour zoomer, double cliquer pour zoomer également, le bouton zoomout pour dézoomer et les flèches ou le drag&drop pour vous déplacer. Et ceci n’est qu’un modeste aperçu de ce qu’il est possible de faire avec Flot!

Voir l’exemple pleine page

4 commentaires

  1. Bonjour,
    J’aimerais savoir s’il est possible d’avoir les codes pour créer le graphique? Car je le trouve très réussi et le tutoriel aussi. Mais je n’arrive pas à le faire fonctionner, je suis débutant et je ne sais pas quoi mettre dans le css, dans le html?

    Merci d’avance.
    Bissey Sébastien.

  2. Pas mal du tout, mais il y a un souci avec le tracking crosshair et le zoom : si on déplace le graphique et/ou on zoom avec la molette, le tracking ne fonctionne plus.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *