2013-08-30 107 views
1

我想創建一個使用d3和強制佈局的圖。我用下面的例子http://bl.ocks.org/mbostock/1062288上手:d3js強制佈局隱藏/取消隱藏節點點擊錯位節點擴展後

enter image description here

我還需要圖像和標籤,所以我看着這個例子http://bl.ocks.org/mbostock/950642得到一個想法,我怎麼能添加它們。

根據用戶與節點的交互情況,我的圖形也會變大,所以如果用戶點擊沒有孩子的節點,ajax請求將轉到後端服務以請求更多節點。該圖旨在用作關係發現應用程序。我創建了以下jsfiddle http://jsfiddle.net/R2JGa/7/以瞭解我想要實現的目標。

它工作得相當好,但我有一個令人討厭的問題:向圖中添加新節點時,舊節點以某種方式錯位。例如,我從3個節點開始,根節點是「flare」。其他2個節點是「有生命力」和「分析」。在我的例子中,任何時候當你點擊一個當前沒有孩子的節點時,孩子將永遠是「x」,「y」,「z」,「t」。展開幾個節點後,您會看到「動畫」或「分析」未鏈接到根節點「flare」,而是鏈接到其他一些節點(x,y,z,t)。或者有時如果您展開x或y或z或t,則子節點具有重複的x或y或z或t。如果點擊「flare」隱藏整個圖形,然後重新打開「flare」,則會看到節點已正確鏈接並命名。

我似乎無法明白爲什麼會發生這種情況。有人可以在這裏解釋一下嗎?我還是新的D3和發現它非常有趣,但這些問題是很討厭......

下面是代碼:

var w = 960, 
      h = 800, 
      node, 
      link, 
      root; 

    var force = d3.layout.force() 
      .charge(-1000) 
      .size([w, h]); 

    var vis = d3.select("#chart").append("svg:svg") 
      .attr("width", w) 
      .attr("height", h); 

    d3.json("data.json", function (json) { 
     root = json; 
     update(); 
    }); 

    function update() { 
     var nodes = flatten(root); 
     nodes.reverse(); 
     nodes = nodes.sort(function (a, b) { 
      return a.index - b.index; 
     }); 

     var links = d3.layout.tree().links(nodes); 

     console.log(nodes); 

     // Restart the force layout. 
     force 
       .nodes(nodes) 
       .links(links) 
       .linkDistance(55) 
       .start(); 


     var link = vis.selectAll(".link") 
       .data(links); 

     link.enter().append("line") 
       .attr("class", "link"); 

     link.exit().remove(); 

     var node = vis.selectAll("g.node") 
       .data(nodes) 


     var groups = node.enter().append("g") 
       .attr("class", "node") 
       .attr("id", function (d) { 
        return d.id 
       }) 
       .on('click', click) 
       .call(force.drag); 


     groups.append("image") 
       .attr("xlink:href", "https://github.com/favicon.ico") 
       .attr("x", -8) 
       .attr("y", -8) 
       .attr("width", 16) 
       .attr("height", 16); 


     groups.append("text") 
       .attr("dx", 12) 
       .attr("dy", "0.35em") 
       .style("font-size", "10px") 
       .text(function (d) { 
        console.log(d); 
        return d.name 
       }); 


     node.exit().remove(); 


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


    // 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) { 
     console.log(d); 
     if (d.children) { 
      d._children = d.children; 
      d.children = null; 
      update(); 
     } else if (d._children) { 
      d.children = d._children; 
      d._children = null; 
      update(); 
     } 
     else { 
      d3.json("expand.json", function (json) { 
       d.children = json.children; 
       update(); 
      }) 
     } 
    } 

    // Returns a list of all nodes under the root. 
    function flatten(root) { 
     var nodes = [], 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; 
    } 

這裏是2個JSON文件我請求:

data.json

{ 
    "name": "flare", 
    "id" : "flare", 
    "children": [ 
     { 
      "name": "analytics", 
      "id": "analytics" 

     }, 
     { 
      "name": "animate", 
      "id": "animate" 
     } 
    ] 
} 

而且expand.json

{"children": [ 
    { 
     "name": "x", 
     "id": "x", 
     "size": 1983 
    }, 
    { 
     "name": "y", 
     "id": "y", 
     "size": 2047 
    }, 
    { 
     "name": "z", 
     "id": "z", 
     "size": 1375 
    }, 
    { 
     "name": "t", 
     "id": "t", 
     "size": 1375 
    } 
]} 

PS:我不得不排序節點數組,否則糟糕的事情發生在圖上,我不明白爲什麼。

回答

2

這裏的工作解決方案fiddle。我認爲問題出在你聲明你的id的方式,並根據數組索引進行排序。你應該讓扁平化代碼聲明id,然後根據id給出它們。同樣在你的遞歸函數中,你可能需要首先聲明父對象,然後聲明子對象。

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

沒有幫助我,確實問題是與遞歸函數,但我不明白爲什麼。最有可能的力量圖需要以某種方式排序的節點...但似乎無法弄清楚哪一個 – user253530

+0

您能告訴我在我的小提琴中解決方案有什麼問題嗎?我認爲這是工作AOK。 – Yogesh

+0

jsfiddle似乎工作正常,但是當你在瀏覽器中使用源代碼並運行它們時,你會發現它沒有。奇怪的。無論如何,你還應該看看有時會錯位的x,y,z,t節點。我試過你的解決方案,但我無法讓它工作。我真的很感謝你的幫助,因爲從你說的話開始,我縮小了問題範圍,並找到了解決方案。問題在於遞歸函數不會每次都在同一位置插入節點,因此圖形會變得混亂。 – user253530

0

我已經設法從Yogesh的答案中找到一個解決方案。這是需要添加到update()函數中的代碼。

  var currentNodes = force.nodes(); 
      var nodes = flatten(root); 
      var actualNodes = []; 

      var values = currentNodes.map(function(obj) { return obj.name}); 
      var newNodesValues = nodes.map(function(obj) { return obj.name }); 


      for(var i = 0; i < currentNodes.length; i++) { 
       if(newNodesValues.indexOf(currentNodes[i].name) !== -1) { 
        actualNodes.push(currentNodes[i]); 
       } 
      } 

      for(var i = 0; i < nodes.length; i++) { 
       if(values.indexOf(nodes[i].name) == -1) { 
        actualNodes.push(nodes[i]); 
       } 
      } 

      nodes = actualNodes; 

      var links = d3.layout.tree().links(nodes); 

      // Restart the force layout. 
      force 
        .nodes(nodes) 
        .links(links) 
        .linkDistance(55) 
        .start(); 
0

下應該做的伎倆:

var i = 0; 

...

var link = vis.selectAll(".link") 
    .data(links, function (d) { 
       return d.id || (d.id = ++i); 
      }); 

...

var node = vis.selectAll("g.node") 
.data(nodes, function (d) { 
       return d.id || (d.id = ++i); 
      }); 

這第二個參數數據()是一個回調函數,當與基準調用,返回,結合每個DOM節點到它的相應的數據的密鑰。當你沒有提供這樣的功能時,d3沒有選擇,只能使用索引將數據綁定到DOM節點。