2013-05-15 90 views

回答

6

可以通過更改的d.target.xd.target.y的值取半徑偏移鏈接的目標由節點,即半徑調整代碼

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.radius)考慮在內。也就是說,用圓弧半徑偏移箭頭的末端。

+0

我明白你的意思,但我無法弄清楚到底哪裏是我應該引入半徑 –

+0

點我想,移動箭頭的到底是不是那麼容易的,因爲結果還取決於從哪個方向它來了。可能我應該在標記上工作。 –

+0

可能是正確的變化將影響:...追加 「(SVG:標記」).attr( 「REFX」,15).attr( 「REFY」,-1.5) –

3

最後我決定爲每個鏈接創建一個標記(而不是每個類)。 該解決方案的優點是定義每個標記的偏移量,取決於在我自己的情況下是refX的目標節點。

// One marker for link... 
    svg.append("svg:defs").selectAll("marker") 
     .data(force.links()) 
    .enter().append("svg:marker") 
     .attr("id", function(link, idx){ return 'marker-' + idx}) 
     .attr("viewBox", "0 -5 10 10") 
     .attr("refX", function(link, idx){ 
     return 10 + link.target.size; 
     }) 
     .attr("refY", 0) 
     .attr("markerWidth", 6) 
     .attr("markerHeight", 6) 
     .attr("orient", "auto") 
    .append("svg:path") 
     .attr("d", "M0,-5L10,0L0,5") 
     .attr("fill", function(link){ 
     if(link.type == 'in') 
      return "green"; 
     return "blue"; 
     }); 

現在有一個小問題,即線條是曲線。這意味着標記/箭頭應該翻譯不僅在X軸,而且在Y軸上,這可能取決於曲線的射線值的...

1

這裏我的解決辦法:

首先我計算路徑水平軸的角度(gamma)。然後我得到半徑的X分量(Math.cos(gamma) * radius)和Y分量(Math.sin(gamma) * radius)。然後用這些組件偏移路徑的末端。

function linkArc(d) { 
    var t_radius = calcRadius(d.target.size); 
    var s_radius = calcRadius(d.source.size); 
    var dx = d.target.x - d.source.x; 
    var dy = d.target.y - d.source.y; 
    var gamma = Math.atan(dy/dx); 
    var tx = d.target.x - (Math.cos(gamma) * t_radius); 
    var ty = d.target.y - (Math.sin(gamma) * t_radius); 
    var sx = d.source.x - (Math.cos(gamma) * s_radius); 
    var sy = d.source.y - (Math.sin(gamma) * s_radius); 

    return "M" + sx + "," + sy + "L" + tx + "," + ty; 
} 

首先你會注意到我沒有使用弧,但原理應該是相同的。 另外我的節點有一個size屬性,我可以從中計算出圓的直徑。

最後我的標記被定義爲是:

var arrowsize = 10; 
var asHalf = arrowsize/2; 
svg.append("defs").selectAll("marker") 
     .data(["arrowhead"]) 
     .enter().append("marker") 
     .attr("id", function (d) { 
      return d; 
     }) 
     .attr("viewBox", "0 -5 " + arrowsize + " " + arrowsize) 
     .attr("refX", arrowsize) 
     .attr("refY", 0) 
     .attr("markerWidth", 9) 
     .attr("markerHeight", 9) 
     .attr("orient", "auto") 
     .attr("class", "arrowhead-light") 
     .append("path") 
     .attr("d", "M 0," + (asHalf * -1) + " L " + arrowsize + ",0 L 0," + asHalf); 

我還沒有找到一種方法來控制標記的每一個副本。

+1

我相信,在你的代碼中的錯誤:對我來說,我不得不添加伽瑪*半徑源座標: 'VAR SX = d.source.x +(Math.cos(伽馬)* s_radius); VAR SY = d.source.y +(Math.sin(伽馬)* s_radius);' –

6

如果您將使用<line>而不是<path>,以下內容適用於您,我使用當前解決方案。它是基於@ɭɘɖɵʊɒɼɖ江戸解決方案:

在你tick事件偵聽器:

linkElements.attr("x1", function(d) { return d.source.x; }) 
     .attr("y1", function(d) { return d.source.y; }) 
     .attr("x2", function(d) { 
      return getTargetNodeCircumferencePoint(d)[0]; 
     }) 
     .attr("y2", function(d) { 
      return getTargetNodeCircumferencePoint(d)[1]; 
     }); 

function getTargetNodeCircumferencePoint(d){ 

     var t_radius = d.target.nodeWidth/2; // nodeWidth is just a custom attribute I calculate during the creation of the nodes depending on the node width 
     var dx = d.target.x - d.source.x; 
     var dy = d.target.y - d.source.y; 
     var gamma = Math.atan2(dy,dx); // Math.atan2 returns the angle in the correct quadrant as opposed to Math.atan 
     var tx = d.target.x - (Math.cos(gamma) * t_radius); 
     var ty = d.target.y - (Math.sin(gamma) * t_radius); 

     return [tx,ty]; 
} 

我相信這個解決方案可以被修改,以適應<path>元素,但是我還沒有嘗試過。

+0

我偉大的工作,搬到了圈子之外的聯繫。我甚至在乘法中做了t_radius + 2,以使鏈接在圓的邊緣之前完成 - 更好地移動標記refX,使得筆劃寬度不會影響箭頭邊緣。 – Evgeny

2

末來回答,但是綜合所有以前的答案了一下,我想出了,因爲角(如果你發現缺乏好奇全局變量的一個全面的解決方案,在D3 V4對我的作品,寫的打字稿)。下面是包含的關鍵部件,包括(因爲我的整個生產代碼太長和NDA下)一個片段。主要想法被註釋爲代碼註釋。最終的結果是這樣的:

所有的

Sample output

首先,既然你試圖讓不同大小的節點,我會假設你有你的節點數據內半徑屬性。假設它是這樣一組對象:

{ 
    id: input.name, 
    type: input.type, 
    radius: input.radius 
} 

然後標記被追加。注意,每一個箭頭(或標記)的大小爲10,其中有一半是5.您可以將其指定爲像@變量ɭɘ-ɖɵʊɒɼɖ-江戸並在他的回答,但我就是太懶。

let marker = svg.append("defs") 
    .attr("class", "defs") 
    .selectAll("marker") 
    // Assign a marker per link, instead of one per class. 
    .data(links, function (d) { return d.source.id + "-" + d.target.id; }); 
// Update and exit are omitted. 
// Enter 
marker = marker 
    .enter() 
    .append("marker") 
    .style("fill", "#000") 
    // Markers are IDed by link source and target's name. 
    // Spaces stripped because id can't have spaces. 
    .attr("id", function (d) { return (d.source.id + "-" + d.target.id).replace(/\s+/g, ''); }) 
    // Since each marker is using the same data as each path, its attributes can similarly be modified. 
    // Assuming you have a "value" property in each link object, you can manipulate the opacity of a marker just like a path. 
    .style("opacity", function (d) { return Math.min(d.value, 1); }) 
    .attr("viewBox", "0 -5 10 10") 
    // refX and refY are set to 0 since we will use the radius property of the target node later on, not here. 
    .attr("refX", 0) 
    .attr("refY", 0) 
    .attr("markerWidth", 5) 
    .attr("markerHeight", 5) 
    .attr("orient", "auto") 
    .append("path") 
    .attr("d", "M0,-5L10,0L0,5") 
    .merge(marker); 

然後,路徑可以參考每個個體標記其ID:

let path = svg.append("g") 
    .attr("class", "paths") 
    .selectAll("path") 
    .data(links, function (d) { return d.source.id + "-" + d.target.id; }); 
// Update and exit are omitted. 
// Enter 
path = path 
    .enter() 
    .append("path") 
    .attr("class", "enter") 
    .style("fill", "none") 
    .style("stroke", "#000") 
    .style("stroke-opacity", function (d) { return Math.min(d.value, 1); }) 
    // This is how to connect each path to its respective marker 
    .attr("marker-end", function(d) { return "url(#" + (d.source.id + "-" + d.target.id).replace(/\s+/g, '') + ")"; }) 
    .merge(path); 

一個可選的東西要修改,如果你想要更多的功能:允許你的。對(「嘀」,打勾)監聽器接收更多變量來測試邊界。例如,svg的寬度和高度。

.on("tick", function() { ticked(node, path, width, height) }) 

,這是你的新勾選功能的基礎上@ɭɘ-ɖɵʊɒɼɖ-江戸的答案:

ticked(node, path, width, height) { 
    node 
    .attr("transform", function(d){return "translate(" + Math.max(d.radius, Math.min(width - d.radius, d.x)) + "," + Math.max(d.radius, Math.min(height - d.radius, d.y)) + ")"}); 

    path 
    .attr("d", d => { 
     let dx = d.target.x - d.source.x, 
      dy = d.target.y - d.source.y, 
      dr = Math.sqrt(dx * dx + dy * dy), 
      gamma = Math.atan2(dy, dx), // Math.atan2 returns the angle in the correct quadrant as opposed to Math.atan 
      sx = Math.max(d.source.radius, Math.min(width - d.source.radius, d.source.x + (Math.cos(gamma) * d.source.radius) )), 
      sy = Math.max(d.source.radius, Math.min(height - d.source.radius, d.source.y + (Math.sin(gamma) * d.source.radius) )), 
      // Recall that 10 is the size of the arrow 
      tx = Math.max(d.target.radius, Math.min(width - d.target.radius, d.target.x - (Math.cos(gamma) * (d.target.radius + 10)) )), 
      ty = Math.max(d.target.radius, Math.min(height - d.target.radius, d.target.y - (Math.sin(gamma) * (d.target.radius + 10)) )); 
     // If you like a tighter curve, you may recalculate dx dy dr: 
     //dx = tx - sx; 
     //dy = ty - sy; 
     //dr = Math.sqrt(dx * dx + dy * dy); 
     return "M" + sx + "," + sy + "A" + dr + "," + dr + " 0 0,1 " + tx + "," + ty; 
    }); 
    } 

正如@約書亞 - 科莫提到的,它應該是一個加號計算時sx和sy。

相關問題