2012-08-08 5 views
51

Ich beginne mit d3.js und versuche, eine Reihe von Knoten zu erstellen, von denen jeder eine zentrierte Nummernbezeichnung enthält.Platzieren von Beschriftungen in der Mitte von Knoten in d3.js

Ich kann das gewünschte Ergebnis visuell erzeugen, aber die Art, wie ich es gemacht habe, ist kaum optimal, da es die x-y-Koordinaten für jedes Textelement fest codiert. Unten ist der 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; 
       }); 

Die node Klasse benutzerdefinierte CSS-Klasse-I getrennt für die circle Elemente definiert haben, während Klassen nodes und labels nicht explizit definiert und sie von diesem answer entlehnt sind.

Wie zu sehen ist, ist die Positionierung jedes Textlabels fest codiert, so dass es in der Mitte jedes Knotens erscheint. Offensichtlich ist dies nicht die richtige Lösung.

Meine Frage ist, wie sollte ich jede Textbeschriftung korrekt jedem Knoten-Kreis dynamisch zuordnen, so dass sich die Positionierung einer Beschriftung automatisch mit der eines Kreises ändert. Konzeptuelle Erklärungen sind mit Codebeispielen sehr willkommen.

+1

Textanker erscheinen nicht direkt noch in D3 zu arbeiten, aber haben Sie etwas so einfaches wie CSS text-align versucht? Dies sollte den Trick tun! http://www.w3schools.com/cssref/pr_text_text-align.asp Versuchen Sie auch, solche Posts aus der d3 Google-Gruppe zu sehen: https://groups.google.com/forum/#!searchin/d3 -js/text-align/d3-js/M6a-97ajkWs/rHJV4_WrhX0J% 5B1-25% 5D – paxRoman

+1

Woher kommen Sie, dass Textanker in d3 nicht funktionieren? –

+0

vor ein paar Monaten haben sie nicht funktioniert ... danke für die Klärung :) schön, eine einfache Annäherung an dieses Problem zu sehen – paxRoman

Antwort

61

Das Attribut text-anchor funktioniert wie erwartet auf einem von D3 erstellten Svg-Element. Sie müssen jedoch die text und die circle an ein gemeinsames g Element anhängen, um sicherzustellen, dass die text und die circle miteinander zentriert sind.

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 + ")"; 
      }); 

Beachten Sie, dass die dataset ist jetzt eine Liste der Objekte, so dass d.y und d.x können, anstatt nur eine Liste von Zeichenketten verwendet werden:

Um dies zu tun, können Sie Ihre nodes Variablen ändern.

Dann ersetzen Sie circle und text append Code mit dem folgenden:

// 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; 
     }); 

Statt nun die Position des sich ändernden circle Sie die Position des g Elements ändern, die sowohl die circle bewegt und die text .

Hier ist eine JSFiddle zeigt zentrierten Text auf Kreise.

Wenn Sie Ihren Text in einem separaten g Elemente haben, so dass es immer oben angezeigt wird, dann die Schöpfung in dem ersten g Elemente gesetzt d.x und d.y Werte verwenden Sie den Text zu transform.

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 + ")"; 
    }); 
+0

danke für die Antwort! Es scheint, dass Ihr Ansatz ähnlich dem ersten Ansatz in diesem [Antwort] (http://stackoverflow.com/questions/11102795/d3-node-labeling/11109803#11109803) ist, was bedeutet, dass jedes Etikett gezeichnet wird * oben nur für seinen Kreis *, wird aber * unter anderen Kreisen * gezeichnet. Ist es möglich, den Two-Data-Join-Ansatz zu verwenden, der in der gleichen Antwort beschrieben wird? (Das war, was ich meinen Versuch modelliert hatte). Vielen Dank! – skyork

+0

Ich habe ein Update dafür gemacht. Die zwei wichtigsten Dinge, die Sie ändern müssen, sind, Ihre Daten zu einer Liste von Objekten anstatt einer Liste von Strings zu machen, so dass 'y' und' x' für jede Zeichenkette gespeichert werden können, und Ihren Text in ein 'g' Element einfügen Das 'g' Element kann transformiert werden. –

+0

Wenn ich Ihren aktualisierten Code oben richtig verstanden habe, erstellen Sie zuerst ein 'g' Elemente und innerhalb der Sie eine Reihe von' g' Elemente für die Daten im Array erstellen, und dann erstellen Sie 'text' Elemente in jedem dieser' g' Element? Was mich ein wenig verwirrt, ist 'text.append (" svg: text ") ...', stellt 'text' hier die Reihe von' g' Elementen dar? Wenn ja, wie erstellt D3 ein 'text' Element für jedes dieser' g' Elemente *? Die Syntax ist ein bisschen irreführend. – skyork

23

Die beste Antwort came from the asker sich:

nur eine weitere Beobachtung: mit nur.attr ("text-anchor", "middle") für jedes Textelement ist das Label in der Mitte horizontal, aber leicht vertikal abgesetzt. Ich reparierte das durch Hinzufügen von attr ("y", ".3em") (entlehnt von Beispielen auf der Website d3.js), was gut zu funktionieren scheint sogar für beliebige Größe des Knotenkreises. Was genau dieses zusätzliche Attribut tut, entzieht sich meinem Verständnis. Sicher, es tut etwas an der Y-Koordinate jedes Textelements, aber warum .3em in insbesondere? Es scheint fast magisch zu mir ...

Fügen Sie einfach .attr("text-anchor", "middle") zu jedem Textelement hinzu.

Beispiel:

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

Ignoriere die ausgewählte Antwort. Dies ist bei weitem ein besserer Ansatz. – Matt

+0

Wow. Gut, dass ich zur nächsten Antwort gescrollt bin. Meine Faulheit, die gewählte Antwort nicht zu lesen, zahlte sich aus. – Martin

+0

Während dies verwendet wird, hat mein HTML den Text, aber es wird nicht in meinem tatsächlichen Knoten angezeigt. –