2011-12-29 3 views
22

Ho lavorato a un grafico di modifica della forza modificato e ho riscontrato alcuni problemi con l'aggiunta di testo/etichetta sui collegamenti in cui i collegamenti non sono allineati correttamente ai nodi. Come sistemarlo?Aggiungi testo/etichetta sui collegamenti nel grafico diretto forzato D3

E come posso aggiungere un listener di eventi a un elemento di testo SVG? L'aggiunta di .on("dblclick",function(d) {....} non funziona.

Ecco il frammento di codice:

<style type="text/css"> 
    .link { stroke: #ccc; } 
    .routertext { pointer-events: none; font: 10px sans-serif; fill: #000000; } 
    .routertext2 { pointer-events: none; font: 9px sans-serif; fill: #000000; } 
    .linktext { pointer-events: none; font: 9px sans-serif; fill: #000000; } 
</style> 

<div id="canvas"> 
</div> 

<script type="text/javascript" src="d3/d3.js"></script> 
<script type="text/javascript" src="d3/d3.layout.js"></script> 
<script type="text/javascript" src="d3/d3.geo"></script> 
<script type="text/javascript" src="d3/d3.geom.js"></script> 


<script type="text/javascript"> 

var w = 960, 
    h = 600, 
    size = [w, h]; // width height  
var vis = d3.select("#canvas").append("svg:svg") 
    .attr("width", w) 
    .attr("height", h) 
    .attr("transform", "translate(0,0) scale(1)") 
    .call(d3.behavior.zoom().on("zoom", redraw)) 
    .attr("idx", -1) 
    .attr("idsel", -1) 
    ; 

var routers = { 
    nodes: [ 
     {id:0, name:"ROUTER-1", group:1, ip: "123.123.123.111", 
      x:394.027, y:450.978,outif:"ge-0/1/0.0",inif:""}, 
     {id:1, name:"ROUTER-2", group:1, ip: "123.123.123.222", 
      x:385.584, y:351.513,outif:"xe-4/2/0.0",inif:"ge-5/0/3.0"}, 
     {id:2, name:"ROUTER-3", group:1, ip: "123.123.123.333", 
      x:473.457, y:252.27,outif:"ae1.0",inif:"xe-1/0/1.0"}, 
     {id:3, name:"ROUTER-4", group:2, ip: "123.123.123.444", 
      x:723.106, y:266.569,outif:"as0.0",inif:"ae1.0"}, 
     {id:4, name:"ROUTER-5", group:3, ip: "123.123.123.555", 
      x:728.14, y:125.287,outif:"so-4/0/2.0",inif:"as1.0"}, 
     {id:5, name:"ROUTER-6", group:3, ip: "123.123.123.666", 
      x:738.975, y:-151.772,outif:"",inif:"PO0/2/2/1" } 
    ], 
    links: [ 
     {source:0, target:1, value:3, name:'link-1',speed:"1000mbps", 
      outif:"ge-0/1/0.0",nextif:"ge-5/0/3.0"}, 
     {source:1, target:2, value:3, name:'link-2',speed:"10Gbps", 
      outif:"xe-4/2/0.0",nextif:"xe-1/0/1.0"}, 
     {source:2, target:3, value:3, name:'link-3',speed:"20Gbps", 
      outif:"ae1.0",nextif:"xe-1/2/1.0"}, 
     {source:3, target:4, value:3, name:'link-4',speed:"1Gbps", 
      outif:"as0.0",nextif:"as1.0"}, 
     {source:4, target:5, value:3, name:'link-5',speed:"OC3", 
      outif:"so-4/0/2.0",nextif:"PO0/2/2/1"} 
    ] 
}; 

var force = d3.layout.force() 
     .nodes(routers.nodes) 
     .links(routers.links) 
     .gravity(0) 
     .distance(100) 
     .charge(0) 
     .size([w, h]) 
     .start(); 

var link = vis.selectAll("g.link") 
     .data(routers.links) 
     .enter().append("svg:g"); 

    link.append("svg:line") 
     .attr("class", "link") 
     .attr("title", function(d) { return "From: "+d.outif+", To: "+d.nextif }) 
     .attr("style", "stroke:#00d1d6;stroke-width:4px") 
     .attr("x1", function(d) { return d.source.x; }) 
     .attr("y1", function(d) { return d.source.y; }) 
     .attr("x2", function(d) { return d.target.x; }) 
     .attr("y2", function(d) { return d.target.y; }); 

    link.append("svg:text") 
     .attr("class", "linktext") 
     .attr("dx", function(d) { return d.source.x; }) 
     .attr("dy", function(d) { return d.source.y; }) 
     .text("some text to add..."); 

    var node = vis.selectAll("g.node") 
     .data(routers.nodes) 
    .enter() 
     .append("svg:g") 
     .attr("id", function(d) { return d.id;}) 
     .attr("title", function(d) {return d.ip}) 
     .attr("class", "node") 
     .attr("x", function(d) { return d.x; }) 
     .attr("y", function(d) { return d.y; }) 
     .on("dblclick",function(d) { 
      alert('router double-clicked'); d3.event.stopPropagation(); 
     }) 
     .on("mousedown", function(d) { 
      if (d3.event.which==3) { 
       d3.event.stopPropagation(); 
       alert('Router right-clicked'); 
      } 
     }) 
     .call(force.drag); 

    node.append("svg:image") 
     .attr("class", "node") 
     .attr("xlink:href", "router.png") 
     .attr("x", -24) 
     .attr("y", -18) 
     .attr("width", 48) 
     .attr("height", 36); 

    node.append("svg:text") 
     .attr("class", "routertext") 
     .attr("dx", -30) 
     .attr("dy", 20) 
     .text(function(d) { return d.name }); 

    node.append("svg:text") 
     .attr("class", "routertext2") 
     .attr("dx", 0) 
     .attr("dy", -20) 
     .attr("title", "some title to show....") 
     .text(function(d) { return d.outif }) 
     .on("click", function(d,i) {alert("outif text clicked");}) 
     .call(force.drag); 

    node.append("svg:text") 
     .attr("class", "routertext2") 
     .attr("dx", -40) 
     .attr("dy", 30) 
     .text(function(d) { return d.inif }); 

    force.on("tick", function() { 
    link.attr("x1", function(d) { return d.source.x; }) 
     .attr("y1", function(d) { return d.source.y; }) 
     .attr("x2", function(d) { return d.target.x; }) 
     .attr("y2", function(d) { return d.target.y; }); 

    node.attr("transform", function(d) { 
     return "translate(" + d.x + "," + d.y + ")"; }); 
    }); 

    function redraw() { 
    vis.attr("transform", 
     "translate(" + d3.event.translate + ")" 
     + "scale(" + d3.event.scale + ")"); 
    }; 

</script> 

risposta

17

Avete sperimentato con la creazione di elementi di testo a parte in uno standalone (più semplice) esempio? Dovrebbe darti un'idea migliore di come i diversi attributi controllano il posizionamento.

Per l'allineamento verticale, utilizzare l'attributo "dy":

  • predefinita, la linea di base del testo è all'origine (allineata in basso)
  • una dy di .35em centra il testo verticalmente
  • un dy di .72em pone la linea superiore del testo all'origine (top-allineati)

Utilizzando unità EM è bello perché si scala automaticamente in base alla dimensione del carattere. Se non si specificano le unità (ad esempio -20 nel codice), per impostazione predefinita i pixel.

per l'allineamento orizzontale, utilizzare l'attributo "text-anchor":

  • L'impostazione predefinita è "start" (allineato a sinistra per le lingue da destra da sinistra a)
  • "middle"
  • "fine"

C'è anche l'attributo "dx", che è allettante da utilizzare per il riempimento. Tuttavia, non lo consiglierei perché c'è un bug in Firefox e Opera che lo fa non funzionare come previsto in congiunzione con il centro di ancoraggio del testo o la fine.

-1

Basta aggiungere questa riga:

.attr("text-anchor", "middle") 

al codice dopo la riga:

node.append("svg:text") 

dovrebbe apparire in questo modo:

node.append("svg:text") 
.attr("text-anchor", "middle") 
...... 
+1

Questo sarà centrare il testo all'interno di un nodo, ma credo che la questione sta chiedendo per centrare il testo su un link. – dchang

5

Sto usando un arco come collegamento tra i nodi con un testo dell'etichetta posizionato al centro. Ecco un frammento di codice:

var vis = d3.select("body") 
    .append("svg") 
    .attr("width", 600) 
    .attr("height", 400) 
    .append("g"); 

    var force = d3.layout.force() 
    .gravity(.05) 
    .distance(120) 
    .charge(-100) 
    .size([600, 400]); 
    var nodes = force.nodes(), links = force.links(); 

    // make an arch between nodes and a text label in the middle 
    var link = vis.selectAll("path.link").data(links, function(d) { 
     return d.source.node_id + "-" + d.target.node_id; }); 
    link.enter().append("path").attr("class", "link"); 

    var linktext = vis.selectAll("g.linklabelholder").data(links); 
    linktext.enter().append("g").attr("class", "linklabelholder") 
    .append("text") 
    .attr("class", "linklabel") 
    .attr("dx", 1) 
    .attr("dy", ".35em") 
    .attr("text-anchor", "middle") 
    .text(function(d) { return "my label" }); 

    // add your code for nodes .... 

    force.on("tick", tick); force.start(); 

    function tick() { 
    // curve 
    link.attr("d", function(d) { 
     var dx = d.target.x - d.source.x, 
     dy = d.target.y - d.source.y, 
     dr = Math.sqrt(dx * dx + dy * dy); 
    return "M" + d.source.x + "," + d.source.y + "A" + dr + "," 
     + dr + " 0 0,1 " + d.target.x + "," + d.target.y; 
    });  
    // link label 
    linktext.attr("transform", function(d) { 
     return "translate(" + (d.source.x + d.target.x)/2 + "," 
     + (d.source.y + d.target.y)/2 + ")"; }); 
    // nodes 
    node.attr("transform", function(d) { 
     return "translate(" + d.x + "," + d.y + ")"; }); 
    } 
18

enter image description here

Utilizzare un esempio più piccolo al di fuori della D3 per vedere come funziona la roba SVG. Quindi ricostruisci questa struttura utilizzando D3 e i tuoi dati personalizzati.

<html> 
    <body> 

<svg width="600px" height="400px"> 

    <defs> 
     <!-- DEFINE AN ARROW THAT WE CAN PLACE AT THE END OF EDGES. --> 
     <!-- USE REFX TO MOVE THE ARROW'S TIP TO THE END OF THE PATH. --> 
     <marker 
      orient="auto" 
      markerHeight="12" 
      markerWidth="12" 
      refY="0" 
      refX="9" 
      viewBox="0 -5 10 10" 
      id="ARROW_ID" 
      style="fill: red; fill-opacity: 0.5;"> 

      <path d="M0, -5L10, 0L0, 5"></path> 

     </marker> 
    </defs> 

    <!-- DEFINE A PATH. SET ITS END MARKER TO THE ARROW'S ID. --> 
    <!-- SET FILL NONE TO DRAW A LINE INSTEAD OF A SHAPE. --> 
    <path 
     d="M100,100 A300,250 0 0,1 500,300" 
     style="fill:none; stroke:grey; stroke-width:2px;" 
     id="PATH_ID" 
     marker-end="url(#ARROW_ID)" /> 

    <!-- DEFINE A TEXT ELEMENT AND SET FONT PROPERTIES. --> 
    <!-- USE DY TO MOVE TEXT ABOVE THE PATH. --> 
    <text 
     style="text-anchor:middle; font: 16px sans-serif;" 
     dy="-12"> 

     <!-- DEFINE A TEXT PATH FOLLOWING THE PATH DEFINED ABOVE. --> 
     <!-- USE STARTOFFSET TO CENTER TEXT. --> 
     <textPath 
      xlink:href="#PATH_ID" 
      startOffset="50%">Centered edge label</textPath> 
    </text> 

</svg> 

    </body> 
</html> 
+0

Come con 'textPath', SVG distingue tra maiuscole e minuscole (almeno in Chrome), quindi l'attributo dovrebbe leggere' startOffset'. –

+1

Grazie per aver segnalato, ho aggiornato la mia risposta. – Jack

10

Creato JS esempio violino per la visualizzazione di etichette su collegamenti in tabella di layout D3 forzata

Vedi demo funzionante in JS Fiddle: http://jsfiddle.net/bc4um7pc/

Dare Id al percorso come di seguito

var path = svg.append("svg:g").selectAll("path") 
    .data(force.links()) 
    .enter().append("svg:path") 
    .attr("class", function(d) { return "link " + d.type; }) 
    .attr("id",function(d,i) { return "linkId_" + i; }) 
    .attr("marker-end", function(d) { return "url(#" + d.type + ")"; }); 

Utilizzare l'elemento textGath SVG per associare le etichette ai collegamenti precedenti specificandone il relativo attributo 'xlink: href' per puntare al rispettivo link/percorso.

var linktext = svg.append("svg:g").selectAll("g.linklabelholder").data(force.links()); 
    linktext.enter().append("g").attr("class", "linklabelholder") 
    .append("text") 
    .attr("class", "linklabel") 
    .style("font-size", "13px") 
    .attr("x", "50") 
    .attr("y", "-20") 
    .attr("text-anchor", "start") 
    .style("fill","#000") 
    .append("textPath") 
    .attr("xlink:href",function(d,i) { return "#linkId_" + i;}) 
    .text(function(d) { 
     return "my text"; //Can be dynamic via d object 
    });