d3.js force layout auto zoom/scale after loading

狂风中的少年 提交于 2019-11-30 06:38:18
TWiStErRob

All the other answers to date require access to data, and iterates through it so the complexity is at least O(nodes). I kept looking and found a way that is solely based on already rendered visual size, getBBox() which is hopefully O(1). It doesn't matter what's in it or how it's laid out, just its size and the parent container's size. I managed to whip up this based on 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);
}

EDIT: The above works in D3 v3. Zoom is changed in D3 v4 and v5, so you have to make some minor changes to the last portion (the code below console.trace):

    var transform = d3.zoomIdentity
        .translate(translate[0], translate[1])
        .scale(scale);

    root
        .transition()
        .duration(transitionDuration || 0) // milliseconds
        .call(zoom.transform, transform);

Your code should be similar to this

   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);

This code work only for xAxis. Because "global circle" svg RX === RY. If it not was for you then you can add same logic for yAxis var startY. Also you need to adjust the initial coordinates considering cr of circle nodes.

You can iterate over the nodes; get the max and min x and y values for all nodes, and calculate the needed zoom and translation to cover all your viz within the SVG size.

I have a piece of code that centres the graph to a given node; this might help you get an idea.

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);

Where w, h are the width and hieight of my SVG canvas, and d the node I want to centre to.

Instead of centring at d.x,d.y, you should calculate the average x and y. And calculate your zoom-scale that will make the width (and height) of your graph to fit in your SVG's width(and size) Pick the bigger zoom of the two.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!