强制网络与 d3 匹配边界框
Force network to fit bounding box with d3
我正在尝试将我的网络放入 div。我使用了这个例子:https://bl.ocks.org/puzzler10/2531c035e8d514f125c4d15433f79d74 但在使用 d3.json()
时无法让它工作
这是我正在使用的代码。
<!DOCTYPE html>
<meta charset="utf-8">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<style>
html, body {
height: 100%;
margin: 0;
}
.links line {
stroke: #999;
stroke-opacity: 0.6;
}
.nodes circle {
stroke: #fff;
stroke-width: 1.5px;
}
text {
font-family: sans-serif;
font-size: 10px;
color: black;
}
.sidebar {
height: 100%;
width: 0;
margin-top:10%;
margin-bottom:1.5%;
margin-left:1.5%;
position: absolute;
z-index: 2000;
top: 0;
left: 0;
bottom:0;
background-color:rgba(240,240,240,0.6);
overflow-x: hidden;
transition: 0.5s;
border-radius:5px;
overflow: hidden;
}
.sidebar:hover {
overflow-y: scroll;
}
svg{
border: 1px solid black;
}
.container {
display: flex;
justify-content: center
};
</style>
<div class="container">
<svg width="500" height="500"></svg>
</div>
<div id="mySidebar" class="sidebar">
<p id="area"></p>
<p id="desc"></p>
<div>
<img id="media" src="">
</div>
<p id="content"></p>
</div>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
//Navbar navigation
function openNav() {
document.getElementById("mySidebar").style.width = "350px";
}
function closeNav() {
document.getElementById("mySidebar").style.width = "0";
}
//Graph options
var radius = 15;
var svg = d3.select("div#container")
.append("svg")
.attr("preserveAspectRatio", "xMinYMin meet")
.attr("viewBox", "0 0 300 300")
.classed("svg-content", true);
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height");
var color = d3.scaleOrdinal(d3.schemeCategory20);
var simulation = d3.forceSimulation()
.force("link", d3.forceLink().id(function(d) { return d.id; }))
.force("charge", d3.forceManyBody().strength(-2000))
.force("center", d3.forceCenter(width / 2, height / 2));
//Graph creation
d3.json("http://myjson.dit.upm.es/api/bins/10fx", function(error, graph) {
if (error) throw error;
var link = svg.append("g")
.attr("class", "links")
.selectAll("line")
.data(graph.links)
.enter().append("line")
.attr("stroke-width", function(d) { return Math.sqrt(d.value); });
var node = svg.append("g")
.attr("class", "nodes")
.selectAll("g")
.data(graph.nodes)
.enter().append("g")
var circles = node.append("circle")
.attr("r", 5)
.attr("fill", function(d) { return color(d.group); });
// Create a drag handler and append it to the node object instead
var drag_handler = d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended);
drag_handler(node);
var labels = node.append("text")
.text(function(d) {
return d.id;
})
.attr('x', 6)
.attr('y', 3);
link.on("click",
function(d) {
console.log(d.content);
openNav();
document.getElementById("content").innerHTML = d.content;
},
)
node.append("title")
.text(function(d) { return d.id; });
simulation
.nodes(graph.nodes)
.on("tick", ticked);
simulation.force("link")
.links(graph.links);
function ticked() {
link
.attr("x1", function(d) { return d.source.x; })
.attr("y1", function(d) { return d.source.y; })
.attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; });
node //This is where the "problem" seems to be located
.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
})
}
});
function dragstarted(d) {
if (!d3.event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}
function dragged(d) {
d.fx = d3.event.x;
d.fy = d3.event.y;
}
function dragended(d) {
if (!d3.event.active) simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
}
</script>
可以将网络约束到边界框的函数如下:
function ticked() {
link
.attr("x1", function(d) { return d.source.x; })
.attr("y1", function(d) { return d.source.y; })
.attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; });
node //This is where the "problem" seems to be located
.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
})
}
我已经尝试了很多东西,但无法让它发挥作用。有什么想法吗?
您共享的块中将节点保持在边界内的部分是
node
.attr("cx", function(d) { return d.x = Math.max(radius, Math.min(width - radius, d.x)); })
.attr("cy", function(d) { return d.y = Math.max(radius, Math.min(height - radius, d.y)); });
注意对 Math.max
和 Math.min
的调用,以确保 d.x
和 d.y
保持在边界内。您的代码缺少此内容。
你可以做类似的事情:
node
.attr("transform", function(d) {
d.x = Math.max(radius, Math.min(width - radius, d.x));
d.y = Math.max(radius, Math.min(height - radius, d.y));
return "translate(" + d.x + "," + d.y + ")";
});
这是一个更新的例子:
<!DOCTYPE html>
<meta charset="utf-8">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<style>
html, body {
height: 100%;
margin: 0;
}
.links line {
stroke: #999;
stroke-opacity: 0.6;
}
.nodes circle {
stroke: #fff;
stroke-width: 1.5px;
}
text {
font-family: sans-serif;
font-size: 10px;
color: black;
}
.sidebar {
height: 100%;
width: 0;
margin-top:10%;
margin-bottom:1.5%;
margin-left:1.5%;
position: absolute;
z-index: 2000;
top: 0;
left: 0;
bottom:0;
background-color:rgba(240,240,240,0.6);
overflow-x: hidden;
transition: 0.5s;
border-radius:5px;
overflow: hidden;
}
.sidebar:hover {
overflow-y: scroll;
}
svg{
border: 1px solid black;
}
.container {
display: flex;
justify-content: center
};
</style>
<div class="container">
<svg width="500" height="500"></svg>
</div>
<div id="mySidebar" class="sidebar">
<p id="area"></p>
<p id="desc"></p>
<div>
<img id="media" src="">
</div>
<p id="content"></p>
</div>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
//Navbar navigation
function openNav() {
document.getElementById("mySidebar").style.width = "350px";
}
function closeNav() {
document.getElementById("mySidebar").style.width = "0";
}
//Graph options
var radius = 15;
var svg = d3.select("div#container")
.append("svg")
.attr("preserveAspectRatio", "xMinYMin meet")
.attr("viewBox", "0 0 300 300")
.classed("svg-content", true);
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height");
var color = d3.scaleOrdinal(d3.schemeCategory20);
var simulation = d3.forceSimulation()
.force("link", d3.forceLink().id(function(d) { return d.id; }))
.force("charge", d3.forceManyBody().strength(-2000))
.force("center", d3.forceCenter(width / 2, height / 2));
//Graph creation
d3.json("http://myjson.dit.upm.es/api/bins/10fx", function(error, graph) {
if (error) throw error;
var link = svg.append("g")
.attr("class", "links")
.selectAll("line")
.data(graph.links)
.enter().append("line")
.attr("stroke-width", function(d) { return Math.sqrt(d.value); });
var node = svg.append("g")
.attr("class", "nodes")
.selectAll("g")
.data(graph.nodes)
.enter().append("g")
var circles = node.append("circle")
.attr("r", 5)
.attr("fill", function(d) { return color(d.group); });
// Create a drag handler and append it to the node object instead
var drag_handler = d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended);
drag_handler(node);
var labels = node.append("text")
.text(function(d) {
return d.id;
})
.attr('x', 6)
.attr('y', 3);
link.on("click",
function(d) {
console.log(d.content);
openNav();
document.getElementById("content").innerHTML = d.content;
},
)
node.append("title")
.text(function(d) { return d.id; });
simulation
.nodes(graph.nodes)
.on("tick", ticked);
simulation.force("link")
.links(graph.links);
function ticked() {
node
.attr("transform", function(d) {
d.x = Math.max(radius, Math.min(width - radius, d.x));
d.y = Math.max(radius, Math.min(height - radius, d.y));
return "translate(" + d.x + "," + d.y + ")";
});
link
.attr("x1", function(d) { return d.source.x; })
.attr("y1", function(d) { return d.source.y; })
.attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; });
}
});
function dragstarted(d) {
if (!d3.event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}
function dragged(d) {
d.fx = d3.event.x;
d.fy = d3.event.y;
}
function dragended(d) {
if (!d3.event.active) simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
}
</script>
我正在尝试将我的网络放入 div。我使用了这个例子:https://bl.ocks.org/puzzler10/2531c035e8d514f125c4d15433f79d74 但在使用 d3.json()
这是我正在使用的代码。
<!DOCTYPE html>
<meta charset="utf-8">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<style>
html, body {
height: 100%;
margin: 0;
}
.links line {
stroke: #999;
stroke-opacity: 0.6;
}
.nodes circle {
stroke: #fff;
stroke-width: 1.5px;
}
text {
font-family: sans-serif;
font-size: 10px;
color: black;
}
.sidebar {
height: 100%;
width: 0;
margin-top:10%;
margin-bottom:1.5%;
margin-left:1.5%;
position: absolute;
z-index: 2000;
top: 0;
left: 0;
bottom:0;
background-color:rgba(240,240,240,0.6);
overflow-x: hidden;
transition: 0.5s;
border-radius:5px;
overflow: hidden;
}
.sidebar:hover {
overflow-y: scroll;
}
svg{
border: 1px solid black;
}
.container {
display: flex;
justify-content: center
};
</style>
<div class="container">
<svg width="500" height="500"></svg>
</div>
<div id="mySidebar" class="sidebar">
<p id="area"></p>
<p id="desc"></p>
<div>
<img id="media" src="">
</div>
<p id="content"></p>
</div>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
//Navbar navigation
function openNav() {
document.getElementById("mySidebar").style.width = "350px";
}
function closeNav() {
document.getElementById("mySidebar").style.width = "0";
}
//Graph options
var radius = 15;
var svg = d3.select("div#container")
.append("svg")
.attr("preserveAspectRatio", "xMinYMin meet")
.attr("viewBox", "0 0 300 300")
.classed("svg-content", true);
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height");
var color = d3.scaleOrdinal(d3.schemeCategory20);
var simulation = d3.forceSimulation()
.force("link", d3.forceLink().id(function(d) { return d.id; }))
.force("charge", d3.forceManyBody().strength(-2000))
.force("center", d3.forceCenter(width / 2, height / 2));
//Graph creation
d3.json("http://myjson.dit.upm.es/api/bins/10fx", function(error, graph) {
if (error) throw error;
var link = svg.append("g")
.attr("class", "links")
.selectAll("line")
.data(graph.links)
.enter().append("line")
.attr("stroke-width", function(d) { return Math.sqrt(d.value); });
var node = svg.append("g")
.attr("class", "nodes")
.selectAll("g")
.data(graph.nodes)
.enter().append("g")
var circles = node.append("circle")
.attr("r", 5)
.attr("fill", function(d) { return color(d.group); });
// Create a drag handler and append it to the node object instead
var drag_handler = d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended);
drag_handler(node);
var labels = node.append("text")
.text(function(d) {
return d.id;
})
.attr('x', 6)
.attr('y', 3);
link.on("click",
function(d) {
console.log(d.content);
openNav();
document.getElementById("content").innerHTML = d.content;
},
)
node.append("title")
.text(function(d) { return d.id; });
simulation
.nodes(graph.nodes)
.on("tick", ticked);
simulation.force("link")
.links(graph.links);
function ticked() {
link
.attr("x1", function(d) { return d.source.x; })
.attr("y1", function(d) { return d.source.y; })
.attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; });
node //This is where the "problem" seems to be located
.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
})
}
});
function dragstarted(d) {
if (!d3.event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}
function dragged(d) {
d.fx = d3.event.x;
d.fy = d3.event.y;
}
function dragended(d) {
if (!d3.event.active) simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
}
</script>
可以将网络约束到边界框的函数如下:
function ticked() {
link
.attr("x1", function(d) { return d.source.x; })
.attr("y1", function(d) { return d.source.y; })
.attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; });
node //This is where the "problem" seems to be located
.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
})
}
我已经尝试了很多东西,但无法让它发挥作用。有什么想法吗?
您共享的块中将节点保持在边界内的部分是
node
.attr("cx", function(d) { return d.x = Math.max(radius, Math.min(width - radius, d.x)); })
.attr("cy", function(d) { return d.y = Math.max(radius, Math.min(height - radius, d.y)); });
注意对 Math.max
和 Math.min
的调用,以确保 d.x
和 d.y
保持在边界内。您的代码缺少此内容。
你可以做类似的事情:
node
.attr("transform", function(d) {
d.x = Math.max(radius, Math.min(width - radius, d.x));
d.y = Math.max(radius, Math.min(height - radius, d.y));
return "translate(" + d.x + "," + d.y + ")";
});
这是一个更新的例子:
<!DOCTYPE html>
<meta charset="utf-8">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<style>
html, body {
height: 100%;
margin: 0;
}
.links line {
stroke: #999;
stroke-opacity: 0.6;
}
.nodes circle {
stroke: #fff;
stroke-width: 1.5px;
}
text {
font-family: sans-serif;
font-size: 10px;
color: black;
}
.sidebar {
height: 100%;
width: 0;
margin-top:10%;
margin-bottom:1.5%;
margin-left:1.5%;
position: absolute;
z-index: 2000;
top: 0;
left: 0;
bottom:0;
background-color:rgba(240,240,240,0.6);
overflow-x: hidden;
transition: 0.5s;
border-radius:5px;
overflow: hidden;
}
.sidebar:hover {
overflow-y: scroll;
}
svg{
border: 1px solid black;
}
.container {
display: flex;
justify-content: center
};
</style>
<div class="container">
<svg width="500" height="500"></svg>
</div>
<div id="mySidebar" class="sidebar">
<p id="area"></p>
<p id="desc"></p>
<div>
<img id="media" src="">
</div>
<p id="content"></p>
</div>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
//Navbar navigation
function openNav() {
document.getElementById("mySidebar").style.width = "350px";
}
function closeNav() {
document.getElementById("mySidebar").style.width = "0";
}
//Graph options
var radius = 15;
var svg = d3.select("div#container")
.append("svg")
.attr("preserveAspectRatio", "xMinYMin meet")
.attr("viewBox", "0 0 300 300")
.classed("svg-content", true);
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height");
var color = d3.scaleOrdinal(d3.schemeCategory20);
var simulation = d3.forceSimulation()
.force("link", d3.forceLink().id(function(d) { return d.id; }))
.force("charge", d3.forceManyBody().strength(-2000))
.force("center", d3.forceCenter(width / 2, height / 2));
//Graph creation
d3.json("http://myjson.dit.upm.es/api/bins/10fx", function(error, graph) {
if (error) throw error;
var link = svg.append("g")
.attr("class", "links")
.selectAll("line")
.data(graph.links)
.enter().append("line")
.attr("stroke-width", function(d) { return Math.sqrt(d.value); });
var node = svg.append("g")
.attr("class", "nodes")
.selectAll("g")
.data(graph.nodes)
.enter().append("g")
var circles = node.append("circle")
.attr("r", 5)
.attr("fill", function(d) { return color(d.group); });
// Create a drag handler and append it to the node object instead
var drag_handler = d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended);
drag_handler(node);
var labels = node.append("text")
.text(function(d) {
return d.id;
})
.attr('x', 6)
.attr('y', 3);
link.on("click",
function(d) {
console.log(d.content);
openNav();
document.getElementById("content").innerHTML = d.content;
},
)
node.append("title")
.text(function(d) { return d.id; });
simulation
.nodes(graph.nodes)
.on("tick", ticked);
simulation.force("link")
.links(graph.links);
function ticked() {
node
.attr("transform", function(d) {
d.x = Math.max(radius, Math.min(width - radius, d.x));
d.y = Math.max(radius, Math.min(height - radius, d.y));
return "translate(" + d.x + "," + d.y + ")";
});
link
.attr("x1", function(d) { return d.source.x; })
.attr("y1", function(d) { return d.source.y; })
.attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; });
}
});
function dragstarted(d) {
if (!d3.event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}
function dragged(d) {
d.fx = d3.event.x;
d.fy = d3.event.y;
}
function dragended(d) {
if (!d3.event.active) simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
}
</script>