2013-05-01 10 views
6

Ich habe Lösungen zum Erstellen mehrerer Force-Layouts auf einer einzelnen Seite gesehen, wobei jedes Layout in einer eigenen SVG enthalten ist. Ich konnte jedoch keine Hilfe finden, wie mehrere Force-Layouts in ein einzelnes SVG integriert werden können. Jedem Layout sind eigene Daten zugeordnet.D3: Mehrere Force-Layouts in einem SVG?

Ein Beispiel für das, was ich gerade mache, finden Sie unter http://jsfiddle.net/connorgr/SRAJa/. Ich habe den wichtigsten Teil des Codes unten eingefügt. Das Endergebnis sieht sehr ähnlich aus wie das Force-Layout wurde nie für alle außer den letzten Knoten/Link-Daten aktiviert (oder gelöscht). Gibt es eine Möglichkeit, dies zu verhindern?

Ich bin nicht in der Lage, die Daten zusammenzuführen und nur ein Layout wegen des Anwendungsfalls für die Visualisierung zu verwenden, die ich erstelle.

/** 
    * Creates a force layout in svgObj for each element in graphs 
    * (svg) svgObj - The SVG to include the force layouts in 
    * (json) graphs - An array of {"nodes":[...],"links":[...]} objects 
    */ 
function generateMultiForce(svgObj, graphs) { 
    for(var i=0; i < graphs.length; i++) { 
    var graph = graphs[i]; 

    var graphArea = svgObj.append("g"); 

    var force = d3.layout.force() 
     .charge(-200) 
     .linkDistance(45) 
     .size([width, height]) 
     .nodes(graph.nodes) 
     .links(graph.links) 
     .start(); 

    var link = graphArea.selectAll(".link") 
     .data(graph.links) 
     .enter().append("line") 
      .attr("class", "link"); 

    var nodeGroup = graphArea.selectAll("g") 
     .data(graph.nodes) 
     .enter().append("g") 
     .call(force.drag); 

    var node = nodeGroup.append("circle") 
     .attr("class", "node") 
     .attr("r", 5) 
     .style("fill", function(d) { 
      return color(d.group); }); 

    var text = nodeGroup.append("text") 
     .attr("x", 8) 
     .attr("y", ".31em") 
     .text(function(d) { return d.name; }); 

    force.on("tick", function() { 
     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; }); 

     nodeGroup.attr("transform", function(d) { 
      return "translate("+d.x+","+d.y+")"; 
     }); 
    }); 
    } 
} 
+0

Es sieht für mich so aus, als wäre das 'tick'-Ereignis (in gewissem Sinne) global. Das heißt, Sie können nur einen Handler für ein "tick" -Ereignis haben, sodass der letzte, den Sie installieren, aufgerufen wird. –

+0

Ein kurzer Tauchgang in den D3-Quellcode, und das scheint zu sein. Bummel. –

Antwort

2

Sie können die generateMultiForce Funktion als jQuery-Plugin schreiben - das scheint die Graphen unabhängig zu halten und Kraft Layout für beide gelten:

var width = 600, 
    height = 600; 

var color = d3.scale.category20(); 

var data = [ 
    { 
    "nodes": [ 
     {"name": "Hello"}, 
     {"name": "World"} 
    ], 
    "links": [ 
     {"source": 0, "target": 1, "value": 0.5} 
    ] 
    }, 
    { 
    "nodes": [ 
     {"name": "Zero"}, 
     {"name": "One"}, 
     {"name": "Two"} 
    ], 
    "links": [ 
     {"source": 0, "target": 1, "value": 0.25}, 
     {"source": 1, "target": 2, "value": 0.5}, 
     {"source": 2, "target": 0, "value": 0.25} 
    ] 
    } 
]; 

(function($) { 
    $.fn.generateMultiForce = function(svgObj) { 

     return this.each(function() { 
      var graph = this; 
      var graphArea = svgObj.append("g"); 

      var force = d3.layout.force() 
       .charge(-200) 
       .linkDistance(45) 
       .size([width, height]) 
       .nodes(graph.nodes) 
       .links(graph.links) 
       .start(); 

      var link = graphArea.selectAll(".link") 
       .data(graph.links) 
       .enter().append("line") 
       .attr("class", "link"); 

      var nodeGroup = graphArea.selectAll("g") 
       .data(graph.nodes) 
       .enter().append("g") 
       .call(force.drag); 

      var node = nodeGroup.append("circle") 
       .attr("class", "node") 
       .attr("r", 5) 
       .style("fill", function(d) { 
        return color(d.group); }); 

      var text = nodeGroup.append("text") 
       .attr("x", 8) 
       .attr("y", ".31em") 
       .text(function(d) { return d.name; }); 

      force.on("tick", function() { 
       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; }); 

       nodeGroup.attr("transform", function(d) { 
        return "translate("+d.x+","+d.y+")"; 
       }); 
      }); 
     }); 
    }; 
})(jQuery); 

var svgTest = d3.select("body").append("svg") 
    .attr("width", width) 
    .attr("height", height); 

$(data).generateMultiForce(svgTest); 
6

Der folgende Ansatz schränkt Kraft, Knoten und Verbindungsdaten zu ihren entsprechenden kraftgesteuerten Layouts. Auf diese Weise können Sie beliebig viele Layouts demselben SVG hinzufügen, ohne Interferenz zwischen den Knoten zu verursachen. Jedes Layout kann individuell formatiert werden. Wenn Sie möchten, dass sich die Layouts gegenseitig beeinflussen, können Sie ihre jeweiligen Tick-Funktionen bearbeiten.

function layout1(inputNodes, inputLinks) { 
    var force = d3.layout.force(); 
    var nodes = force.nodes(); 
    var links = force.links(); 

    var update = function() { 
     //append nodes and links from data 

     force.on("tick",function(e){ 
     //tick movement 

     } 
    } 
    for(var i=0; i<inputNodes.length; i++){ 
     nodes.push(inputNodes[i]); 
    } 
    for(var i=0; i<inputLinks.length; i++){ 
     links.push(inputLinks[i]); 
    } 
    update(); 
} 

Nun ist die zweite Kraft-directed-Layout kann eine identische Struktur aufweisen und die gleichen Variablennamen sowie:

function layout2(inputNodes, inputLinks) { 
    var force = d3.layout.force(); 
    var nodes = force.nodes(); 
    var links = force.links(); 

    var update = function() { 
     //append nodes and links from data 

     force.on("tick",function(e){ 
     //tick movement 

     } 
    } 
    for(var i=0; i<inputNodes.length; i++){ 
     nodes.push(inputNodes[i]); 
    } 
    for(var i=0; i<inputLinks.length; i++){ 
     links.push(inputLinks[i]); 
    } 
    update(); 
} 

schließlich mit welchem ​​Daten instantiieren:

var layout1 = new layout1(inputNodes, inputLinks); 
var layout2 = new layout2(inputNodes, inputLinks); 

Diese Methode kann zur Erstellung mehrerer Layouts im laufenden Betrieb verwendet werden. Hoffe das ist nah dran was du suchst.