network graph: relayout when the slider is released, not on every pixel

layoutGraph() runs 140 iterations of an O(n^2) force loop. Measured by calling
it standalone with the same constants:

     20 nodes     24 ms
     50 nodes     57 ms
    100 nodes    199 ms
    200 nodes    729 ms   (NODE_LIMIT)

The edge length slider called it from oninput, which a range input fires dozens
of times per drag, each call blocking the main thread. Dragging it on a level 2
graph froze the tab for several seconds.

oninput now only moves the label, which is what has to stay live, and onchange
does the layout once, on release.
This commit is contained in:
jolavillette 2026-08-14 13:36:15 +02:00
parent 307981e960
commit 1a9442fae2

View File

@ -209,10 +209,15 @@ const NetworkGraph = () => {
`Edge length ${edgeLength}`,
m('input[type=range][min=60][max=180][step=5]', {
value: edgeLength,
// The label follows the slider, the layout waits for the release:
// layoutGraph() is 140 iterations of an O(n^2) force loop, which
// measures 24 ms at 20 nodes, 199 ms at 100 and 729 ms at the 200
// node cap. A range input fires oninput dozens of times per drag,
// each one blocking the main thread for that long.
oninput: (event) => {
edgeLength = Number(event.target.value);
redrawLayout();
},
onchange: redrawLayout,
}),
]),
m('.network-graph__zoom-control', [