2012-08-08 16 views
51

Sto iniziando con d3.js e sto provando a creare una fila di nodi ognuno dei quali contiene un'etichetta con il numero centrato.Posizionamento delle etichette al centro dei nodi in d3.js

Sono in grado di produrre visivamente il risultato desiderato, ma il modo in cui l'ho fatto è difficilmente ottimale in quanto implica la codifica hard delle coordinate x-y per ciascun elemento di testo. Di seguito è riportato il codice:

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

Il node classe è classe CSS personalizzata ho definito separatamente per i circle elementi, mentre le classi nodes e labels non sono esplicitamente definiti e sono presi in prestito da questo answer.

Come visto, il posizionamento di ciascuna etichetta di testo è hardcoded in modo che appaia al centro di ciascun nodo. Ovviamente, questa non è la soluzione giusta.

La mia domanda è che come dovrei associare correttamente ogni etichetta di testo a ogni cerchio di nodi dinamicamente in modo che se il posizionamento di un'etichetta cambia insieme a quello di un cerchio automaticamente. La spiegazione concettuale è estremamente gradita nell'esempio di codice.

+1

ancoraggi di testo sembrano non funzionare direttamente in D3 ancora, ma ha fatto si è tentato qualcosa di semplice come CSS text-align? Questo dovrebbe fare il trucco! http://www.w3schools.com/cssref/pr_text_text-align.asp Prova anche a dare un'occhiata ai post di questo tipo dal gruppo Google d3: https://groups.google.com/forum/#!searchin/d3 -js/text-align/d3-js/M6a-97ajkWs/rHJV4_WrhX0J% 5B1-25% 5D – paxRoman

+1

Dove ti viene l'idea che gli ancoraggi di testo non funzionano in d3? –

+0

pochi mesi fa non hanno funzionato ... grazie per il chiarimento :) bello vedere un approccio semplice a questo problema – paxRoman

risposta

61

L'attributo text-anchor funziona come previsto su un elemento svg creato da D3. Tuttavia, è necessario aggiungere text e circle a un elemento comune g per garantire che lo text e lo circle siano centrati l'uno con l'altro.

Per fare questo, è possibile modificare la variabile nodes a:

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

Si noti che il dataset è ora un elenco di oggetti in modo che d.y e d.x può essere usato invece di una lista di stringhe.

Poi, sostituire la circle e text codice di accodamento con il seguente:

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

Ora, invece di cambiare la posizione del circle si cambia la posizione dell'elemento g che muove sia la circle e text .

Ecco un JSFiddle che mostra il testo centrato sui cerchi.

Se si desidera avere il testo sia in un g elemento separato in modo che appaia sempre in primo piano, quindi utilizzare i d.x e d.y valori impostati nella creazione del primo g dell'elemento per transform il testo.

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

grazie per la risposta! Sembra che il tuo approccio sia simile al primo approccio delineato in questa [risposta] (http://stackoverflow.com/questions/11102795/d3-node-labeling/11109803#11109803), il che significa che ogni etichetta verrà disegnata * in alto solo per il suo cerchio *, ma verrà disegnato * sotto altri cerchi *. È possibile farlo usando l'approccio di due dati-join delineato nella stessa risposta? (che era quello che ho modellato il mio tentativo su). Grazie! – skyork

+0

Ho fatto un aggiornamento per questo. Le due cose principali che devi modificare sono rendere i tuoi dati un elenco di oggetti invece di un elenco di stringhe in modo che 'y' e' x' possano essere memorizzati per ogni stringa e mettere il tuo testo all'interno di un elemento 'g' in modo che l'elemento 'g' può essere trasformato. –

+0

se ho capito correttamente il codice aggiornato sopra, per prima cosa crei un elemento 'g' e all'interno del quale crei una serie di elementi' g' per i dati nell'array, e poi crei gli elementi 'text' all'interno di ognuno di questi' elemento g'? Ciò che mi confonde un po 'è 'text.append (" svg: text ") ...', 'testo' qui rappresenta la serie di elementi' g'? In tal caso, in che modo D3 crea un elemento 'text' per * ciascuno di questi elementi' g' *? La sintassi è un po 'fuorviante. – skyork

23

La migliore risposta came from the asker se stesso:

solo un ulteriore osservazione: con solo.attr ("text-anchor", "middle") per ogni elemento di testo, l'etichetta è al centro in orizzontale ma leggermente in verticale. Ho risolto questo problema aggiungendo attr ("y", ".3em") (preso a prestito da esempi sul sito d3.js), che sembra funzionare bene anche per dimensioni arbitrarie del cerchio del nodo. Tuttavia, che cosa esattamente questo attributo aggiuntivo elude la mia comprensione. Certo, lo fa qualcosa alla coordinata y di ogni elemento di testo, ma perché .3em in particolare? Mi sembra quasi magico ...

Basta aggiungere .attr("text-anchor", "middle") a ciascun elemento di testo.

Esempio:

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

Ignora la risposta selezionata. Questo è di gran lunga un approccio migliore. – Matt

+0

Wow. Per fortuna ho fatto scorrere fino alla prossima risposta. La mia pigrizia per non aver letto la risposta scelta è stata ripagata. – Martin

+0

Durante l'utilizzo, il mio codice HTML ha il testo ma non viene visualizzato nel mio vero nodo. –