Flowingdata.comの このニースフォースレイアウト を使用してネットワーク図を作成しています。
私の図は現在、5から750のノードとそれらの関係を示しています。それは私のニーズに合うようにいくつかのカスタム変更でうまく機能します。しかし、私が仕事に就けないことが1つあります。コンテナを自動調整するためのviewBox
とpreserveAspectRatio
があります。ただし、ノードの数によっては、エッジ(主に上部と下部)の周りに切り取られるノードが常にあります。 。また、ノードが非常に少ない場合は、ノードが中央に表示され、周囲に大きな空きスペースがあります(ノードは大きなコンテナです)。
レイアウトを自動ズームまたは拡大縮小して自動フィットする方法はありますか?大きなレイアウトがいくらかズームアウトされ、小さなレイアウトがズームインされるようにします。ズームイベントを設定しているので、スクロールとパンは魅力のように機能します。しかし、内容に合わせて自動的にそれを行うことはできますか?
D3.jsスタートアップコード:
vis = d3.select(selection)
.append("svg")
.attr("viewBox", "0 0 " + width + " " + height )
.attr("preserveAspectRatio", "xMidYMid meet")
.attr("pointer-events", "all")
.call(d3.behavior.zoom().scaleExtent([.1, 3])
.on("zoom", redraw)).append('g');
これまでの他のすべての回答はデータへのアクセスを必要とし、データを反復処理するため、複雑さは少なくともO(nodes)
です。私は探し続け、すでにレンダリングされた視覚的なサイズgetBBox()
のみに基づく方法を見つけました。これはうまくいけばO(1)
です。何が入っているか、どのようにレイアウトされているかは関係ありません。サイズと親コンテナのサイズだけです。 http://bl.ocks.org/mbostock/9656675 に基づいてこれを作成することができました:
var root = // any svg.select(...) that has a single node like a container group by #id
function lapsedZoomFit(ticks, transitionDuration) {
for (var i = ticks || 100; i > 0; --i) force.tick();
force.stop();
zoomFit(transitionDuration);
}
function zoomFit(transitionDuration) {
var bounds = root.node().getBBox();
var parent = root.node().parentElement;
var fullWidth = parent.clientWidth || parent.parentNode.clientWidth,
fullHeight = parent.clientHeight || parent.parentNode.clientHeight;
var width = bounds.width,
height = bounds.height;
var midX = bounds.x + width / 2,
midY = bounds.y + height / 2;
if (width == 0 || height == 0) return; // nothing to fit
var scale = 0.85 / Math.max(width / fullWidth, height / fullHeight);
var translate = [fullWidth / 2 - scale * midX, fullHeight / 2 - scale * midY];
console.trace("zoomFit", translate, scale);
root
.transition()
.duration(transitionDuration || 0) // milliseconds
.call(zoom.translate(translate).scale(scale).event);
}
編集:上記はD3v3で機能します。ズームはD3v4およびv5で変更されているため、最後の部分(console.trace
の下のコード)にいくつかの小さな変更を加える必要があります。
var transform = d3.zoomIdentity
.translate(translate[0], translate[1])
.scale(scale);
root
.transition()
.duration(transitionDuration || 0) // milliseconds
.call(zoom.transform, transform);
次に2つの例を示します。
自動ズーム付きの強制レイアウト(キャンバス):
https://vida.io/documents/pT3RDxrjKHLQ8CQxi
自動ズーム付きの強制レイアウト(SVG)
https://vida.io/documents/9q6B62xQgnZcYYen2
SVGのスケールと描画を実行する関数:
function scaleAndDraw() {
var xExtent = d3.extent(d3.values(nodes), function(n) { return n.x; }),
yExtent = d3.extent(d3.values(nodes), function(n) { return n.y; });
if ((xExtent[1] - xExtent[0]) > config.width) {
scaleX = (xExtent[1] - xExtent[0]) / config.width;
scaleY = (yExtent[1] - yExtent[0]) / config.height;
scale = 1 / Math.max(scaleX, scaleY);
translateX = Math.abs(xExtent[0]) * scale;
translateY = Math.abs(yExtent[0]) * scale,
svg.attr("transform", "translate(" +
translateX + "," + translateY + ")" +
" scale(" + scale + ")");
}
draw();
}
あなたのコードはこれに似ているはずです
vis = d3.select(selection)
.append("svg")
.attr("viewBox", "0 0 " + width + " " + height )
.attr("preserveAspectRatio", "xMidYMid meet")
.attr("pointer-events", "all")
.call(zoomListener)
.on("zoom", redraw));
var mainGroup = vis.append('g');
function zoom() {
mainGroup.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
}
var zoomListener = d3.behavior.zoom().on("zoom", zoom);
var xArray = YOUR_NODES.map(function(d) { //YOUR_NODES is something like json.nodes after forse.end()
return d.x
});
var minX = d3.min(xArray);
var maxX = d3.max(xArray);
var scaleMin = Math.abs(width / (maxX - minX));
var startX = (minX) / scaleMin;
var startY = 50 / scaleMin;
// Same as in the zoom function
mainGroup.attr("transform", "translate(" + [startX, startY] + ")scale(" + scaleMin + ")");
// Initialization start param of zoomListener
zoomListener.translate([startX, startY]);
zoomListener.scale(scaleMin);
zoomListener.scaleExtent([scaleMin, 1])
vis.call(zoomListener);
このコードはxAxisでのみ機能します。 「グローバルサークル」svgRX === RYであるため。そうでない場合は、yAxis var startY
に同じロジックを追加できます。また、円ノードのcr
を考慮して初期座標を調整する必要があります。
ノードを反復処理できます。すべてのノードの最大および最小のx値とy値を取得し、SVGサイズ内のすべてのvizをカバーするために必要なズームと変換を計算します。
グラフを特定のノードの中央に配置するコードがあります。これはあなたがアイデアを得るのを助けるかもしれません。
zs = zoom.scale()
zt = zoom.translate();
dx = (w/2.0/zs) - d.x;
dy = (h/2.0/zs) - d.y;
zoom.translate([dx, dy]);
zoom.scale(zs);
ここで、w、hはSVGキャンバスの幅と高さ、dは中央に配置するノードです。
D.x、d.yを中心とする代わりに、平均xとyを計算する必要があります。そして、グラフの幅(および高さ)がSVGの幅(およびサイズ)に合うようにズームスケールを計算します。2つのうち大きい方のズームを選択します。