web-dev-qa-db-fra.com

Placer des étiquettes au centre des nœuds dans d3.js

Je commence par d3.js, et j'essaie de créer une ligne de nœuds dont chacun contient une étiquette numérique centrée.

Je suis capable de produire visuellement le résultat souhaité, mais la façon dont je l'ai fait n'est pas optimale car cela implique de coder en dur les coordonnées x-y pour chaque élément de texte. Voici le code:

var svg_w = 800;
var svg_h = 400;
var svg = d3.select("body")
    .append("svg")
    .attr("width", svg_w)
    .attr("weight", svg_h);

var dataset = [];
for (var i = 0; i < 6; i++) {
    var datum = 10 + Math.round(Math.random() * 20);
    dataset.Push(datum);
}

var nodes = svg.append("g")
               .attr("class", "nodes")
               .selectAll("circle")
               .data(dataset)
               .enter()
               .append("circle")
               .attr("class", "node")
               .attr("cx", function(d, i) {
                   return (i * 70) + 50;
               })
               .attr("cy", svg_h / 2)
               .attr("r", 20);

var labels = svg.append("g")
                .attr("class", "labels")
                .selectAll("text")
                .data(dataset)
                .enter()
                .append("text")
                .attr("dx", function(d, i) {
                    return (i * 70) + 42
                })
                .attr("dy", svg_h / 2 + 5)
                .text(function(d) {
                    return d;
                });

La classe node est une classe CSS personnalisée que j'ai définie séparément pour les éléments circle, tandis que les classes nodes et labels ne sont pas explicitement définies et elles sont empruntées à partir de cela réponse .

Comme vu, le positionnement de chaque étiquette de texte est codé en dur afin qu'il apparaisse au centre de chaque nœud. De toute évidence, ce n'est pas la bonne solution.

Ma question est de savoir comment dois-je associer correctement chaque étiquette de texte à chaque cercle de nœuds de manière à ce que si le positionnement d'une étiquette change automatiquement avec celui d'un cercle. L'explication conceptuelle est extrêmement bienvenue avec l'exemple de code.

54
skyork

L'attribut text-anchor fonctionne comme prévu sur un élément svg créé par D3. Cependant, vous devez ajouter le text et le circle dans un élément g commun pour vous assurer que le text et le circle sont centrés les uns avec les autres.

Pour ce faire, vous pouvez remplacer votre variable nodes par:

var nodes = svg.append("g")
           .attr("class", "nodes")
           .selectAll("circle")
           .data(dataset)
           .enter()
           // Add one g element for each data node here.
           .append("g")
           // Position the g element like the circle element used to be.
           .attr("transform", function(d, i) {
             // Set d.x and d.y here so that other elements can use it. d is 
             // expected to be an object here.
             d.x = i * 70 + 50,
             d.y = svg_h / 2;
             return "translate(" + d.x + "," + d.y + ")"; 
           });

Notez que dataset est maintenant une liste d'objets de sorte que d.y et d.x peut être utilisé au lieu d'une simple liste de chaînes.

Ensuite, remplacez votre code d'ajout circle et text par le code suivant:

// Add a circle element to the previously added g element.
nodes.append("circle")
      .attr("class", "node")
      .attr("r", 20);

// Add a text element to the previously added g element.
nodes.append("text")
     .attr("text-anchor", "middle")
     .text(function(d) {
       return d.name;
      });

Maintenant, au lieu de changer la position de circle, vous changez la position de l'élément g qui déplace à la fois le circle et le text.

Voici un JSFiddle montrant le texte centré sur les cercles.

Si vous souhaitez que votre texte se trouve dans un élément g distinct afin qu'il apparaisse toujours en haut, utilisez le d.x et d.y valeurs définies dans la création du premier élément g pour transform le texte.

var text = svg.append("svg:g").selectAll("g")
         .data(force.nodes())
         .enter().append("svg:g");

text.append("svg:text")
    .attr("text-anchor", "middle")
    .text(function(d) { return d.name; });

text.attr("transform",  function(d) {
      return "translate(" + d.x + "," + d.y + ")"; 
    });
63
Brant Olsen

La meilleure réponse venue du demandeur lui-même:

juste une observation supplémentaire: avec seulement .attr ("text-anchor", "middle") pour chaque élément de texte, l'étiquette est au milieu horizontalement mais légèrement décalée verticalement. J'ai corrigé cela en ajoutant attr ("y", ".3em") (emprunté à des exemples sur le site Web d3.js), ce qui semble bien fonctionner même pour une taille arbitraire de cercle de nœuds. Cependant, ce que cet attribut supplémentaire fait exactement échappe à ma compréhension. Bien sûr, cela fait quelque chose à la coordonnée y de chaque élément de texte, mais pourquoi .3em en particulier? Cela me semble presque magique ...

Ajoutez simplement .attr("text-anchor", "middle") à chaque élément de texte.

Exemple:

node.append("text")
    .attr("x", 0)
    .attr("dy", ".35em")
    .attr("text-anchor", "middle")
    .text(function(d) { return d.name; });
31
tacone

Cette page décrit ce qui se passe sous le capot svg en ce qui concerne les éléments de texte. Comprendre la machinerie et les structures de données sous-jacentes m'a aidé à mieux comprendre comment j'ai dû modifier mon code pour le faire fonctionner.

4
MichaelGreene