Bringing a d3-force network into the viewport
One of the issues I’ve always had with node-link diagrams in D3 is that nodes tend to migrate out of the viewpoint, forcing you to zoom and pan to get the whole graph into view.
I finally hacked together a piece of code to solve that. Even brushing still works.
const rescale_to_viewport = () => {
let extentX = extent(nodes.map((d) => transform.applyX(d.x)))
let extentY = extent(nodes.map((d) => transform.applyY(d.y)))
let scaleX = (extentX[1]-extentX[0])/(0.9*width)
let scaleY = (extentY[1]-extentY[0])/(0.9*height)
let scale = max([scaleX,scaleY])
transform.k /= scale
simulationUpdate()
extentX = extent(nodes.map((d) => transform.applyX(d.x)))
extentY = extent(nodes.map((d) => transform.applyY(d.y)))
let moveX = ((extentX[1]+extentX[0])/2) - (width/2)
let moveY = ((extentY[1]+extentY[0])/2) - (height/2)
transform.x -= moveX
transform.y -= moveY
simulationUpdate()
zoomScale = transform.k;
panX = transform.x;
panY = transform.y;
}
The zoomScale
, panX
and panY
are necessary to keep brushing working as it should. My brushed
function:
function brushed(event) {
if (event.selection) {
brush_active = true;
let [[x0, y0], [x1, y1]] = event.selection;
let selectedNodes = nodes.filter((d) => {
let x = d.x * zoomScale + panX;
let y = d.y * zoomScale + panY;
return x >= x0 && x < x1 && y >= y0 && y < y1;
});
$selected = selectedNodes.map((d) => d.id);
}
}