2012-08-08 33 views
51

Estoy empezando con d3.js y estoy intentando crear una fila de nodos, cada uno de los cuales contiene una etiqueta de número centrado.Colocación de etiquetas en el centro de los nodos en d3.js

Soy capaz de producir el resultado deseado visualmente, pero la forma en que lo hice no es óptima, ya que implica la codificación rigurosa de las coordenadas x y para cada elemento de texto. A continuación se muestra el código:

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

El node clase es la clase CSS personalizado He definido por separado para los elementos circle, mientras que las clases nodes y labels no están explícitamente definidos y que son tomados de esta answer.

Como se ve, el posicionamiento de cada etiqueta de texto está codificado de manera que aparece en el centro de cada nodo. Obviamente, esta no es la solución correcta.

Mi pregunta es cómo debería asociar correctamente cada etiqueta de texto con cada círculo de nodo dinámicamente de modo que si el posicionamiento de una etiqueta cambia junto con el de un círculo automáticamente. La explicación conceptual es extremadamente bienvenida con un ejemplo de código.

+1

anclajes de texto parecen no funcionar directamente en D3 aún, pero sí que ha intentado algo tan simple como CSS text-align? ¡Esto debería funcionar! http://www.w3schools.com/cssref/pr_text_text-align.asp También intente echar un vistazo a publicaciones como estas del grupo de Google d3: https://groups.google.com/forum/#!searchin/d3 -js/text-align/d3-js/M6a-97ajkWs/rHJV4_WrhX0J% 5B1-25% 5D – paxRoman

+1

¿De dónde sacas la idea de que los anclajes de texto no funcionan en d3? –

+0

hace unos meses no funcionó ... gracias por aclarar :) bueno ver un enfoque simple para este problema – paxRoman

Respuesta

61

El atributo text-anchor funciona como se esperaba en un elemento svg creado por D3. Sin embargo, debe agregar text y circle en un elemento común g para asegurarse de que el text y el circle estén centrados entre sí.

Para ello, puede cambiar la variable de 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 + ")"; 
      }); 

Tenga en cuenta que la dataset es ahora una lista de objetos para que d.y y d.x se puede utilizar en lugar de sólo una lista de cadenas.

A continuación, reemplace el código de agregación circle y text con lo siguiente:

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

Ahora, en lugar de cambiar la posición de la circle cambia la posición del elemento g que se mueve tanto el circle y la text .

Aquí hay un JSFiddle que muestra el texto centrado en los círculos.

Si usted quiere tener su texto sea en un g elemento separado de modo que siempre aparece en la parte superior, a continuación, utilizar los d.x y d.y valores establecidos en la creación del primer elemento de g a transform el texto.

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

gracias por la respuesta! Parece que su enfoque es similar al primer enfoque descrito en esta [respuesta] (http://stackoverflow.com/questions/11102795/d3-node-labeling/11109803#11109803), lo que significa que cada etiqueta se dibujará * en la parte superior solo para su círculo *, pero se dibujará * debajo de otros círculos *. ¿Es posible hacerlo utilizando el enfoque de unión de dos datos descrito en la misma respuesta? (que fue lo que modelé mi intento). ¡Gracias! – skyork

+0

Hice una actualización para esto. Las dos cosas principales que necesita cambiar son hacer que sus datos sean una lista de objetos en lugar de una lista de cadenas para que 'y' y' x' puedan almacenarse para cada cadena, y poner su texto dentro de un elemento 'g' para que el elemento 'g' puede transformarse. –

+0

si entendí correctamente su código actualizado arriba, primero crea un elemento 'g' y dentro del cual crea una serie de elementos' g' para los datos en la matriz, y luego crea elementos 'text' dentro de cada uno de ellos' g elemento? Lo que me confunde un poco es 'text.append (" svg: text ") ...', ¿'text' aquí representa la serie de elementos' g'? Si es así, ¿cómo crea D3 un elemento 'text' para * cada uno de estos elementos' g' *? La sintaxis es un poco engañosa. – skyork

23

La mejor respuesta came from the asker mismo:

sólo una observación más: con solamente.attr ("text-anchor", "middle") para cada elemento de texto, la etiqueta está en el centro horizontalmente pero ligeramente vertical. Lo arreglé agregando attr ("y", ".3em") (tomado de ejemplos en el sitio web d3.js), que parece funcionar bien incluso para el tamaño arbitrario del círculo de nodos. Sin embargo, qué exactamente este atributo adicional elude mi comprensión. Claro, hace algo a la coordenada y de cada elemento de texto, pero ¿por qué .3em en particular? Me parece casi mágico ...

Simplemente agregue .attr("text-anchor", "middle") a cada elemento de texto.

Ejemplo:

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

Ignora la respuesta seleccionada. Este es de lejos un mejor enfoque. – Matt

+0

Wow. Menos mal que pasé a la siguiente respuesta. Mi pereza por no leer la respuesta elegida valió la pena. – Martin

+0

Mientras uso esto, mi HTML tiene el texto pero no aparece en mi nodo actual. –