Empresas
Empregos
  • Sobre nós
  • Soluções
    • Publicação de vagas
      Publique sua vaga e receba candidatos qualificados em 48h.
    • Avaliações de candidatos
      Mais de 500 testes técnicos e psicológicos, mais anti-fraude.
    • Headhunting
      Busca executiva personalizada do início ao fim.
    • Folha de Pagamento + EOR
      Dispersão de folha e EOR em mais de 15 países da LATAM.
  • Preços
  • Empregos

0

305
Visualizações
Unable to update nodes position using d3 force and Svelte

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!

about 4 years ago · Juan Pablo Isaza
1 Respostas
Responde à pergunta

0

One of the issues is that you replace the nodes/links references. This entails that the simulation happens on objects that you no longer have any reference to, while you render a different set of objects, which after the first tick will never change again.

One approach would be to add a separate object/s that are used for updating the DOM generated by Svelte.

e.g.

  let links = $network.links.map(d => cloneDeep(d));
  let nodes = $network.nodes.map(d => cloneDeep(d));
  // Initial render state
  let render = {
    links,
    nodes,
  }

  // ...

  function simulationUpdate() {
    // (No need to call tick, a tick has already happened here)
    render = {
      nodes: nodes.map(d => cloneDeep(d)),
      links: links.map(d => cloneDeep(d)),
    };
  }

Adjust the each loops. You also need to make the links loop keyed, or adjust the Link component code to make sourceNode/targetNode reactive instead of const:

{#each render.links as link (link)}
...
{#each render.nodes as node}

(Using the link itself as key causes a re-render of all elements because the links are cloned, so none of the objects are identical.)

Also, you might need to call restart when the center changes to make sure it applies correctly:

  $: {
    simulation
      .force("center")
      .x(width / 2)
      .y(height / 2);
    simulation.restart();
  }

Alternatively to having separate objects for rendering, you can use the {#key} feature to make the DOM re-render (for large graphs this may have a negative impact). You only need some variable to change and use that as trigger:

  let renderKey = false;
  // ...
  function simulationUpdate() {
    renderKey = !renderKey;
  }
    {#if simulation}
      {#key renderKey}
        <g>
          {#each links as link}
            <Link {link} {nodes} />
          {/each}
        </g>
        <g>
          {#each nodes as node}
              <Node {node} x={node.x} y={node.y} />
          {/each}
        </g>
      {/key}
    {/if}
about 4 years ago · Juan Pablo Isaza Relatório
Responde à pergunta
Encontrar trabalhos remotos

Descubra a nova forma de encontrar um emprego!

melhores empregos
Principais categorias de trabalho
Empresas
Postar vaga Preços Comercial
Jurídico
Termos e Condições Política de privacidade
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomende algumas ofertas para mim
Preciso de ajuda