【问题标题】:D3 force layout: Moving arrows along links depending on node radiusD3 力布局:根据节点半径沿链接移动箭头
【发布时间】:2015-03-22 00:17:35
【问题描述】:

在这个D3example

节点具有可变半径。我设法移动了节点标签,以便它们总是紧挨着他们的圈子。

但是,如何移动箭头? (你可以看到,对于微软、苹果等,他们几乎都被圆圈覆盖了)

相关问题:

here

here

here

here

【问题讨论】:

  • 通过将参考点(相对于标记被绘制到每个路径中的位置)从 0,0 移开来偏移箭头。这是通过 .attr("refX", 15) 完成的。增加该数字将使箭头移开。请注意,这种技术并不完美:首先,它将相同的 refX 应用于所有标记。由于您需要它取决于节点半径,因此您必须做额外的工作来创建并为每个圆创建一个标记。其次,箭头移动得越远,它的旋转与路径切线的匹配就越少。使链接成直线可以避免这个问题。

标签: javascript svg d3.js force-layout


【解决方案1】:

我在网上搜索,没有一个答案,所以我自己做了:

代码如下:

   //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
}

【讨论】:

  • +1 有效!我一直在寻找你所做的这个算法。这与我之前为另一个目的所做的相同。
【解决方案2】:

要根据节点半径调整箭头的位置,您可以调整路径的端点。更新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;
}

【讨论】:

    猜你喜欢
    • 2013-03-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多