antvis / layout

Layout algorithms for graphs.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

多次迭代的布局在 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呈现布局的变化过程可以给用户更好的体验。当然可能不同业务场景会不一样,但作为库应当提供这个接口。