I tried to make a simple d3 force-directed graph with the svelte reactive statement, but apparently, it's not working together.
1st example, this is the simplest way I make the graph (working REPL):
<script>
import * as d3 from 'd3'
import { onMount } from 'svelte'
import data from './data.json'
let svg,
width = 500,
height = 400,
links, nodes
let simulation = d3.forceSimulation(data)
</script>
<svg
bind:this={svg}
width={width}
height={height}
viewBox="{-width/2} {-height/2} {width} {height}"
>
{#each data as node}
<g >
<circle bind:this={nodes}
fill="cornflowerblue"
r="5"
cx={node.x}
cy={node.y}
></circle>
</g>
{/each}
</svg>
2nd example, I want to add some force with reactive statement to it, let's say forceX, and it doesn't work, until I add simulation.tick(100)
or any number > 0 inside the .tick() (working REPL):
<script>
// the before part is still the same
let simulation = d3.forceSimulation(data)
$: simulation.force("x", d3.forceX(0)) // not working, the circle stay at the initial position
$: simulation.tick(100) // the force worked, but not animating from the initial position
</script>
<!-- the rest of svg is still the same -->
3rd example, I tried the .on("tick") with function. The function is fired (I only give a console.log
to test if the tick
working.) but I have no idea how to pass the modified data to the {#each}
block. Looking at the console.log(data) inside the update function, the x
and y
data are changing every tick, but not updating the actual circle
position. (working REPL):
<script>
// the before part is still the same
let simulation = d3.forceSimulation(data)
function update() {
console.log(data)
return data // I don't know if it's the correct way, but still not working tho
}
$: simulation.force("x", d3.forceX(0))
$: simulation.on("tick", update)
</script>
<!-- the rest of svg is still the same -->
data.json
[
{value: 10},
{value: 12},
{value: 15},
{value: 8},
{value: 7},
{value: 12},
{value: 25},
{value: 20},
{value: 16},
{value: 13},
{value: 5},
{value: 7},
{value: 8},
{value: 10},
{value: 12},
{value: 14},
{value: 24},
{value: 23},
{value: 22},
{value: 11},
]
CodePudding user response:
The simplest way to address your problem in step #3 is to declare a displayData
array, to copy over data
into displayData
inside your update function, and to iterate your #each
block over displayData
.
data
being an import variable, you cannot directly reassign to it, and because you cannot reassign to it, it is not reactive and data
updates do not trigger a re-render.
By assigning data
to displayData
in your update
function (which will run on every tick), you will trigger a re-render of your #each
block (provided you now iterate over displayData
).