web-dev-qa-db-ja.com

d3.jsは、ロード後にレイアウトの自動ズーム/スケールを強制します

Flowingdata.comの このニースフォースレイアウト を使用してネットワーク図を作成しています。

enter image description here

enter image description here

enter image description here

私の図は現在、5から750のノードとそれらの関係を示しています。それは私のニーズに合うようにいくつかのカスタム変更でうまく機能します。しかし、私が仕事に就けないことが1つあります。コンテナを自動調整するためのviewBoxpreserveAspectRatioがあります。ただし、ノードの数によっては、エッジ(主に上部と下部)の周りに切り取られるノードが常にあります。 。また、ノードが非常に少ない場合は、ノードが中央に表示され、周囲に大きな空きスペースがあります(ノードは大きなコンテナです)。

レイアウトを自動ズームまたは拡大縮小して自動フィットする方法はありますか?大きなレイアウトがいくらかズームアウトされ、小さなレイアウトがズームインされるようにします。ズームイベントを設定しているので、スクロールとパンは魅力のように機能します。しかし、内容に合わせて自動的にそれを行うことはできますか?

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');
19
DaFrenk

これまでの他のすべての回答はデータへのアクセスを必要とし、データを反復処理するため、複雑さは少なくとも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);
18
TWiStErRob

次に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();
}
1
Phuoc Do

あなたのコードはこれに似ているはずです

   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を考慮して初期座標を調整する必要があります。

1

ノードを反復処理できます。すべてのノードの最大および最小の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つのうち大きい方のズームを選択します。

0
Marjancek