2013-05-05 73 views
1

我试图做一个强制导向的布局,其中的链接是指向节点的箭头(如示例herehere所示),并且还有可以折叠的子节点(如Mike Bostock的示例所示: herehere)。D3js可定向路径的可折叠力布局?

到目前为止折叠节点工作正常,但我无法理解如何将箭头包含在路径中。下面是我的代码部分,根据上面的例子:

force.nodes(nodes) 
    .links(links) 
    .gravity(0.05) 
    .charge(-1500) 
    .linkDistance(100) 
    .friction(0.5) 
    .linkStrength(function(l, i) {return 1 }) 
    .size([w, h]) 
    .start(); 

// Append markers 
vis.append("svg:defs").selectAll("marker") 
    .data(["end"]) 
    .enter().append("svg:marker") 
    .attr("id", String) 
    .attr("viewBox", "0 -5 10 10") 
    .attr("refX", 15) 
    .attr("refY", -1.5) 
    .attr("markerWidth", 6) 
    .attr("markerHeight", 6) 
    .attr("orient", "auto") 
//.append("svg:path")  // <-- I not sure what this does 
    //.attr("d", "M0,-5L10,0L0,5"); 


var path = vis.selectAll("path") 
    .data(force.links()); 


// Enter new paths 
path.enter().insert("svg:path") 
    .attr("class", "link") 
    .attr("marker-end", "url(#end)") 
    .style("stroke", "#ccc"); 


// Exit any old paths. 
path.exit().remove(); 


// Update the nodes… 
var node = vis.selectAll("g.node") 
    .data(nodes, function(d) { return d.id; }) 

node.select("circle") 
    .style("fill", color); 

// Enter any new nodes. 
var nodeEnter = node.enter().append("svg:g") 
    .attr("class", "node") 
    .attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; }) 
    .on("click", click) 
    .call(force.drag); 


//Add an immage to the node 
nodeEnter.append("svg:image") 
     .attr("xlink:href", function(d) { return d.image;}) 
     .attr("x", function(d) { return (0 - Math.sqrt(d.size))/10 || 4.5;}) 
     .attr("y", function(d) { return (0 - Math.sqrt(d.size))/10 || 4.5;}) 
     .attr("height", 16) 
     .attr("width", 16); 


// Exit any old nodes. 
node.exit().remove(); 

// Re-select for update. 
node = vis.selectAll("g.node"); 
path = vis.selectAll("path") 

force.on("tick", function() { 
    // Draw curved links 
    path.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; 
    }); 

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

我明白的是,下面的代码段是负责绘制箭头头部,通过指定其中的箭头应指向的块(例如.data(["end"])

vis.append("svg:defs").selectAll("marker") 
    .data(["end"]) 
    .enter().append("svg:marker") 
    .attr("id", String) 
    .attr("viewBox", "0 -5 10 10") 
    .attr("refX", 15) 
    .attr("refY", -1.5) 
    .attr("markerWidth", 6) 
    .attr("markerHeight", 6) 
    .attr("orient", "auto") 
    .append("svg:path")  
    .attr("d", "M0,-5L10,0L0,5"); 

被输入的路径时,这然后被引用(即.attr("marker-end", "url(#end)");)。

但是我可能会错过一些东西,因为在我的图中显示了路径,但没有显示箭头。

感谢您的帮助!

+0

好问题!天哪,我在和那些箭头奋斗。我期待着看到这里出现的情况。 – d3noob 2013-05-05 18:19:01

+0

您在SVG标记定义中附加的“路径”负责绘制实际的箭头。你是否运行过你的代码(第一代码块)? – 2013-05-06 10:33:48

+0

谢谢@LarsKotthoff,在我发布这个问题后,我发现了这一点。在它未被注释之前它没有工作,并且因为我不明白它做了什么,所以我评论了它。写'path.link'几乎可以修复它。我把我的答案放在下面。然而,仍然有一些东西阻止它完全正常工作...... – djjupa 2013-05-06 20:01:46

回答

0

我发现了一个“几乎”工作的解决方案。下面是完整的代码,以及还有什么是在底部故障短路解释:

var w = 1280, 
     h = 800, 
     root, 
        vis; 

    var force = d3.layout.force() 
       .gravity(200) 
       .charge(-1500) 
       .linkDistance(100) 
       .friction(0.01) 
       .size([w, h]) 
       ; 


    $(document).ready(function() { 

     var newHeight = '100%'; 

     $("#svgdiv").html("<svg id='graph' xmlns='http://www.w3.org/2000/svg' width='100%' height='100%'></svg>"); 

     vis = d3.select("svg"); 

     d3.json("../json/flare.json", function(json) { 
      root = json; 
      root.fixed = true; 
      root.x = w/2; 
      root.y = h/2; 


     // Build the arrow 
     var defs = vis.insert("svg:defs").selectAll("marker") 
      .data(["end"]); 

      defs.enter().append("svg:marker") 
      .attr("id", String) 
      .attr("viewBox", "0 -5 15 15") 
      .attr("refX", 15) 
      .attr("refY", -1.5) 
      .attr("markerWidth", 6) 
      .attr("markerHeight", 6) 
      .attr("orient", "auto") 
      .append("svg:path") 
      .attr("d", "M0,-5L10,0L0,5");  

      update(); 
     }); 
    }); 



    /** 
    * 
    */ 
    function update() { 
     var nodes = flatten(root), 
      links = d3.layout.tree().links(nodes); 

     // Restart the force layout. 
     force.nodes(nodes) 
      .links(links) 
      .gravity(0.05) 
      .charge(-1500) 
      .linkDistance(100) 
      .friction(0.5) 
      .linkStrength(function(l, i) {return 1 }) 
      .size([w, h]) 
      .start(); 


     var path = vis.selectAll("path.link") // <-- THIS WAS CHANGED TO "path.links" 
      .data(links, function(d) { return d.target.id; }); 

      path.enter().insert("svg:path") 
      .attr("class", "link") 
      .attr("marker-end", "url(#end)") 
      .style("stroke", "#ccc"); 


     // Exit any old paths. 
     path.exit().remove(); 


     // Update the nodes… 
     var node = vis.selectAll("g.node") 
      .data(nodes, function(d) { return d.id; }); 

     // Enter any new nodes. 
     var nodeEnter = node.enter().insert("svg:g") 
      .attr("class", "node") 
      .attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; }) 
      .on("click", click) 
      .call(force.drag); 


     node.select("circle") 
      .style("fill", color); 


     nodeEnter.append("svg:circle") 
      .attr("r", function(d) { return Math.sqrt(d.size)/10 || 4.5; }) 
      .style("fill", color); 

     // Add text to the node (as defined by the json file) 
     nodeEnter.append("svg:text") 
      .attr("text-anchor", "middle") 
      .attr("dx", function(d) { return Math.sqrt(d.size)/10 || 4.5; }) 
      .attr("dy", ".35em") 
      .text(function(d) { return d.name; }); 
     /* */ 

     //Add an image to the node 
     nodeEnter.append("svg:image") 
       .attr("xlink:href", function(d) { return d.logo;}) 
       .attr("x", function(d) { return (0 - Math.sqrt(d.size))/10 || 4.5;}) 
       .attr("y", function(d) { return (0 - Math.sqrt(d.size))/10 || 4.5;}) 
       .attr("height", 16) 
       .attr("width", 16); 
     /* */  


     // Exit any old nodes. 
     node.exit().remove(); 

     // Re-select for update. 
     node = vis.selectAll("g.node"); 
     path = vis.selectAll("path.link"); // <-- THIS WAS CHANGED TO "path.link" 


     force.on("tick", function() { 


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


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

     }); 

    } 


    // Color leaf nodes orange, and packages white or blue. 
    function color(d) { 
     return d._children ? "#3182bd" : d.children ? "#c6dbef" : "#fd8d3c"; 
    } 

    // Toggle children on click. 
    function click(d) { 
     if (d.children) { 
     d._children = d.children; 
     d.children = null; 
     } else { 
     d.children = d._children; 
     d._children = null; 
     } 

     update(); 
    } 



    // Returns a list of all nodes under the root. 
    function flatten(root) { 
     var nodes = []; 
     var i = 0; 

     function recurse(node) { 
     if (node.children) 
      node.children.forEach(recurse); 
     if (!node.id) 
      node.id = ++i; 
     nodes.push(node); 
     } 

     recurse(root); 
     return nodes; 
    } 

我认为那箭没有显示的是,我给了一个类的路径,作为原因之一path.enter().insert("svg:path").attr("class", "link"),但所选择的路径是,当我没有正确地引用它,因此没有引起它
(即我有:

var path = vis.selectAll("path") 

,它应该已经:

var path = vis.selectAll("path.link").) 

然后我还发现应该在update()函数之外定义箭头标记的defs,这是在折叠和展开节点时调用的。否则,每次点击某个节点时,它都会将其附加到svg,并且这不是非常有效。

所以现在节点崩溃onclick和箭头绘制(虽然他们是丑陋的)。但是,仍然有一个问题让我非常困惑:一段时间后,没有任何明显的时间间隔或点击模式,图表冻结并在浏览器中调试时显示Cannot read property 'target' of undefined。这个错误被触发的“嘀”的功能,在这里被定义曲线路径:

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

它没有找到d.target因为路径变量进入force.on("tick", function() ...

之前重新开始(这样path = vis.selectAll("path.link");)奇怪的是,它在一开始就有效,突然之后可能会停止工作! 因此,任何机构都不知道会发生什么?

编辑:

我现在知道什么是错的。出于某种原因,我正在使用脚本d3.layout.js,我在某处找到了,我认为这是崩溃树所需要的。我删除了这个库,并使用了正常的d3.v3.js,并且所有的工作都像它应该......只是箭头很丑。所以,上面的脚本应该可以工作,具有可折叠节点和定向路径。

+0

我创建了一个要显示实例的要点:[http://bl.ocks.org/djjupa/5653692](http://bl。 ocks.org/djjupa/5653692) – djjupa 2013-05-27 07:21:49