多次迭代的布局在 Worker 中运行是否还需要触发 tick 事件?
xiaoiver opened this issue · comments
d3.force 提供了 tick
方法,配合 stop
可以完成同步方式下的 static force layout:
https://github.com/d3/d3-force#simulation_tick
const simulation = d3.forceSimulation(nodes)
// 省略其他参数
.stop();
for () {
simulation.tick();
}
// 一次性计算完成后渲染
文档中也建议这种方式适合放在 WebWorker 中运行,同时也不会触发 tick
事件,同步计算完成后进行渲染等操作。
如果把 Worker 中的首次布局计算看作服务端渲染,整个过程有点像 React SSR 中的 hydrate:
- Worker 中执行 static force layout,返回结果给主线程。类似返回服务端渲染结果 HTML 片段
- 主线程接收到首次布局结果,需要更新图模型中的数据(如果后续需要调用 assign 的话),便于 drag 时继续执行使用。类似给 HTML 添加交互
在 Supervisor 中使用
配合 Supervisor 使用时,不关心 tick
,只关心全部迭代完成后的最终结果:
const graph = new Graph();
const force = new D3ForceLayout();
const supervisor = new Supervisor(graph, force, { iterations: 1000 });
supervisor.on('layoutend', (positions) => {
// 进行首次渲染,并绑定节点拖拽事件
createNodesEdges(positions);
// 更新图模型中的数据
graph.mergeNodeData();
});
supervisor.start();
当对单个节点进行拖拽交互时:
function moveAt(target, canvasX, canvasY) {
// 更新图模型当前节点的数据
graph.mergeNodeData(positions.nodes[i].id, {
x: canvasX - shiftX,
y: canvasY - shiftY,
});
// 停止当前布局
force.stop();
// 开始新的布局计算
force.assign(graph, {
center: [200, 200], // The center of the graph by default
preventOverlap: true,
nodeSize: 20,
onTick: (positions) => {
updateNodesEdges(positions);
},
});
}
在 Worker 中执行 static layout:
const graph = new Graph();
const layout = new D3ForceLayout();
layout.stop();
layout.tick(1000);
// 通知主进程计算完成
之前在 layout 中的使用方式:https://github.com/antvis/layout/blob/master/src/layout/force/force.ts#L232-L248
触发 tick
更好一些,在渲染性能不是瓶颈的情况下,对于耗时较长的大图,使用tick
呈现布局的变化过程可以给用户更好的体验。当然可能不同业务场景会不一样,但作为库应当提供这个接口。