My JsFiddle uses the excllent Dagre to dynamcially create a network graph. Full code below, to satisy posting requirements.
My problem is that as I add nodes, I cannot get it to auto-zoom to fit the nodes onto the (SVG canvas of) the graph.
Add a few nodes, and ...
How can I auto-zoom to fit after adding a new node?
Hmmmm, after a certain level of zomming, it might become difficult to read, so I suppose that I ought to limit zooming after a certain level & leave it to user scrolling, with not all of the graph visible at all Times(?).
If only Dagre handled this (and auto-layout (I would strongly prefer to centre one node, lock it in the centre and have the other nodes appear around it - I don't care about ordering, but some form of weighting, whilw not strictly necessary, wouldn't hurt)). Thinks, maybe I am using the wrong library, but searching, and even a quarion on S/W reccomendations didn't help, and Dagre is otherwise excellent.
Anyhoo, I digress. And idea how to get it to auto-zoom?
Here's the HTML:
<!doctype html>
<headd>
<meta charset="utf-8">
<title>Dynamic social network</title>
<link rel="stylesheet" href="style.css">
<script src="https://d3js.org/d3.v5.min.js" charset="utf-8"></script>
<script src="https://dagrejs.github.io/project/dagre-d3/latest/dagre-d3.js"></script>
</head>
<body>
<!-- based loosely on https://dagrejs.github.io/project/dagre-d3/latest/demo/clusters.html -->
<!-- docs at https://npmdoc.github.io/node-npmdoc-dagre/build/apidoc.html-->
<h1>Social network demo</h1>
<style id="css">
.clusters rect {
fill: whitesmoke;
stroke: #999;
stroke-width: 1.5px;
margin: auto;
}
text {
font-weight: 300;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serf;
font-size: 14px;
}
.node rect {
stroke: #999;
fill: #fff;
stroke-width: 1.5px;
}
.edgePath path {
stroke: #333;
stroke-width: 1.5px;
}
</style>
<a href="javascript:AddGroup()">Add a group</a>
<span> </span>
<a href="javascript:AddPerson()">Add a person (to a random group, at a random level)</a>
<br>
<br>
<svg id="svg-canvas" width=600 height=6000></svg>
<script id="js">
// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
let forenames = [];
forenames.push("Michael");
forenames.push("David");
forenames.push("John");
forenames.push("James");
forenames.push("Robert");
forenames.push("Mark");
forenames.push("William");
forenames.push("Richard");
forenames.push("Thomas");
forenames.push("Jeffrey");
forenames.push("Steven");
forenames.push("Joseph");
forenames.push("Timothy");
forenames.push("Kevin");
forenames.push("Scott");
forenames.push("Brian");
forenames.push("Charles");
forenames.push("Paul");
forenames.push("Daniel");
forenames.push("Christopher");
forenames.push("Anthony");
forenames.push("Kenneth");
forenames.push("Gregory");
forenames.push("Ronald");
forenames.push("Donald");
forenames.push("Lisa");
forenames.push("Mary");
forenames.push("Susan");
forenames.push("Karen");
forenames.push("Margaret");
forenames.push("Patricia");
forenames.push("Linda");
forenames.push("Donna");
forenames.push("Michelle");
forenames.push("Cynthia");
forenames.push("Sandra");
forenames.push("Deborah");
forenames.push("Tammy");
forenames.push("Pamela");
forenames.push("Christine");
forenames.push("Laura");
forenames.push("Elizabeth");
forenames.push("Julie");
forenames.push("Brenda");
forenames.push("Jennifer");
forenames.push("Barbara");
forenames.push("Angela");
forenames.push("Sharon");
forenames.push("Debra");
forenames.push("Teresa");
// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
let surnames = [];
surnames.push("Smith");
surnames.push("Jones");
surnames.push("Williams");
surnames.push("Taylor");
surnames.push("Brown");
surnames.push("Davies");
surnames.push("Evans");
surnames.push("Wilson");
surnames.push("Thomas");
surnames.push("Johnson");
surnames.push("Roberts");
surnames.push("Robinson");
surnames.push("Thompson");
surnames.push("Wright");
surnames.push("Walker");
surnames.push("White");
surnames.push("Edwards");
surnames.push("Hughes");
surnames.push("Green");
surnames.push("Hall");
surnames.push("Lewis");
surnames.push("Harris");
surnames.push("Clarke");
surnames.push("Jackson");
surnames.push("Wood");
surnames.push("Turner");
surnames.push("Martin");
surnames.push("Cooper");
surnames.push("Hill");
surnames.push("Ward");
const initalNodeName = 'Hiro protaganist';
let groupNumber = 0;
let lastAddedGroupName = '';
// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
function RenderGraph()
{
// Round the corners of the nodes
g.nodes().forEach(function(v) {
let node = g.node(v);
node.rx = node.ry = 5;
});
// Run the renderer. This is what draws the final graph.
render(d3.select("svg g"), g);
// Set up an SVG group so that we can translate the final graph.
let svg = d3.select("svg"),
svgGroup = svg.append("g");
// Center the graph
let xCenterOffset = (svg.attr("width") - g.graph().width) / 2;
svgGroup.attr("transform", "translate(" + xCenterOffset + ", 20)");
svg.attr("height", g.graph().height + 40);
let selections = inner.selectAll("g.node");
selections
.on('mouseover', function(d) {
console.log('mouseover ' + d);
})
// .on('mouseout', function(d) {
// console.log('mouseout ' + d);
// })
// .on('mousedown', function(d) {
// console.log('mousedown ' + d);
// })
// .on('mouseup', function(d) {
// console.log('mouseup ' + d);
// })
.on('click', function (d) {
alert('You clicked "' + d + '"');
});
}
// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
function AddGroup()
{
let groupName = 'Group_' + ++groupNumber;
lastAddedGroupName = groupName;
console.log('Added ' + groupName);
g.setNode(groupName, { label: groupName, clusterLabelPos: 'bottom' });
RenderGraph();
}
// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
function AddPerson()
{
let numChildrenInGroup = 0;
if (groupNumber === 0) // No groups, so create one, to add the new person to
{
AddGroup();
}
else
{
numChildrenInGroup = g.children(lastAddedGroupName).length;
// 1 chance in 4 to add a new group & switch to that
if ((Math.floor(Math.random() * 4) + 1) === 1)
{
AddGroup();
numChildrenInGroup = g.children(lastAddedGroupName).length;
}
else
{
if (numChildrenInGroup == 4)
{
AddGroup();
numChildrenInGroup = 0;
}
}
}
let forname = forenames[Math.floor(Math.random() * forenames.length)];
let suname = surnames[Math.floor(Math.random() * surnames.length)];
let name = forname + ' ' + suname;
let level = 1; // FixMe:
if (numChildrenInGroup > 0)
{
level = Math.floor(Math.random() * 2) + 1;
}
let borderColour = 'green';
if (level === 2)
borderColour = 'blue';
g.setNode(name, {label: name, style: 'stroke: ' + borderColour, level: level});
g.setParent(name, lastAddedGroupName);
g.setEdge(initalNodeName, name, { arrowhead: 'undirected', label: level} );
console.log('Added ' + name);
RenderGraph();
} // AddPerson()
// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
// Create the input graph
let g = new dagreD3.graphlib.Graph({compound:true})
.setGraph({})
.setDefaultEdgeLabel(function() { return {}; });
// Here we're setting the initial, central node
g.setNode(initalNodeName, {label: initalNodeName, style: 'stroke: red', level: 2});
// Create the renderer
let render = new dagreD3.render();
// // Set up an SVG group so that we can translate the final graph.
let svg = d3.select("svg"),
inner = svg.append("g");
// ToDo: Look at https://github.com/dagrejs/dagre-d3/issues/144
// // Set up zoom support
// let zoom = d3.behavior.zoom().on("zoom", function() {
// inner.attr("transform", "translate(" + d3.event.translate + ")" +
// "scale(" + d3.event.scale + ")");
// });
// svg.call(zoom);
RenderGraph();
</script>
</body>
</html>
and here's the CSS:
body {
width: 960px;
margin: 0 auto;
color: #333;
font-weight: 300;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serf;
}
h1 {
font-size: 3em;
font-weight: 300;
}
h2 {
font-size: 1.5em;
font-weight: 300;
}
section {
margin-bottom: 3em;
}
section p {
text-align: justify;
}
svg {
border: 1px solid #ccc;
overflow: hidden;
margin: 0 auto;
}
pre {
border: 1px solid #ccc;
}
question from:
https://stackoverflow.com/questions/65869171/how-can-i-auto-zoom-my-dagre-graph-after-dynamically-adding-a-node