2013-05-05 74 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