【问题标题】:Build intelligent path between source and target in d3.js在 d3.js 中构建源和目标之间的智能路径
【发布时间】:2021-08-08 20:26:21
【问题描述】:

我正在尝试在源到目标之间创建点。不知何故,我能够创造积分,但它们还不够好。在某些时候,路径会与其他形状重叠。

只是一个例子,路径与其他形状重叠。在这种情况下,从源到目标,我将路径分成了一半。因此,如果路径连接到下一个形状但它不起作用,如果下一个形状与下面的情况非常相似。

可以有许多源形状,可以是 3,4 或 10,也可以有多个目标。我无法明确定义一些固定路径。

【问题讨论】:

  • 你能发布你的数据模型吗?
  • @MichaelRovinsky 我添加了有问题的数据模型
  • 如果我们有 3 个形状和一个目标,@MichaelRovinsky 下面的代码可以正常工作。但如果我们有更多的源和目标形状,它不会创建正确的路径。请检查我更新的问题。不知何故,我需要为目标找到正确的路径,因为我将 5 个形状放在单行中,下一个形状将出现在下一行。因此,目标可能会出现在下一行。
  • 我认为不可能为任何类型的布局创建一个简单而通用的路径构建器。尤其是当你不仅要在两个节点之间画一条路径,还要考虑其他节点,你根本不知道你有多少个节点。只需尝试在纸上画出所有可能的案例,您将进入一个无休止的过程……无论如何,如果您的场景数量非常有限,请一次画出它们,然后发布一个新问题。我会尽量回答
  • @MichaelRovinsky 感谢您的帮助并让我对此有所了解。我认为没有那么多场景,我已经提到了一些场景,这些场景将在这个问题stackoverflow.com/questions/67668788/…

标签: javascript angular d3.js


【解决方案1】:

使用getPath函数计算链接路径:

const getPath = link => `M ${link.fromX},${link.fromY} H ${link.fromX + 50} V ${link.toY} H ${link.toX}`;

在 sn-p 中查看它是如何工作的:

const data = [
    {
        "shapeName": "rect1",
        "shapeId": "1.1",
        "coordinateData": {
            "height": 125,
            "width": 100,
            "xCoordinate": 300,
            "yCoordinate": 100
        },
        "ticks": [
            {
                "tickId": "1.1.1",
                "sourceTarget": [
                    {
                        "sourceTick": "1.1.1",
                        "targetTick": "1.4.11"
                    }
                ]
            }
        ]
    
    },
    {
        "shapeName": "rect2",
        "shapeId": "1.2",
        "coordinateData": {
            "height": 125,
            "width": 100,
            "xCoordinate": 850,
            "yCoordinate": 100
        },
        "ticks": [
            {
                "tickId": "1.2.1",
                "sourceTarget": [
                    {
                        "sourceTick": "1.2.1",
                        "targetTick": "1.4.12"
                    }
                ]
            }
        ]
    
    },
    {
        "shapeName": "rect3",
        "shapeId": "1.3",
        "coordinateData": {
            "height": 125,
            "width": 100,
            "xCoordinate": 1400,
            "yCoordinate": 100
        },
        "ticks": [
            {
                "tickId": "1.3.1",
                "sourceTarget": [
                    {
                        "sourceTick": "1.3.1",
                        "targetTick": "1.4.13"
                    }
                ]
            }
        ]
    
    },
    {
        "shapeName": "rect4",
        "shapeId": "1.4",
        "coordinateData": {
            "height": 375,
            "width": 100,
            "xCoordinate": 1750,
            "yCoordinate": 100
        },
        "ticks": [
            {
                "tickId": "1.4.11",
                "sourceTarget": null
            },
            {
                "tickId": "1.4.12",
                "sourceTarget": null
            },
            {
                "tickId": "1.4.13",
                "sourceTarget": null
            }
        ]
    
    }
];


const svg = d3.select('svg');

svg.selectAll('rect.shape')
  .data(data, d => d.shapeId)
  .enter()
  .append('rect')
  .classed('shape', true)
  .attr('width', d => d.coordinateData.width)
  .attr('height', d => d.coordinateData.height)
  .attr('x', d => d.coordinateData.xCoordinate)
  .attr('y', d => d.coordinateData.yCoordinate);
   
    
const ticks = data.reduce((all, item) => {
 item.ticks.forEach((tick, index) => {
  const x = item.coordinateData.xCoordinate + (tick.sourceTarget ? item.coordinateData.width : 0);
  const y = item.coordinateData.yCoordinate + item.coordinateData.height / 2 + (index - (item.ticks.length - 1) / 2) * 30; 
  all.push({id: tick.tickId, target: tick.sourceTarget ? tick.sourceTarget[0].targetTick : null, x, y});
  });
  return all;
}, []);    

svg.selectAll('circle.tick')
  .data(ticks, d => d.id)
  .enter()
  .append('circle')
  .classed('tick', true)
  .attr('cx', d => d.x)
  .attr('cy', d => d.y)
  .attr('r', 10)

const links = ticks.filter(d => d.target).map((tick, index) => {
  const target = ticks.find(t => t.id === tick.target);
  return {id: index, fromX: tick.x, fromY: tick.y, toX: target.x, toY: target.y};
});

const getPath = link => `M ${link.fromX},${link.fromY} H ${link.fromX + 50} V ${link.toY} H ${link.toX}`;

svg.selectAll('path.link')
  .data(links, d => d.id)
  .enter()
  .append('path')
  .classed('link', true)
  .attr('d', getPath)
svg {
  border: 1px solid gray;
}
path, .shape {
  stroke-width: 1;
  stroke: black;
  fill: none;
}

.tick {
  fill: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg width="600" height="150" viewBox="0 0 2000 500"/>

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-05-01
    • 2016-02-14
    • 1970-01-01
    • 1970-01-01
    • 2020-05-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多