antvis / layout

Layout algorithms for graphs.
201 stars 55 forks source link

多次迭代的布局在 Worker 中运行是否还需要触发 tick 事件? #132

Open xiaoiver opened 1 year ago

xiaoiver commented 1 year ago

d3.force 提供了 tick 方法,配合 stop 可以完成同步方式下的 static force layouthttps://github.com/d3/d3-force#simulation_tick

const simulation = d3.forceSimulation(nodes)
// 省略其他参数
  .stop();

for () {
  simulation.tick();
}

// 一次性计算完成后渲染

文档中也建议这种方式适合放在 WebWorker 中运行,同时也不会触发 tick 事件,同步计算完成后进行渲染等操作。

如果把 Worker 中的首次布局计算看作服务端渲染,整个过程有点像 React SSR 中的 hydrate

在 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

xiaoiver commented 1 year ago

使用文档 & 例子: https://observablehq.com/d/2db6b0cc5e97d8d6#cell-1315

zuiidea commented 1 year ago

触发 tick 更好一些,在渲染性能不是瓶颈的情况下,对于耗时较长的大图,使用tick呈现布局的变化过程可以给用户更好的体验。当然可能不同业务场景会不一样,但作为库应当提供这个接口。