【问题标题】:d3 graph layout - arrows headsd3 图形布局 - 箭头
【发布时间】:2023-06-07 05:36:02
【问题描述】:

我已经看到了几个关于如何根据节点的半径移动有向图中的箭头的问题,但在我的示例中我无法弄清楚如何做到这一点: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
});

【问题讨论】:

    标签: layout d3.js graph force-layout


    【解决方案1】:

    将数据绑定中的节点半径保存到节点。

    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;
     });
    

    希望这会有所帮助。

    【讨论】:

      【解决方案2】:

      这是小提琴: 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];
          });
      

      注意这种方式链接不会到中心,而是到外面。所以实际上这种方式不会将箭头向下移动链接,而是指向节点外部的链接反过来移动箭头。

      【讨论】: