我想创建一个简单的 d3 水平海峡图。
它将包含节点、链接和每个链接的线。
如何在刻度中正确定义节点/边的坐标来实现它(无需手动设置坐标)。
我需要提前做一些计算吗?
我基本上希望第一个节点出现在链(图)的最左侧,最后一个节点(它是一条条纹线)出现在链(图)的最右侧。
如何确定图中节点的顺序以对齐它们。
更新
这是我到目前为止达到的小提琴:
这里我只是硬编码了我想要动态的 y 轴。
我的节点也没有顺序。
const nodes = [
{ id: 'A' },
{ id: 'B' },
{ id: 'C' },
];
const edges = [
{ source: 'A', target: 'B' },
{ source: 'B', target: 'C' },
];
const svg = d3.select("svg");
// Create arrowhead marker
svg.append('defs').append('marker')
.attr('id', 'arrowhead')
.attr('viewBox', '-0 -5 10 10')
.attr('refX', 15)
.attr('refY', 0)
.attr('orient', 'auto')
.attr('markerWidth', 10)
.attr('markerHeight', 10)
.attr('xoverflow', 'visible')
.append('svg:path')
.attr('d', 'M 0,-5 L 10 ,0 L 0,5')
.attr('fill', '#ccc')
.style('stroke','none');
const simulation = d3.forceSimulation(nodes)
.force("link", d3.forceLink(edges).id(d => d.id).distance(150))
.force("charge", d3.forceManyBody().strength(-100))
.force("y", d3.forceY(200))
.force("x", d3.forceX(200));
//.force("center", d3.forceCenter(400, 200));
const link = svg.selectAll(".link")
.data(edges)
.enter().append("line")
.attr("class", "link")
.attr('marker-end', 'url(#arrowhead)');
const node = svg.selectAll(".node")
.data(nodes)
.enter().append("circle")
.attr("class", "node")
.attr("r", 20);
simulation.on("tick", () => {
link.attr("x1", d => d.source.x)
.attr("y1", d => 20)
.attr("x2", d => d.target.x)
.attr("y2", d => 20);
node.attr("cx", d => d.x)
.attr("cy", d => 20);
});
https://jsfiddle.net/g87s23L5/3/
理想情况下,我正在寻找一种使用 d3 内置力量的方法
当您创建模拟时,我调整了力(“x”,...)以根据节点的索引来定位节点。这将确保节点按从左到右的顺序出现。
这是固定代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>D3 Horizontal Straight Graph</title>
<script src="https://d3js.org/d3.v6.min.js"></script>
<style>
.node {
fill: #3498db;
stroke: #2980b9;
stroke-width: 2px;
cursor: pointer;
}
.link {
fill: none;
stroke: #ccc;
stroke-width: 2px;
cursor: pointer;
marker-end: url(#arrowhead); /* Add arrowhead to links */
.line {
stroke: black;
stroke-width: 2px;
}
}
</style>
</head>
<body>
<svg width="800" height="400"></svg>
<script>
const nodes = [
{ id: 'A' },
{ id: 'B' },
{ id: 'C' },
];
const edges = [
{ source: 'A', target: 'B' },
{ source: 'B', target: 'C' },
];
const svg = d3.select("svg");
// Create arrowhead marker
svg.append('defs').append('marker')
.attr('id', 'arrowhead')
.attr('viewBox', '-0 -5 10 10')
.attr('refX', 15)
.attr('refY', 0)
.attr('orient', 'auto')
.attr('markerWidth', 10)
.attr('markerHeight', 10)
.attr('xoverflow', 'visible')
.append('svg:path')
.attr('d', 'M 0,-5 L 10 ,0 L 0,5')
.attr('fill', '#ccc')
.style('stroke','none');
const simulation = d3.forceSimulation(nodes)
.force("link", d3.forceLink(edges).id(d => d.id).distance(150))
.force("charge", d3.forceManyBody().strength(-100))
.force("y", d3.forceY(20))
.force("x", d3.forceX((d, i) => i * 20)); // Position nodes based on index
const link = svg.selectAll(".link")
.data(edges)
.enter().append("line")
.attr("class", "link")
.attr('marker-end', 'url(#arrowhead)');
const node = svg.selectAll(".node")
.data(nodes)
.enter().append("circle")
.attr("class", "node")
.attr("r", 20);
simulation.on("tick", () => {
link.attr("x1", d => d.source.x)
.attr("y1", d => 20)
.attr("x2", d => d.target.x)
.attr("y2", d => 20);
node.attr("cx", d => d.x)
.attr("cy", d => 200);
});
</script>
</body>
</html>