2011-12-29 53 views
22

我一直在研究修改后的强制有向图,并在链接未正确对齐节点的链接上添加文本/标签时遇到了一些问题。如何解决它?将文本/标签添加到D3强制定向图中的链接上

我如何添加一个事件监听器到一个SVG文本元素?添加.on("dblclick",function(d) {....}只是不起作用。

下面的代码片段:

<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> 

回答

17

你有没有在一个独立的(简单)的例子分别创建文本元素实验?它应该让你更好地了解不同属性如何控制定位。

对于垂直取向,使用“DY”属性:

  • 默认情况下,文字的基线是在
  • .35em的DY垂直居中文本原点(底部对齐)
  • .72em的DY放置在原点文本的背线(顶部对齐)

使用em单位是不错的,因为它会根据字体的大小自动调整。如果您不指定单位(如代码中的-20),则默认为像素。

水平取向,使用“文本锚点”属性:

  • 默认是“开始”(左到右语言左对齐)
  • “中间”
  • “end”

还有“dx”属性,它很适合用于填充。然而,我不会推荐它,因为Firefox和Opera中存在一个错误,导致它无法按预期与文本锚中间或结尾一起工作。

-1

只需加上此行:

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

的代码行后:

node.append("svg:text") 

它应该是这样的:

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

这此链接将一个节点内的文本居中,但我相信这个问题是问到链接文本居中。 – dchang 2012-12-18 23:57:17

5

我使用的是弓作为标签文本位于中间的节点之间的链接。下面的代码片段:

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

使用D3之外的小例子来看看SVG的东西是如何工作的。然后用D3和你的自定义数据重建这个结构。

<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

与'textPath'类似,SVG区分大小写(至少在Chrome中),所以属性应该读为'startOffset'。 – 2015-01-06 13:47:03

+1

感谢您指出,我更新了我的答案。 – Jack 2015-01-06 15:10:21

10

表示跨越在D3强制布局图链接标签创建JS提琴例如

见JS小提琴工作演示:http://jsfiddle.net/bc4um7pc/

给标识的到您的路径类似下面

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

使用SVG textPath元素将标签与上述链接关联,方法是指定其标签'xlink:href'属性指向其各自的链接/路径。

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