javascript d3.js 加载后强制布局自动缩放/缩放
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/16236600/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
d3.js force layout auto zoom/scale after loading
提问by DaFrenk
I'm using this nice force layoutfrom Flowingdata.com to create a network diagram.
我正在使用Flowingdata.com 的这个不错的力布局来创建网络图。
My diagram currently shows between 5 and 750 nodes with their relations. It works great with some custom changes to fit my needs. However one thing I can't get to work. I have a viewBox
with preserveAspectRatio
to auto fit the container it is in. But depending on the amount of nodes there are always some nodes around the edges (mainly top and buttom) that get cut off. And if there are very little nodes, it shows them in the middle with huge empty space around it (it's a big container it's in).
我的图表目前显示了 5 到 750 个节点及其关系。它适用于一些自定义更改以满足我的需求。但是有一件事我不能上班。我有一个viewBox
具有preserveAspectRatio
自动适应容器它。但是这取决于节点的数量总是有围绕得到切断边缘(主要是顶部和BUTTOM)一些节点。如果节点很少,它会在中间显示它们,周围有巨大的空白空间(它是一个大容器)。
Is there any way to auto zoom or scale the layout to auto fit? So that a big layout gets somewhat zoomed out and a small layout zoomed in. I have a zoom event setup so scrolling and panning works like a charm. But can it automatically do that to fit the contents?
有没有办法自动缩放或缩放布局以自动适应?这样一个大的布局会被缩小一些,而一个小的布局会被放大。我有一个缩放事件设置,所以滚动和平移就像一个魅力。但是它可以自动做到这一点以适应内容吗?
The d3.js startup code:
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');
回答by 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:
迄今为止,所有其他答案都需要访问数据,并对其进行迭代,因此复杂性至少为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);
}
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
):
编辑:以上适用于 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);
回答by Evgeniy Tkachenko
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.
此代码仅适用于 xAxis。因为“全球圈”svg RX === RY。如果它不适合您,那么您可以为 yAxis 添加相同的逻辑var startY
。您还需要考虑cr
圆形节点来调整初始坐标。
回答by Marjancek
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.
您可以遍历节点;获取所有节点的最大和最小 x 和 y 值,并计算所需的缩放和平移以覆盖 SVG 大小内的所有可视化。
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.
其中 w、h 是我的 SVG 画布的宽度和高度,d 是我想要居中的节点。
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.
您应该计算 x 和 y 的平均值,而不是以 dx,dy 为中心。并计算您的缩放比例,这将使您的图形的宽度(和高度)适合您的 SVG 的宽度(和大小) 选择两者中较大的缩放比例。