Bewegen und Zoomen eines Force Directed Graph

Zu viele Nodes sprengen den Rahmen des SVG-Layouts. Dann ist hineinzoomen in den Graphen sinnvoll und auch das Bewegen des sichtbaren Ausschnitts. Mit den passenden D3-Methoden ist das einfach.

Zoom in den Graphen

Zunächst brauchen wir eine Variable und eine Methode für den Zoom sowie eine root-Gruppe, in der sich alle node- und link-Elemente befinden. Zoom und Bewegung werden nachher über die root-Gruppe ausgeführt. Jedes darin liegende Element wird entsprechend mit behandelt. Im Zoom wird das Bewegen mit implementiert.

// - - - - - - - - - -
// declaration
// - - - - - - - - - -
let
  ...
  root = undefined,
  zoom = undefined,
  ...
  setRoot = undefined,
  setZoom = undefined,
  ...;

Das Definieren des Zooms ist einfach. d3.zoom() wird konfiguriert und an eine SVG-Gruppe (root) gehängt. Es steuert das transform-Attribute über die Werte für translate() für das Bewegen und scale() für das hinein- oder herauszoomen. Auslöser ist das zoom-Ereignis, das (auf meinem MacBook) mit dem Touchpad ausgelöst wird – zoomen mit zwei Fingern und bewegen mit gedrücktem Pad und eben bewegen. (Mausrad und linke Taste gehen natürlich auch).

// - - - - - - - - - -
// functions
// - - - - - - - - - -
setZoom = function () {
  zoom = d3.zoom()
    .scaleExtent([1 / 4, 4])
    .on('zoom', function () {
      root.attr('transform',
        'translate(' + d3.event.transform.x + ',' + d3.event.transform.y + ')' +
        'scale(' + d3.event.transform.k + ')');
    });
}

Hinzufügen der setRoot-Funktion. Die node- und link-Gruppen werden für das Zooming in eine gemeinsame Gruppe mit der ID root verpackt.

setRoot = function (id) {
  root = svg.append('g')
    .attr('id', id);
};
...
// building the links
link = root.append("g")
  .attr("class", "links")
  .selectAll("line")
  ...

// building the nodes by circles
groupData = root.selectAll()
  .data(graph.nodes);

Bei Ausführen der einzelnen Methoden muss nun setZoom() zuerst gesetzt werden.

// - - - - - - - - - -
// control
// - - - - - - - - - -
setZoom();
setCanvas('#diagram'),
setRoot('#root');

Damit es am Ende funktioniert, muss das svg-Objekt die zoom-Methode aufrufen. Das geht über ein call(zoom) am Ende der Befehlskette.

setCanvas = function (contextSelector) {
  svg = d3.select(contextSelector)
    .append('svg')
    .attr('width', canvas.width)
    .attr('height', canvas.height)
    .attr('viewbox', canvas.viewbox.x + ' ' + canvas.viewbox.y + ' ' + canvas.viewbox.width + ' ' + canvas.viewbox.height)
    .call(zoom);
};