2013-08-06 56 views
2

我正在執行我的強制佈局中的箭頭(http://bl.ocks.org/mbostock/1153292),並且完美地工作。但是,人們很快就會意識到,箭頭的位置和大小在這裏被硬編碼,因爲節點不會改變大小。如何動態更改定向d3.js力佈局上方向箭頭的位置和大小?

我有一張圖是我動態更改節點大小,因此我希望箭頭相應地進行更新,否則它們會覆蓋節點或覆蓋節點或者只是未連接到節點。

只有一個帖子我找到(linking nodes of variable radius with arrows)談論這個問題。然而,它沒有得到回答,一個海報給出的讓邊緣在節點半徑而不是中心處結束的答案並不是我想要做的。這將需要不斷重新計算邊緣位置,這給定了我具有的邊緣數量是不實際的。

我以爲這會比較簡單,但一直沒有弄清楚。目前我正在處理的更改是將標記創建移動到節點的生成之下,否則就無法獲取節點大小的數據,除非我想運行我正在使用的大小方法,這將會大大浪費處理能力(我有數百個節點)。

我試圖(例如粗糙,我的代碼是一個複雜一點)

var path = svg.append("svg:g").selectAll("path") 
    .data(force.links()) 
    .enter().append("svg:path") 
    .attr("class", function(d) { return d.target.nodeID; }); 

var circle = svg.append("svg:g").selectAll("circle") 
    .data(force.nodes()) 
     .enter().append("svg:circle") 
    .attr("r", nodeSize) //Dynamically determine size 
    .call(force.drag); 

// Per-node markers, as each node could potentially have a unique size 
svg.append("svg:defs").selectAll("marker") 
    .data(nodes, function(d) { return d.nodeID; }) 
    .enter().append("svg:marker") 
    .attr("id", function(d) { return d.nodeID; }) 
    .attr("viewBox", "0 -5 10 10") 
    .attr("refX", function(d) { return d.r; }) //Offset by the radius of the node 
    .attr("refY", 0) 
    .attr("markerWidth", 6) 
    .attr("markerHeight", 6) 
    .attr("orient", "auto") 
    .append("svg:path") 
    .attr("d", "M0,-5L10,0L0,5"); 

//Now that we know radius data for nodes, add the arrows in 
path.each(function(d) { 
    d.attr("marker-end", function(d) { return "url(#" + d.target.nodeID + ")"; }) 
}); 

沒有人有去了解這一點的最好方式的點子?提前致謝!

更新:根據要求,我創建了一個jsfiddle(http://jsfiddle.net/herbstmb/j5wJ7/)。這是一個基本的力量佈局,我試圖讓動態箭頭大小能夠工作。

+0

你可以把這個代碼片段到工作[小提琴](http://jsfiddle.net/)?它看起來沒有太多缺失,並且會讓其他人更容易嘗試找出解決方案。 – Jake

+0

我創建了一個。檢查帖子中的更新。這是一個非常基本的強制佈局,我只是試圖添加動態大小的箭頭。 –

+2

語法錯誤控制檯抱怨是在上一行中,您打開一個帶有單引號的字符串並用雙引號關閉它。關於你的問題,我注意到你提出了一些關於丟棄潛在解決方案的評論,因爲在數百個節點上運行潛在的性能問題。您可能應該嘗試最簡單的解決方案,並在放棄之前對其進行測試。你會驚訝地發現你可以在幾秒內完成幾百個點。目前瀏覽器速度很快(甚至相對而言,移動)。 –

回答

3

這是一個老問題,但這裏是我的解決方案。這個想法是繪製連接節點的路徑,使得終點位於節點的邊緣而不是節點的中心。從移動開始專利套裝示例(http://bl.ocks.org/mbostock/1153292),I取代linkArc法:

function drawCurve(d) { 
    var sourceX = d.source.x; 
    var sourceY = d.source.y; 
    var targetX = d.target.x; 
    var targetY = d.target.y; 

    var theta = Math.atan((targetX - sourceX)/(targetY - sourceY)); 
    var phi = Math.atan((targetY - sourceY)/(targetX - sourceX)); 

    var sinTheta = d.source.r * Math.sin(theta); 
    var cosTheta = d.source.r * Math.cos(theta); 
    var sinPhi = d.target.r * Math.sin(phi); 
    var cosPhi = d.target.r * Math.cos(phi); 

    // Set the position of the link's end point at the source node 
    // such that it is on the edge closest to the target node 
    if (d.target.y > d.source.y) { 
     sourceX = sourceX + sinTheta; 
     sourceY = sourceY + cosTheta; 
    } 
    else { 
     sourceX = sourceX - sinTheta; 
     sourceY = sourceY - cosTheta; 
    } 

    // Set the position of the link's end point at the target node 
    // such that it is on the edge closest to the source node 
    if (d.source.x > d.target.x) { 
     targetX = targetX + cosPhi; 
     targetY = targetY + sinPhi;  
    } 
    else { 
     targetX = targetX - cosPhi; 
     targetY = targetY - sinPhi; 
    } 

    // Draw an arc between the two calculated points 
    var dx = targetX - sourceX, 
     dy = targetY - sourceY, 
     dr = Math.sqrt(dx * dx + dy * dy); 
    return "M" + sourceX + "," + sourceY + "A" + dr + "," + dr + " 0 0,1 " + targetX + "," + targetY; 
} 

注意,該代碼期望的「R」,或半徑,屬性是在節點的數據。這將相當於jsfiddle中的size屬性。如果你想畫鏈接爲直線,就可以返回這個字符串,而不是:

return "M" + sourceX + "," + sourceY + "L" + targetX + "," + targetY; 

要放置箭頭的點在正確的位置上,我改變了對REFx和REFY屬性,從而使箭頭點是節點的邊緣:

svg.append("defs").selectAll("marker") 
    .data(["suit", "licensing", "resolved"]) 
    .enter().append("marker") 
    .attr("id", function(d) { return d; }) 
    .attr("viewBox", "0 -5 10 10") 
    .attr("refX", 10) 
    .attr("refY", 0) 
    .attr("markerWidth", 6) 
    .attr("markerHeight", 6) 
    .attr("orient", "auto") 
    .append("path") 
    .attr("d", "M0,-5L10,0L0,5");