我正在嘗試使用 D3 force 和 Svelte 創建一個簡單的網路。
網路位置應取決于使用bind:clientWidth和計算的容器尺寸bind:clientHeight。我的意思是如果網路容器的寬度=200,高度=300,那么網路中心應該是x=100,y=150。
創建圖表的代碼是這樣的:
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>
這很簡單:width并且height是道具。它創建一些本地商店并用新資料更新它們。由于那width是height動態的,我計算forceCenter了反應塊中的力。
然后,為了繪制節點,我使用了一個Node帶有 prop nodes、x、的組件y。我知道我只能使用nodes或只能使用x,y,但這是一個測驗。問題是節點位置即使發生變化也不會width改變height。
因此,如果您更改視窗大小,則不會重新計算圖形,但應該重新計算。為什么?
這是一個完整的作業示例
謝謝!
uj5u.com熱心網友回復:
問題之一是您替換了nodes/links參考。這意味著模擬發生在您不再有任何參考的物件上,而您渲染一組不同的物件,在第一次滴答之后將永遠不會再改變。
一種方法是添加一個單獨的物件,用于更新 Svelte 生成的 DOM。
例如
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)),
};
}
調整each回圈。您還需要使鏈接回圈鍵控,或調整Link組件代碼以使sourceNode/targetNode反應而不是const:
{#each render.links as link (link)}
...
{#each render.nodes as node}
(使用link自身作為鍵會導致重新渲染所有元素,因為鏈接是克隆的,所以沒有一個物件是相同的。)
此外,您可能需要restart在中心更改時致電以確保其正確應用:
$: {
simulation
.force("center")
.x(width / 2)
.y(height / 2);
simulation.restart();
}
或者使用單獨的物件進行渲染,您可以使用該{#key}功能使 DOM 重新渲染(對于大圖,這可能會產生負面影響)。您只需要一些變數來更改并將其用作觸發器:
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}
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/482530.html
標籤:javascript d3.js 苗条
上一篇:d3用于說明折線圖中的缺失資料
