I'm trying to create a simple network using D3 force and Svelte.
The network position should depend on the container dimensions computed using bind:clientWidth and bind:clientHeight.
I mean that if the network container has width=200, height=300, then the network center should be x=100, y=150.
The code that creates the graph is this:
export let width;
export let height;
let svg;
let simulation;
let zoomTransform = zoomIdentity;
let links = $network.links.map(d => cloneDeep(d));
let nodes = $network.nodes.map(d => cloneDeep(d));
simulation = forceSimulation(nodes)
.force(
"link",
forceLink(
links.map(l => {
return {
...l,
source: nodes.find(n => n.id === l.source),
target: nodes.find(n => n.id === l.source)
};
})
)
)
.force(
"collision",
forceCollide()
.strength(0.2)
.radius(120)
.iterations(1)
)
.force("charge", forceManyBody().strength(0))
.force("center", forceCenter(width / 2, height / 2))
.on("tick", simulationUpdate);
// .stop()
// .tick(100)
function simulationUpdate() {
simulation.tick();
nodes = nodes.map(n => cloneDeep(n));
links = links.map(l => cloneDeep(l));
}
$: {
simulation
.force("center")
.x(width / 2)
.y(height / 2);
}
</script>
<svg bind:this={svg} viewBox={`${0} ${0} ${width} ${height}`} {width} {height}>
{#if simulation}
<g>
{#each links as link}
<Link {link} {nodes} />
{/each}
</g>
{:else}
null
{/if}
{#if simulation}
<g>
{#each nodes as node}
<Node {node} x={node.x} y={node.y} />
{/each}
</g>
{:else}
null
{/if}
</svg>
It's very simple: width and height are props.
It create some local stores and updated them with new data.
Since that width and height are dynamic, I compute the forceCenter force in a reactive block.
Then, to draw the nodes, I use a Node component with prop nodes, x, y. I kwnow that I can use only nodes or only x,y but it was a test. The problem is that the nodes position never changes even if width and height change.
So if you change the window size, the graph is not recomputed but it should. Why?
HERE a complete working example
Thank you!