d3.js加载后强制布局自动缩放/缩放

xa9qqrwz  于 2022-11-12  发布在  其他
关注(0)|答案(4)|浏览(291)

我正在使用www.example.com上的this nice force layoutFlowingdata.com创建网络图。

我的图目前显示了5到750个节点及其关系。它可以很好地与一些自定义的变化,以满足我的需要。但有一件事我不能得到工作。我有一个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');
raogr8fs

raogr8fs1#

到目前为止,所有其他的答案都需要访问数据,并通过它进行迭代,因此复杂度至少为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);
}

编辑:上面的代码在D3 v3中有效。缩放在D3 v4和v5中发生了变化,所以你必须对最后一部分做一些小的修改(console.trace下面的代码):

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

root
  .transition()
  .duration(transitionDuration || 0) // milliseconds
  .call(zoom.transform, transform);
  • 警告 *(未经测试,但请注意):根据注解中的Ngo Quang Duong,如果SVG viewBox不是0 0 width height,则可能需要调整一些变量,但即使这样也可能不够:
var fullWidth  = parent.viewBox.baseVal.width ;
var fullHeight = parent.viewBox.baseVal.height;
var translate = [
    parent.viewBox.baseVal.x + fullWidth  / 2 - scale * midX,
    parent.viewBox.baseVal.y + fullHeight / 2 - scale * midY
];
rhfm7lfc

rhfm7lfc2#

您的代码应该类似于

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。因为“全局圆”SVG RX === RY。如果不适用,那么您可以为yAxis var startY添加相同的逻辑。此外,您需要考虑圆节点的cr来调整初始坐标。

j8yoct9x

j8yoct9x3#

适用于D3 v7的已接受答案:

zoomToFit() {
    var bounds = this.svgInner.node().getBBox();
    var parent = this.svgInner.node().parentElement;
    var fullWidth = parent.clientWidth,
      fullHeight = parent.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 = (.9) / Math.max(width / fullWidth, height / fullHeight);
    var translate = [fullWidth / 2 - scale * midX, fullHeight / 2 - scale * midY];

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

    this.svgInner.attr("transform", transform);
  }
1rhkuytd

1rhkuytd4#

您可以迭代节点;获取所有节点的最大和最小x和y值,并计算所需的缩放和平移,以在SVG大小内覆盖所有可视化。
我有一段代码,它使图形以给定的节点为中心;这也许能帮你想个主意

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是我想居中的节点。
你应该计算x和y的平均值,而不是以d.x,d.y为中心。然后计算缩放比例,使图形的宽度(和高度)适合SVG的宽度(和大小)。

相关问题