d3 图形布局 - 箭头

d3 graph layout - arrows heads

我已经看到几个关于如何根据节点的半径在有向图中移动箭头的问题,但我无法在我的示例中弄清楚该怎么做:https://jsfiddle.net/Lx58yux4/

//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", 25)
    .attr("refY", 0)
    .attr("markerWidth", 8)
    .attr("markerHeight", 8)
    .attr("orient", "auto")
  .append("path")
    .attr("d", "M0,-5L10,0L0,5 L10,0 L0, -5")
    .style("stroke", "#4679BD")
    .style("opacity", "0.6");

//onTick
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;
    });
    d3.selectAll("circle").attr("cx", function (d) {
        //return d.x;
        return d.x = Math.max(radius, Math.min(width - 10, d.x));
    })
        .attr("cy", function (d) {
        return d.y = Math.max(radius, Math.min(height - 10, d.y));
        //return d.y;
    });
    d3.selectAll("text").attr("x", function (d) {
        return d.x;
    })
        .attr("y", function (d) {
        return d.y;
    });
    node.each(collide(5.0));    //collision detection
});

在节点数据绑定中保存节点半径。

node.append("circle")
    .attr("r", function(d) { 
        d.radius = (10 + d.users/250); 
        return d.radius; 
    })
    .style("fill", function (d) {
        return color(d.group);
     });

现在,在 tick 函数中更新链接,如下所示。

 link.attr("x1", function (d) {
    return d.source.x;
 })
 .attr("y1", function (d) {
    return d.source.y;
 })
 .attr("x2", function (d) {
    var diffX = d.target.x - d.source.x;  
    var diffY = d.target.y - d.source.y;
    var pathLength = Math.sqrt((diffX * diffX) + (diffY * diffY));
    var  offsetX = (diffX * d.target.radius) / pathLength;          
    return d.target.x-offsetX;
 })
 .attr("y2", function (d) {
     var diffX = d.target.x - d.source.x;   
     var diffY = d.target.y - d.source.y;
     var pathLength = Math.sqrt((diffX * diffX) + (diffY * diffY));
     var offsetY = (diffY * d.target.radius) / pathLength;
     return d.target.y-offsetY;
 });

希望对您有所帮助。

这是 fiddle : https://jsfiddle.net/Lx58yux4/2/

它可能需要 tweeking 但基本上你必须计算出节点中心与外部之间的差异,然后获得两个节点之间的向量以将箭头向下移动。这使用毕达哥拉斯定理 c=sqrt(a^2+b^2);

这是代码的主要部分:

function getVector(d) {
  var x1 = d.target.x;
  var y1 = d.target.y;
  var x2 = d.source.x;
  var y2 = d.source.y;

  var a = x1 - x2; //difference in x
  var b = y1 - y2; //difference in y
  var c = Math.sqrt((a * a) + (b * b)); //single vector
  var nodeRadius;
  node.filter(function(e) {
    return e.name === d.target.name; //return the links target
  }).each(function(n,i) {

    nodeRadius = 10 + n.users / 250 //as you had before, you could set this where you give it to the node
  });

  var vectorX = a / (c / nodeRadius );
  var vectorY = b / (c / nodeRadius );
  var thisVector = [vectorX, vectorY];
  return thisVector;
}

然后在目标的 x 和 y 中使用它

  .attr("x2", function(d) {

      var thisVector = getVector(d);

      return d.target.x - thisVector[0];
    })
    .attr("y2", function(d) {
      var thisVector = getVector(d);

      return d.target.y - thisVector[1];
    });

注意这样 link 不会去中心,而是去外面。所以实际上,这种方式不会将箭头向下移动 link,而是将 link 移动到节点的外部,从而移动箭头。