提高D3js力导向图加载速度

D3力导向图

过去一段时间曾两次使用了 D3 力导向图来描绘族群关系。

http://bl.ocks.org/mbostock/1062288

第一次用的时候不求甚解,交差了事。当时点也不多,很顺利完成了任务。这一次确不同,每个图里要渲染的有成千上万个点,每次渲染都死慢死慢,一大堆点动来动去,动半天才稳定下来,这一晃几分钟过去了。

阅读了一下官方文档 ,发现问题出来 tick 上。

force.start() 后,有一个计时器不停地触发 tick 直到所有节点最终受力平衡稳定下来。

可以理解为,有个计时器不停在打点,每打一次点需要触发一次 tick() 里的动作。而 tick() 的默认动作是重绘所有节点和连线位置。因为图形渲染需要时间长,渲染的次数又多,所以需要等很长时间。

function tick() {
  link.attr("x1", function(d) { return d.source.x; })
      .attr("y1", function(d) { return d.source.y; })
      .attr("x2", function(d) { return d.target.x; })
      .attr("y2", function(d) { return d.target.y; });

  node.attr("cx", function(d) { return d.x; })
      .attr("cy", function(d) { return d.y; });
}

怎样加快出图的速度呢?或者说,能不能不看中间复杂的变化过程,只看最终稳定的结果?

从官方文档里查到 alpha 参数。这个值最开始是 1,随着图形趋于稳定,它也会逐渐趋近 0。

force.alpha([value])

Gets or sets the force layout's cooling parameter, alpha. If value is specified, sets alpha to the specified value and returns the force layout. If value is greater than zero, this method also restarts the force layout if it is not already running, dispatching a "start" event and enabling the tick timer. If value is nonpositive, and the force layout is running, this method stops the force layout on the next tick and dispatches an "end" event. If value is not specified, this
method returns the current alpha value.

于是,对原来的代码稍作修改:

force.on("tick", function () {

    if(force.alpha()<=0.05){  // 足够稳定时,才渲染一次

      link.attr("x1", function (d) { return d.source.x; })
          .attr("y1", function (d) { return d.source.y; })
          .attr("x2", function (d) { return d.target.x; })
          .attr("y2", function (d) { return d.target.y; });
      node.attr("cx", function (d) { return d.x; })
          .attr("cy", function (d) { return d.y; });

      force.stop(); // 渲染完成后立即停止刷新
    }
});

这样修改之后,一个上万节点的图,通常在几秒内就可以绘制完成了。比如出这么个图,几秒钟就搞定了。

阅读 107