D3 力布局:根据节点半径沿链接移动箭头
D3 force layout: Moving arrows along links depending on node radius
在此D3
example:
节点具有可变半径。我设法移动了节点标签,使它们始终紧挨着它们的圆圈。
但是,如何移动箭头? (可以看到微软、苹果等几乎都被圆圈覆盖了)
相关问题:
here
here
here
here
要根据节点半径调整箭头的位置,可以调整路径的终点。已更新 jsFiddle
function linkArc(d) {
var targetX = d.target.x - d.target.started,
targetY = d.target.y - d.target.started,
dx = targetX - d.source.x,
dy = targetY - d.source.y,
dr = (d.straight == 0)?Math.sqrt(dx * dx + dy * dy):0;
return "M" + d.source.x + "," + d.source.y +
" L " + targetX + "," + targetY;
}
我在网上搜索,none 的答案有效,所以我自己做了:
代码如下:
//arrows
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", 9)
.attr("refY", 0)
.attr("markerWidth", 10)
.attr("markerHeight", 10)
.attr("orient", "auto")
.append("path")
.attr("d", "M0,-5L10,0L0,5 L10,0 L0, -5")
.style("stroke", "#4679BD")
.style("opacity", "0.6");
//Create all the line svgs but without locations yet
var link = svg.selectAll(".link")
.data(forceData.links)
.enter().append("line")
.attr("class", "link")
.style("marker-end", "url(#suit)");
//Set up the force layout
var force = d3.layout.force()
.nodes(forceData.nodes)
.links(forceData.links)
.charge(-120)
.linkDistance(200)
.size([width, height])
.on("tick", tick)
.start();
function tick(){
link.attr("x1", function (d) { return d.source.x; })
.attr("y1", function (d) { return d.source.y; })
.attr("x2", function (d) {
return calculateX(d.target.x, d.target.y, d.source.x, d.source.y, d.target.radius);
})
.attr("y2", function (d) {
return calculateY(d.target.x, d.target.y, d.source.x, d.source.y, d.target.radius);
});
d3.selectAll("circle")
.attr("cx", function (d) { return d.x; })
.attr("cy", function (d) { return d.y; });
d3.select("#forcelayoutGraph").selectAll("text")
.attr("x", function (d) { return d.x; })
.attr("y", function (d) { return d.y; });
}
function calculateX(tx, ty, sx, sy, radius){
if(tx == sx) return tx; //if the target x == source x, no need to change the target x.
var xLength = Math.abs(tx - sx); //calculate the difference of x
var yLength = Math.abs(ty - sy); //calculate the difference of y
//calculate the ratio using the trigonometric function
var ratio = radius / Math.sqrt(xLength * xLength + yLength * yLength);
if(tx > sx) return tx - xLength * ratio; //if target x > source x return target x - radius
if(tx < sx) return tx + xLength * ratio; //if target x < source x return target x + radius
}
function calculateY(tx, ty, sx, sy, radius){
if(ty == sy) return ty; //if the target y == source y, no need to change the target y.
var xLength = Math.abs(tx - sx); //calculate the difference of x
var yLength = Math.abs(ty - sy); //calculate the difference of y
//calculate the ratio using the trigonometric function
var ratio = radius / Math.sqrt(xLength * xLength + yLength * yLength);
if(ty > sy) return ty - yLength * ratio; //if target y > source y return target x - radius
if(ty < sy) return ty + yLength * ratio; //if target y > source y return target x - radius
}
在此D3
example:
节点具有可变半径。我设法移动了节点标签,使它们始终紧挨着它们的圆圈。
但是,如何移动箭头? (可以看到微软、苹果等几乎都被圆圈覆盖了)
相关问题:
here
here
here
here
要根据节点半径调整箭头的位置,可以调整路径的终点。已更新 jsFiddle
function linkArc(d) {
var targetX = d.target.x - d.target.started,
targetY = d.target.y - d.target.started,
dx = targetX - d.source.x,
dy = targetY - d.source.y,
dr = (d.straight == 0)?Math.sqrt(dx * dx + dy * dy):0;
return "M" + d.source.x + "," + d.source.y +
" L " + targetX + "," + targetY;
}
我在网上搜索,none 的答案有效,所以我自己做了:
代码如下:
//arrows
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", 9)
.attr("refY", 0)
.attr("markerWidth", 10)
.attr("markerHeight", 10)
.attr("orient", "auto")
.append("path")
.attr("d", "M0,-5L10,0L0,5 L10,0 L0, -5")
.style("stroke", "#4679BD")
.style("opacity", "0.6");
//Create all the line svgs but without locations yet
var link = svg.selectAll(".link")
.data(forceData.links)
.enter().append("line")
.attr("class", "link")
.style("marker-end", "url(#suit)");
//Set up the force layout
var force = d3.layout.force()
.nodes(forceData.nodes)
.links(forceData.links)
.charge(-120)
.linkDistance(200)
.size([width, height])
.on("tick", tick)
.start();
function tick(){
link.attr("x1", function (d) { return d.source.x; })
.attr("y1", function (d) { return d.source.y; })
.attr("x2", function (d) {
return calculateX(d.target.x, d.target.y, d.source.x, d.source.y, d.target.radius);
})
.attr("y2", function (d) {
return calculateY(d.target.x, d.target.y, d.source.x, d.source.y, d.target.radius);
});
d3.selectAll("circle")
.attr("cx", function (d) { return d.x; })
.attr("cy", function (d) { return d.y; });
d3.select("#forcelayoutGraph").selectAll("text")
.attr("x", function (d) { return d.x; })
.attr("y", function (d) { return d.y; });
}
function calculateX(tx, ty, sx, sy, radius){
if(tx == sx) return tx; //if the target x == source x, no need to change the target x.
var xLength = Math.abs(tx - sx); //calculate the difference of x
var yLength = Math.abs(ty - sy); //calculate the difference of y
//calculate the ratio using the trigonometric function
var ratio = radius / Math.sqrt(xLength * xLength + yLength * yLength);
if(tx > sx) return tx - xLength * ratio; //if target x > source x return target x - radius
if(tx < sx) return tx + xLength * ratio; //if target x < source x return target x + radius
}
function calculateY(tx, ty, sx, sy, radius){
if(ty == sy) return ty; //if the target y == source y, no need to change the target y.
var xLength = Math.abs(tx - sx); //calculate the difference of x
var yLength = Math.abs(ty - sy); //calculate the difference of y
//calculate the ratio using the trigonometric function
var ratio = radius / Math.sqrt(xLength * xLength + yLength * yLength);
if(ty > sy) return ty - yLength * ratio; //if target y > source y return target x - radius
if(ty < sy) return ty + yLength * ratio; //if target y > source y return target x - radius
}