我正在尝试将使用 d3js 版本 3 编写的力导向图更新为 d3js 版本 7。
以下代码片段是使用 d3js v3 的工作实现:
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height");
graph = {
nodes: [],
links: [],
}
var simulation = d3.layout.force()
.size([width, height])
.nodes(graph.nodes)
.links(graph.links)
.on("tick", function() {
svg.selectAll('.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 })
svg.selectAll('.node')
.attr("cx", function (d) { return d.x })
.attr("cy", function (d) { return d.y })
.attr("transform", function (d) {
return "translate(" + d.x + "," + d.y + ")";
})
});
function update() {
// update links
var link = svg.selectAll('.link').data(graph.links);
link.enter()
.insert('line', '.node')
.attr('class', 'link')
.style('stroke', '#d9d9d9');
link
.exit()
.remove()
// update nodes
var node = svg.selectAll('.node').data(graph.nodes);
var g = node.enter()
.append('g')
.attr('class', 'node');
g.append('circle')
.attr("r", 20)
.style("fill", "#d9d9d9");
g.append('text')
.attr("class", "text")
.text(function (d) { return d.name });
node
.exit()
.remove();
// update simulation
simulation
.linkDistance(100)
.charge(-200)
.start();
};
function addNode(node) {
graph.nodes.push(node);
update();
};
function connectNodes(source, target) {
graph.links.push({
source: source,
target: target,
});
update();
};
addNode({
id: "you",
name: "you",
});
let index = 1;
// add a new node every three seconds and connect to 'you'
const interval = window.setInterval(() => {
let id = Math.random().toString(36).replace('0.','');
id = id.slice(0,4);
addNode({
id: id,
name: id
});
connectNodes(0, index);
index++;
}, 3000);
// no more than 8 nodes
setTimeout(() => {
clearInterval(interval)
}, 3000 * 8);Run Code Online (Sandbox Code Playgroud)
<html>
<head>
<script src="https://d3js.org/d3.v3.min.js"></script>
</head>
<body>
<svg width="400" height="200"></svg>
</body>
</html>Run Code Online (Sandbox Code Playgroud)
以下代码片段是我尝试使用 d3js v7 实现上述代码片段:
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height");
graph = {
nodes: [],
links: [],
}
var simulation = d3.forceSimulation()
.force("center", d3.forceCenter(width / 2, height / 2).strength(0.01))
.nodes(graph.nodes)
.force("link", d3.forceLink(graph.links).distance(100))
.on("tick", function() {
svg.selectAll('.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 })
svg.selectAll('.node')
.attr("cx", function (d) { return d.x })
.attr("cy", function (d) { return d.y })
.attr("transform", function (d) {
return "translate(" + d.x + "," + d.y + ")";
})
});
function update() {
// update links
var link = svg.selectAll('.link').data(graph.links);
link.enter()
.insert('line', '.node')
.attr('class', 'link')
.style('stroke', '#d9d9d9');
link
.exit()
.remove()
// update nodes
var node = svg.selectAll('.node').data(graph.nodes);
var g = node.enter()
.append('g')
.attr('class', 'node');
g.append('circle')
.attr("r", 20)
.style("fill", "#d9d9d9");
g.append('text')
.attr("class", "text")
.text(function (d) { return d.name });
node
.exit()
.remove();
// update simulation
simulation
.nodes(graph.nodes)
.force("link", d3.forceLink(graph.links).distance(100))
.force("charge", d3.forceManyBody().strength(-200))
.restart()
};
function addNode(node) {
graph.nodes.push(node);
update();
};
function connectNodes(source, target) {
graph.links.push({
source: source,
target: target,
});
update();
};
addNode({
id: "you",
name: "you",
});
let index = 1;
// add a new node every three seconds and connect to 'you'
const interval = window.setInterval(() => {
let id = Math.random().toString(36).replace('0.','');
id = id.slice(0,4);
addNode({
id: id,
name: id
});
connectNodes(0, index);
index++;
}, 3000);
// no more than 8 nodes
setTimeout(() => {
clearInterval(interval)
}, 3000 * 8);Run Code Online (Sandbox Code Playgroud)
<html>
<head>
<script src="https://d3js.org/d3.v7.min.js"></script>
</head>
<body>
<svg width="400" height="200"></svg>
</body>
</html>Run Code Online (Sandbox Code Playgroud)
d3js v7 代码片段不会产生与 d3js v3 相同的结果 - 这是为什么?我所做的确切更改可以在此差异中看到: https: //www.diffchecker.com/wdq7AFbU。
即使不添加任何连接,两种实现之间也存在差异。v3 实现使“you”节点从随机方向飞入,而使用 v7 实现时“you”节点始终从同一方向飞入。
由于 v7 实现中的新节点卡在左上角,因此力的施加方式似乎也存在一些差异。
我注意到 DOM 的属性正确地反映了状态。只是模拟提前停止了。
\n简而言之, 的默认值对于预期结果来说太短d3.force.alphaDecay了;指示模拟的结束。尝试稍微扩大该值。根据d3-force github readme ,最新的默认值为0.001 。在我的测试会话中,将值设置为alphaDecayalphaDecay1/5(0.0002)似乎足以获得相同的结果。
尝试运行下面的代码。效果很好。
\n使用 DOM 和 SVG 时,尝试添加匹配的data-ooo标签以查看是否d3.selection正常工作。我已将节点数据的属性(例如.index和 ).target添加到.source属性(例如data-index,data-id,data-target,data-source...和 )中,注意到一切都已就位。
var svg = d3.select("svg"),\n width = +svg.attr("width"),\n height = +svg.attr("height");\n\ngraph = {\n nodes: [],\n links: [],\n}\n\nvar simulation = d3.forceSimulation()\n .force("center", d3.forceCenter(width / 2, height / 2).strength(0.01))\n .nodes(graph.nodes)\n .force("link", d3.forceLink(graph.links).distance(100))\n .on("tick", function() {\n svg.selectAll(\'.link\')\n .attr("x1", function (d) { return d.source.x })\n .attr("y1", function (d) { return d.source.y })\n .attr("x2", function (d) { return d.target.x })\n .attr("y2", function (d) { return d.target.y })\n \n svg.selectAll(\'.node\')\n .attr("cx", function (d) { return d.x })\n .attr("cy", function (d) { return d.y })\n .attr("transform", function (d) {\n return "translate(" + d.x + "," + d.y + ")";\n })\n }).alphaDecay(0.0002) // just added alpha decay to delay end of execution\n \nfunction update() {\n // update links\n var link = svg.selectAll(\'.link\').data(graph.links);\n link.enter()\n .insert(\'line\', \'.node\')\n .attr(\'class\', \'link\')\n .style(\'stroke\', \'#d9d9d9\');\n link\n .exit()\n .remove()\n \n // update nodes\n var node = svg.selectAll(\'.node\').data(graph.nodes);\n var g = node.enter()\n .append(\'g\')\n .attr(\'class\', \'node\');\n g.append(\'circle\')\n .attr("r", 20)\n .style("fill", "#d9d9d9");\n g.append(\'text\')\n .attr("class", "text")\n .text(function (d) { return d.name });\n node\n .exit()\n .remove();\n \n // update simulation\n simulation\n .nodes(graph.nodes)\n .force("link", d3.forceLink(graph.links).distance(100))\n .force("charge", d3.forceManyBody().strength(-200))\n .restart()\n};\n\nfunction addNode(node) {\n graph.nodes.push(node);\n update();\n};\n\nfunction connectNodes(source, target) {\n graph.links.push({\n source: source,\n target: target,\n });\n update();\n};\n\naddNode({\n id: "you",\n name: "you",\n});\n\nlet index = 1;\n\n// add a new node every three seconds and connect to \'you\'\nconst interval = window.setInterval(() => {\n let id = Math.random().toString(36).replace(\'0.\',\'\');\n id = id.slice(0,4);\n addNode({\n id: id,\n name: id\n });\n \n connectNodes(0, index);\n index++;\n}, 3000);\n\n// no more than 8 nodes\nsetTimeout(() => {\n clearInterval(interval)\n}, 3000 * 8);Run Code Online (Sandbox Code Playgroud)\r\n<html>\n<head>\n <script src="https://d3js.org/d3.v7.min.js"></script>\n</head>\n<body>\n <svg width="400" height="200"></svg>\n</body>\n</html>Run Code Online (Sandbox Code Playgroud)\r\n\n\n执行simulation.restart().alpha(0.3)似乎给出了与我之前的帖子的答案中提到的相同的效果。两者有什么区别吗?
\n
d3-force github 自述文件说alpha代表熵。简单来说,alpha代表模拟的生命周期;alpha=1 代表开始,alpha=0 代表结束。
\n\n\nhttps://github.com/d3/d3-force#simulation_alpha
\nα 大致类似于模拟退火中的温度。随着模拟 \xe2\x80\x9c 冷却\xe2\x80\x9d,它会随着时间的推移而减小。当alpha达到alphaMin时,模拟停止
\n
这是一个简单的伪代码来说明这个想法。
\nalpha = 1\nalphaDecay = 0.002\nfunction tick() {\n alpha = alpha - alphaDecay\n}\n\nloop {\n tick()\n if alpha equals to 0 then end simulation\n}\nRun Code Online (Sandbox Code Playgroud)\n评论中提到的先前答案在重新启动时增加了alpha,因为他想在重置后给模拟更多的时间。
\n在我的回答中,我将alphaDecay设置为较低的数字,以便模拟可以工作更长的时间。
\n\n\n另外,v3 和 v7 的实现仍然存在一些差异;1) v3 中的碰撞更有弹性,2) 添加的新节点来自随机方向。您知道在 v7 实现中可以修复哪些问题来获得 1) 和 2) 吗?
\n
请阅读此d3-force v1 github 变更日志;自 d3 v4 以来,d3-force 成为一个单独的软件包,此更改日志解释了这些更改。
\n变更日志提到了许多改进:
\n\n\n力模拟现在使用速度 Verlet 积分而不是位置 Verlet,跟踪节点\xe2\x80\x99 位置(node.x、node.y)和速度(node.vx、node.vy)而不是它们之前的位置(node .px、node.py)。\n新的链接力取代了force.linkStrength,并采用更好的默认启发式方法来提高稳定性。
\n
d3-force 的物理集成已得到改进,精度更高。这就是它看起来与 v3 实现不同的原因。
\n虽然可以以特定方式调整模拟外观,但这more elastic意味着什么呢?是否意味着反作用力更强?或者这是否意味着更快的动画(但在相同的时间内)?只要请求更详细,它肯定可以调整。每个 d3 包都具有令人惊讶的简单结构和公式。可以查看内部并改变其内部功能。
https://github.com/d3/d3-force#simulation_nodes
\n在模拟过程中操纵节点的.x和来改变它们的位置。.y
addNode({ id: /* ... */, x: 0, y: 100}) // like this\nRun Code Online (Sandbox Code Playgroud)\n编辑:我的回答中有一些关于 alpha 和时间的增减关系的拼写错误。
\n