Edit :
Le problème a été résolu. La solution se trouve dans la partie "Résolution" en fin de message (+ d'infos en lisant la première page de ce topic).
Bonjour tout le monde,
Contexte
Je dois créer un module de statistiques qui affiche un graphe dont les données sont stockables dans ses axes, par un système de drag'n drop. Les données sont affichées sous forme de barres verticales.
La génération du graphe se fait grâce à D3.js, une bibliothèque plus compliquée que d'autres qui permet cependant de faire plus de choses, plus en détails. L'usage ou le non-usage de cette bibliothèque n'a pas de conséquence sur mon problème.
Lorsque je passe mes données chiffrées en entrée du générateur de graphe, celui-ci procède à une mise à l'échelle de la hauteur, pour que chaque barre ait sa propre hauteur tout en s'assurant que la hauteur de la plus haute barre, égale à son ordonnée mise à l'échelle, ne dépasse pas la hauteur du graphe.
Je n'ai pas vraiment eu de difficulté pour mettre à l'échelle la hauteur. Vous trouverez en annexe le code qui s'en occupe. Par contre, je bute sur la mise à l'échelle de la largeur.
Problème
Je n'arrive pas à trouver la formule qui mette à l'échelle la largeur des barres :
- Chaque barre doit avoir sa propre largeur, égale à son abscisse mise à l'échelle (fait !);
- La largeur cumulée de chaque barre ne doit pas dépasser celle du graphe (échec !).
Le gros soucis que je rencontre, c'est donc que les barres dépassent horizontalement de mon graphe.
Ce que je souhaite obtenir
Voici le genre de graphe que je souhaite créer (considérez la largeur de chacune de ses barres, c'est ce que je veux mettre en place) :
Ce que j'ai obtenu
Note : sur chaque barre est inscrit "(<a>;<b>)". Bien entendu, <a> est l'abscisse et <b>, l'ordonnée.
Avec la version A du code JS (cf. la partie suivante) Les barres dépassent du graphe + notez la deuxième barre qui commence, à droite (elle commence par le haut, c'est étonnant… mais sûrement dû au problème de largeur).
Avec la version A-variante-1
C'est pour l'instant la version qui correspond le mieux à ce que je souhaite obtenir. Les largeurs me semblent tout à fait cohérentes ! Malheureusement, elles sont trop petites, et les barres ne remplissent pas le graphe…
Avec la version A-variante-2
Avec la version B Les largeurs ne sont pas cohérentes.
Avec la version C
Ce que j'ai codé jusqu'à maintenant pour essayer de résoudre mon problème*
Parent-direct du parent-direct des barres du graphe (note : leur parent-direct ne possède pas de code CSS):
1 2 3 4 5 6 7 8 9 10 11 | #graph { position : absolute; width : 100%; /* Important, non ? */ background : url("img/grille.png"), linear-gradient(#373737, #000); right : 0; left : 55px; top : 125px; bottom : 0; border-radius : 0 0 10px 0; } |
Code JS pour déterminer la largeur mise à l'échelle de chaque barre :
Indication : plusieurs versions sont à l'essai. Pour l'instant, aucune ne solutionne le problème.
Version A
1 2 3 4 5 6 7 8 9 10 | var width = $(this.css_selector).width(); // Récupération de la largeur du graphe var max_value_x = 0; for (var i = 0; i < this.array_JSON.length; i++) { // this.array_JSON = [{x, y}] où x : abscisse et y : ordonnée if (max_value_x < this.array_JSON[i].x) { // Cette boucle et cette condition servent juste à savoir quelle est la plus grande abscisse max_value_x = this.array_JSON[i].x; } } // Indication de la largeur de la barre qui va être créée : new_Node.style("width", function (data) { return data.x/max_value_x*width + "%"; }); // data : un élément de this.array_JSON |
Version A-variante-1
Modification à effectuer : return data.x/max_value_x*width + "%"; DEVIENT return data.x/max_value_x + "%";
Version A-variante-2
Modification à effectuer : return data.x/max_value_xwidth + "%"; DEVIENT return data.x/max_value_x(total_x/width) + "%";
Version B
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | var max_value_x = 0; var total_x = 0; var last_x = 0; for (var i = 0; i < this.array_JSON.length; i++) { if (max_value_x < this.array_JSON[i].x) { max_value_x = this.array_JSON[i].x; } total_x += this.array_JSON[i].x; this.array_JSON[i].width = this.array_JSON[i].x - last_x; last_x = this.array_JSON[i].x; } new_Node.style("width", function (data) { return data.width/total_x*100 + "%"; }); |
Version C :
1 2 3 4 5 6 7 8 9 10 | var width = $(this.css_selector).width(); var total_x = 0; for (var i = 0; i < this.array_JSON.length; i++) { total_x += this.array_JSON[i].x; } var width_divided = width/total_x; new_Node.style("width", function (data) { return data.x*width_divided + "%"; }); |
Annexes
Code JS complet du générateur (vous y trouverez la mise à l'échelle de la hauteur de chaque barre comme indiqué en début de ce message, ainsi que celle de la largeur, évidemment) :
Indication : ce code utilise la version A du JS (cf. partie précédente). Il est très facile de localiser les lignes à changer pour utiliser les autres versions !
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | Graph.prototype.generateGraph = function() { $(this.css_selector).empty(); $(this.css_selector).append("<section>"); var width = $(this.css_selector).width(); var height = $(this.css_selector).height(); var array_selected_nodes = d3.select(this.css_selector+">section:first-child").selectAll(this.d3_js_select_option); var update_Selection = array_selected_nodes.data(this.array_JSON); var min_value_y = 0; var max_value_y = 0; var max_value_x = 0; for (var i = 0; i < this.array_JSON.length; i++) { if (min_value_y > this.array_JSON[i].y) { min_value_y = this.array_JSON[i].y; } if (max_value_y < this.array_JSON[i].y) { max_value_y = this.array_JSON[i].y; } if (max_value_x < this.array_JSON[i].x) { max_value_x = this.array_JSON[i].x; } } var range_y = max_value_y - min_value_y; var height_coef = height / range_y; var enter_Selection = update_Selection.enter(); var new_Node = enter_Selection.append(this.d3_js_select_option); new_Node.style("background-color", function (data) { return "rgba(255,144,0, 0.5)"; }); new_Node.style("display", "inline-block"); new_Node.style("vertical-align", "bottom"); new_Node.style("color", "#FFF"); new_Node.style("width", function (data) { return data.x/max_value_x*width + "%"; }); new_Node.style("height", function (data) { return data.y*height_coef + "px"; }); new_Node.text(function(data) { return "(" + data.x+";"+data.y + ")"; }); new_Node.style("margin-right", "1px"); }; |
Conclusion
La formule que j'ai placée pour les largeurs donne des valeurs trop importantes, ce qui en conséquence retourne des pourcentages de largeur beaucoup trop grands. Du coup les données dépassent du graphe horizontalement.
Qu'est-ce qui ne va pas dans mon p'tit programme ?
Merci d'avance, et bonne journée !
Résolution
1 2 3 4 5 6 7 8 9 10 | Graph.prototype.generateGraph = function() { var total_x = 0; for (var i = 0; i < this.array_JSON.length; i++) { total_x += this.array_JSON[i].x; } new_Node.style("width", function (data) { return (data.x/total_x)*100 + "%"; }); }; |
Résultat visuel :
Largeurs cohérentes + les barres remplissent bien toute la largeur du graphe
* : partie sujette à des mises-à-jour + cette partie ne contient pas d'annexes (une partie leur est dédiée).