Я использую d3.js v6 с силовым макетом для представления сетевого графа. Я добавляю и удаляю узлы, но когда я перезапускаю симуляцию, все узлы переходят в верхнее левое положение, а затем возвращаются в исходное положение.
У меня есть следующий фрагмент кода, который точно показывает, что я имею в виду, я видел другие примеры в Интернете, которые отлично работают, но не смог найти, что я делаю неправильно, любая помощь действительно приветствуется.
var dataset = {
nodes: [
{
id: 1
},
{
id: 2
}
],
links: [{
id: 1,
source: 1,
target: 2
}]
};
let switchBool = false;
let svg = d3.select('svg')
.attr('width', '100%')
.attr('height', '100%');
const width = svg.node()
.getBoundingClientRect().width;
const height = svg.node()
.getBoundingClientRect().height;
console.log(`${width}, ${height}`);
svg = svg.append('g');
svg.append('g')
.attr('class', 'links');
svg.append('g')
.attr('class', 'nodes');
const simulation = d3.forceSimulation();
initSimulation();
let link = svg.select('.links')
.selectAll('line');
loadLinks();
let node = svg.select('.nodes')
.selectAll('.node');
loadNodes();
restartSimulation();
function initSimulation() {
simulation
.force('link', d3.forceLink())
.force('charge', d3.forceManyBody())
.force('collide', d3.forceCollide())
.force('center', d3.forceCenter())
.force('forceX', d3.forceX())
.force('forceY', d3.forceY());
simulation.force('center')
.x(width * 0.5)
.y(height * 0.5);
simulation.force('link')
.id((d) => d.id)
.distance(100)
.iterations(1);
simulation.force('collide')
.radius(10);
simulation.force('charge')
.strength(-100);
}
function loadLinks() {
link = svg.select('.links')
.selectAll('line')
.data(dataset.links, (d) => d.id)
.join(
enter => enter.append('line').attr('stroke', '#000000'),
);
}
function loadNodes() {
node = svg.select('.nodes')
.selectAll('.node')
.data(dataset.nodes, (d) => d.id)
.join(
enter => {
const nodes = enter.append('g')
.attr('class', 'node')
nodes.append('circle').attr('r', 10);
return nodes;
},
);
}
function restartSimulation() {
simulation.nodes(dataset.nodes);
simulation.force('link').links(dataset.links);
simulation.alpha(1).restart();
simulation.on('tick', ticked);
}
function ticked() {
link
.attr('x1', (d) => d.source.x)
.attr('y1', (d) => d.source.y)
.attr('x2', (d) => d.target.x)
.attr('y2', (d) => d.target.y);
node.attr('transform', (d) => `translate(${d.x},${d.y})`);
}
function updateData() {
switchBool = !switchBool;
if (switchBool) {
dataset.nodes.push({id: 3});
dataset.links.push({id: 2, source: 1, target: 3});
} else {
dataset.nodes.pop();
dataset.links.pop();
}
loadLinks();
loadNodes();
restartSimulation();
}
<script src="https://d3js.org/d3.v6.min.js"></script>
<div>
<button onclick="updateData()">Add/Remove</button>
<svg></svg>
</div>