我正在尝试确定D3力导向图的尺寸,以便在渲染画布后可以调整其大小。
我想我快要到了-我可以得到单个节点的大小,但是我不知道如何遍历节点以检查每个节点的边界框(或者更好地,获得图形的整体大小)在一次)。调整大小当前是在单击按钮时发生的。
下面是我的代码(作为一种临时措施,我只是将单个节点的大小乘以15,但应根据呈现的图形的尺寸对其进行动态调整):
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.link {
fill: none;
stroke-width: 1.5px;
}
circle {
stroke: black;
stroke-width: 1.5px;
}
text {
font: 10px sans-serif;
pointer-events: none;
}
#resizebutton {
position: absolute;
top: 10px;
left: 20px;
height: 100px;
width: 100px;
}
#graph {
position: absolute;
top: 30px;
left: 200px;
}
</style>
<body>
<div id="root">
<div id="resizebutton">
<button onclick="resizeGraph()">Resize</button>
</div>
<div style="background-color:lightgrey" id="graph" height="800px" width="800px">
</div>
</div>
</body>
<script src="https://d3js.org/d3.v3.min.js"></script>
<script>
var nodes = [{
name: "node1"
},
{
name: "node2"
},
{
name: "node3"
},
{
name: "node4"
},
{
name: "node5"
},
{
name: "node6"
},
{
name: "node7"
},
{
name: "node8"
},
{
name: "node9"
}
];
var links = [{
source: 0,
target: 8
},
{
source: 1,
target: 8
},
{
source: 2,
target: 8
},
{
source: 3,
target: 8
},
{
source: 4,
target: 8
},
{
source: 5,
target: 8
},
{
source: 6,
target: 8
},
{
source: 7,
target: 8
}
];
var width = 800,
height = 800;
var force = d3.layout.force()
.nodes(nodes)
.links(links)
.size([width, height])
.linkDistance(150)
.charge(-300)
.on("tick", tick)
.start();
var svg = d3.select("#graph").append("svg")
.attr("width", width)
.attr("height", height);
var colors = d3.scale.category10();
var path = svg.append("g").selectAll("path")
.data(force.links())
.enter().append("line")
.attr('class', 'link')
.attr('stroke', function(d, i) {
return colors(i);
})
var circle = svg.append("g").selectAll("circle")
.data(force.nodes())
.enter().append("circle")
.attr("r", 8)
.attr('class', 'circle')
.attr('fill', function(d, i) {
return colors(i);
})
.call(force.drag);
var text = svg.append("g").selectAll("text")
.data(force.nodes())
.enter().append("text")
.attr("x", 14)
.attr("y", ".31em")
.text(function(d) {
return d.name;
});
function tick() {
path.attr({
x1: function(d) {
return d.source.x;
},
y1: function(d) {
return d.source.y;
},
x2: function(d) {
return d.target.x;
},
y2: function(d) {
return d.target.y;
}
});
circle.attr("transform", transform);
text.attr("transform", transform);
}
function transform(d) {
return "translate(" + d.x + "," + d.y + ")";
}
function resizeGraph() {
var bbox = circle.node().getBBox();
wid = bbox.width * 15;
hei = bbox.height * 15;
document.getElementById("graph").style.width = wid + "px";
document.getElementById("graph").style.height = hei + "px";
svg.attr("width", wid + "px");
svg.attr("height", hei + "px");
force.size([wid, hei]).resume();
};
</script>
非常感谢任何帮助。
答案 0 :(得分:2)
无论您的目标是什么,您都不需要使用实际的DOM元素(例如,使用getBBox()
)来获取力图的大小,只需使用其数据即可。
在您的代码中,这将为您提供包含图表的矩形的四个角:
var minX = d3.min(circle.data(), function(d){return d.x});
var minY = d3.min(circle.data(), function(d){return d.y});
var maxX = d3.max(circle.data(), function(d){return d.x});
var maxY = d3.max(circle.data(), function(d){return d.y});
这是一个基于您的代码的演示,单击获取大小绘制容器矩形(要获得准确的矩形,请在单击之前等待模拟停止):
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.link {
fill: none;
stroke-width: 1.5px;
}
circle {
stroke: black;
stroke-width: 1.5px;
}
text {
font: 10px sans-serif;
pointer-events: none;
}
#resizebutton {
position: absolute;
top: 10px;
left: 20px;
height: 100px;
width: 100px;
}
#graph {
position: absolute;
top: 30px;
left: 100px;
}
</style>
<body>
<div id="root">
<div id="resizebutton">
<button onclick="resizeGraph()">Get size</button>
</div>
<div id="graph" height="400px" width="400px">
</div>
</div>
</body>
<script src="https://d3js.org/d3.v3.min.js"></script>
<script>
var nodes = [{
name: "node1"
},
{
name: "node2"
},
{
name: "node3"
},
{
name: "node4"
},
{
name: "node5"
},
{
name: "node6"
},
{
name: "node7"
},
{
name: "node8"
},
{
name: "node9"
}
];
var links = [{
source: 0,
target: 8
},
{
source: 1,
target: 8
},
{
source: 2,
target: 8
},
{
source: 3,
target: 8
},
{
source: 4,
target: 8
},
{
source: 5,
target: 8
},
{
source: 6,
target: 8
},
{
source: 7,
target: 8
}
];
var width = 400,
height = 400;
var force = d3.layout.force()
.nodes(nodes)
.links(links)
.size([width, height])
.linkDistance(150)
.charge(-300)
.on("tick", tick)
.start();
var svg = d3.select("#graph").append("svg")
.attr("width", width)
.attr("height", height);
var colors = d3.scale.category10();
var path = svg.append("g").selectAll("path")
.data(force.links())
.enter().append("line")
.attr('class', 'link')
.attr('stroke', function(d, i) {
return colors(i);
})
var circle = svg.append("g").selectAll("circle")
.data(force.nodes())
.enter().append("circle")
.attr("r", 8)
.attr('class', 'circle')
.attr('fill', function(d, i) {
return colors(i);
})
.call(force.drag);
var text = svg.append("g").selectAll("text")
.data(force.nodes())
.enter().append("text")
.attr("x", 14)
.attr("y", ".31em")
.text(function(d) {
return d.name;
});
function tick() {
path.attr({
x1: function(d) {
return d.source.x;
},
y1: function(d) {
return d.source.y;
},
x2: function(d) {
return d.target.x;
},
y2: function(d) {
return d.target.y;
}
});
circle.attr("transform", transform);
text.attr("transform", transform);
}
function transform(d) {
return "translate(" + d.x + "," + d.y + ")";
}
function resizeGraph() {
var minX = d3.min(circle.data(), function(d) {
return d.x
});
var minY = d3.min(circle.data(), function(d) {
return d.y
});
var maxX = d3.max(circle.data(), function(d) {
return d.x
});
var maxY = d3.max(circle.data(), function(d) {
return d.y
});
svg.append("rect")
.attr("x", minX - 8)
.attr("y", minY - 8)
.attr("width", maxX - minX + 16)
.attr("height", maxY - minY + 16)
.style("stroke", "red")
.style("fill", "none");
};
</script>
答案 1 :(得分:1)
与Gerardo的answer相反,我实际上喜欢您使用边界框的方法。您可以轻松地保留对包含圆圈的<g>
的引用,并从中获取边界框。由于组的大小和位置由其内容固有地确定,因此其边界框也是其中所有圆的边界框。
实现非常简单:
var circles = svg.append("g"); // Keep the reference to the enclosing g
var circle = circles.selectAll("circle") // Append to that group
从那里开始,您可以通过以下操作获得所有圈子的总体范围:
var bbox = circles.node().getBBox();
借用Gerardo的实现并插入上面的代码将导致以下工作演示。
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.link {
fill: none;
stroke-width: 1.5px;
}
circle {
stroke: black;
stroke-width: 1.5px;
}
text {
font: 10px sans-serif;
pointer-events: none;
}
#resizebutton {
position: absolute;
top: 10px;
left: 20px;
height: 100px;
width: 100px;
}
#graph {
position: absolute;
top: 30px;
left: 100px;
}
</style>
<body>
<div id="root">
<div id="resizebutton">
<button onclick="resizeGraph()">Get size</button>
</div>
<div id="graph" height="400px" width="400px">
</div>
</div>
</body>
<script src="https://d3js.org/d3.v3.min.js"></script>
<script>
var nodes = [{
name: "node1"
},
{
name: "node2"
},
{
name: "node3"
},
{
name: "node4"
},
{
name: "node5"
},
{
name: "node6"
},
{
name: "node7"
},
{
name: "node8"
},
{
name: "node9"
}
];
var links = [{
source: 0,
target: 8
},
{
source: 1,
target: 8
},
{
source: 2,
target: 8
},
{
source: 3,
target: 8
},
{
source: 4,
target: 8
},
{
source: 5,
target: 8
},
{
source: 6,
target: 8
},
{
source: 7,
target: 8
}
];
var width = 400,
height = 400;
var force = d3.layout.force()
.nodes(nodes)
.links(links)
.size([width, height])
.linkDistance(150)
.charge(-300)
.on("tick", tick)
.start();
var svg = d3.select("#graph").append("svg")
.attr("width", width)
.attr("height", height);
var colors = d3.scale.category10();
var path = svg.append("g").selectAll("path")
.data(force.links())
.enter().append("line")
.attr('class', 'link')
.attr('stroke', function(d, i) {
return colors(i);
})
var circles = svg.append("g");
var circle = circles.selectAll("circle")
.data(force.nodes())
.enter().append("circle")
.attr("r", 8)
.attr('class', 'circle')
.attr('fill', function(d, i) {
return colors(i);
})
.call(force.drag);
var text = svg.append("g").selectAll("text")
.data(force.nodes())
.enter().append("text")
.attr("x", 14)
.attr("y", ".31em")
.text(function(d) {
return d.name;
});
function tick() {
path.attr({
x1: function(d) {
return d.source.x;
},
y1: function(d) {
return d.source.y;
},
x2: function(d) {
return d.target.x;
},
y2: function(d) {
return d.target.y;
}
});
circle.attr("transform", transform);
text.attr("transform", transform);
}
function transform(d) {
return "translate(" + d.x + "," + d.y + ")";
}
function resizeGraph() {
var bbox = circles.node().getBBox();
var rect = svg.append("rect")
.style("stroke", "red")
.style("fill", "none")
.attr(bbox);
};
</script>
与Gerardo的方法相比,一个好处是,该解决方案将自动处理所有的计算,即,它将考虑圆的半径。如果圆的大小不同,而仅知道圆心还不够,这将很方便。
如果您还希望文本成为整体边框的一部分,只需将<g>
环绕在圆圈和上,然后使用该外部组即可。