過去一段時間曾兩次使用了 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(); // 渲染完成后立即停止刷新
}
});
這樣修改之后,一個上萬節點的圖,通常在幾秒內就可以繪制完成了。比如出這么個圖,幾秒鍾就搞定了。