迄今为止,所有其他答案都需要访问数据,并对其进行迭代,因此复杂性至少为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在评论中的说法,如果您的 SVGviewBox
不是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
];