微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

具有动态偏移箭头的D3.js力导向图?

如何解决具有动态偏移箭头的D3.js力导向图?

我在D3.js中有一个力导向图,其中节点半径与该数据的属性(例如,浏览量)成正比,链接宽度与链接数据的属性(例如,点击数)成正比。我想给链接曲线指示方向。问题是链接移动到数据节点的 center ,因此,如果我使用marker-end,则会得到:

SVG image showing arrows pointing to center of data node,where they will be invisible

(数据节点通常用链接到另一数据类别的颜色填充...)

我使用以下方法创建~~ arcs ~~曲线

positionLink = (d) => {
    const offset = 100;
    const midpoint_x = (d.source.x + d.target.x) / 2;
    const midpoint_y = (d.source.y + d.target.y) / 2;
  
    const dx = d.source.x - d.target.x;
    const dy = d.source.y - d.target.y;
  
    // Perpendicular vector 
    const nx = -dy;
    const ny = dx;
    const norm_length = Math.sqrt((nx*nx)+(ny*ny));
    const normx = nx / norm_length;
    const normy = ny / norm_length;
  
    const offset_x = parseFloat(midpoint_x + offset * normx.toFixed(2));
    const offset_y = parseFloat(midpoint_y + offset * normy.toFixed(2));
    const arc = `M ${d.source.x.toFixed(2)} ${d.source.y.toFixed(2)} S ${offset_x} ${offset_y} ${d.target.x.toFixed(2)} ${d.target.y.toFixed(2)}`;

    return arc;
  };

我的代码称为arc是SVG“ S”路径,这是“平滑曲线”,但我对此并不特别担心:我只需要将圆弧彼此拉开即可,我可以显示一个方向和另一个方向的数据之间的差异。

如何找到Bezier曲线与圆的交点?

(由于曲线的目标是圆的中心,因此我想可以将其改写为“距终点r处的贝塞尔曲线的值”)

如果我说的是那一点,我可以使它成为箭头的顶点。

(更好的是,如果我在那个点有Bezier的斜率,这样我就可以真正对齐,但是我认为我可以将其与中点和锚点之间的线对齐...)

解决方法

考虑以下迭代方法:

使用path.getPointAtLength,可以遍历路径,直到找到一个从圆心正好r的点,然后使用这些坐标重新绘制路径。

const data = [{
  x: 50,y: 100,r: 20
},{
  x: 100,y: 30,r: 5
}];
const links = [{
    source: data[0],target: data[1]
  },{
    source: data[1],target: data[0]
  }
];

positionLink = (source,target) => {
  const offsetPx = 100;
  const midpoint = {
    x: (source.x + target.x) / 2,y: (source.y + target.y) / 2
  };

  const dx = source.x - target.x;
  const dy = source.y - target.y;

  // Perpendicular vector 
  const nx = -dy;
  const ny = dx;
  const norm_length = Math.sqrt((nx * nx) + (ny * ny));
  const normx = nx / norm_length;
  const normy = ny / norm_length;

  const offset = {
    x: parseFloat(midpoint.x + offsetPx * normx.toFixed(2)),y: parseFloat(midpoint.y + offsetPx * normy.toFixed(2)),};

  const arc = `M ${source.x.toFixed(2)} ${source.y.toFixed(2)} S ${offset.x} ${offset.y} ${target.x.toFixed(2)} ${target.y.toFixed(2)}`;

  return arc;
};

euclidean = (point,other) => Math.sqrt(point.x * other.x + point.y * other.y);

findPointAtLength = (path,point,fromEnd) => {
  // For the target we need to start at the other side of the path
  let offset = point.r;
  if (fromEnd) {
    const totalLength = path.getTotalLength();
    offset = totalLength - offset;
  }

  let current = path.getPointAtLength(offset);

  // Gradually increase the offset until we're exactly 
  // `r` away from the circle centre
  while (euclidean(point,current) < point.r) {
    offset += 1;
    current = path.getPointAtLength(offset);
  }

  return {
    x: current.x,y: current.y
  };
};

// Use function because we want access to `this`,// which points to the current path HTMLElement
positionLinkAtEdges = function(d) {
  // First,place the path in the old way
  d3.select(this).attr("d",positionLink(d.source,d.target));

  // Then,position the path away from the source
  const source = findPointAtLength(this,d.source,false);
  const target = findPointAtLength(this,d.target,true);

  return positionLink(source,target);
}

const svg = d3.select("svg").append("g");

svg
  .selectAll("circle")
  .data(data)
  .enter()
  .append("circle")
  .attr("cx",d => d.x)
  .attr("cy",d => d.y)
  .attr("r",d => d.r);

svg
  .selectAll("path")
  .data(links)
  .enter()
  .append("path")
  .attr("d",positionLinkAtEdges)
  .attr("marker-end","url(#triangle)");
g circle,g path {
  fill: none;
  stroke: black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg>
  <defs>
    <marker id="triangle" viewBox="0 0 10 10"
          refX="10" refY="5" 
          markerUnits="strokeWidth"
          markerWidth="10" markerHeight="10"
          orient="auto">
      <path d="M 0 0 L 10 5 L 0 10 z" fill="#f00"/>
    </marker>
  </defs>
</svg>

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?