2016-06-29 11 views
0

Dies ist meine erste Frage zum Stacküberlauf. Bitte ertragen Sie mich, wenn ich Fehler mache. Ich habe hier viele Fragen gesucht und nicht genau das gefunden, wonach ich gesucht habe (in einem Fall habe ich es, weiß aber nicht, wie ich es umsetzen soll). Und es scheint, dass die einzigen Leute, die ähnliche Fragen gestellt haben, keine Antworten erhalten haben.D3 Layout erzwingen - Textumbruch und Knotenüberlappungen

Ich habe ein Kraft-Layout mit D3 erstellt und die Dinge funktionieren fast so, wie ich es möchte. Zwei Dinge, für die ich Probleme habe zu bearbeiten:

1) Knoten von der Überlappung fernhalten: Ja, ich habe Mike Bostocks Code für geclusterte Force-Layouts gelesen und erneut gelesen. Ich weiß nicht, wie ich das in meinen Code implementieren kann, ohne dass irgendwas schief geht! Ich habe versucht, diesen Code aus einem Tutorial, aber es fixiert meine Knoten in einer Ecke und gespreizte die Links auf der ganzen Leinwand:

var padding = 1, // separation between circles 
    radius=8; 
function collide(alpha) { 
var quadtree = d3.geom.quadtree(graph.nodes); 
    return function(d) { 
var rb = 2*radius + padding, 
    nx1 = d.x - rb, 
    nx2 = d.x + rb, 
    ny1 = d.y - rb, 
    ny2 = d.y + rb; 
quadtree.visit(function(quad, x1, y1, x2, y2) { 
    if (quad.point && (quad.point !== d)) { 
    var x = d.x - quad.point.x, 
     y = d.y - quad.point.y, 
     l = Math.sqrt(x * x + y * y); 
     if (l < rb) { 
     l = (l - rb)/l * alpha; 
     d.x -= x *= l; 
     d.y -= y *= l; 
     quad.point.x += x; 
     quad.point.y += y; 
    } 
    } 
    return x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1; 
}); 
}; 
} 

Sie können die zusätzlich zu der tick-Funktion in meiner Geige sehen (unten verlinkt) auf Kommentar .

2) Wickeln Sie meine Textbeschriftungen so ein, dass sie in die Knoten passen. Im Moment werden sie über den Hover auf den vollen Namen des Knotens erweitert, aber ich werde das irgendwann in einen Tooltip umwandeln (sobald ich diese Knicke herausgefunden habe, werde ich einen Tooltip herausfinden) - im Moment will ich nur die ursprünglichen, kurzen Namen wrap innerhalb der Knoten. Ich habe this answer und this answer (http://jsfiddle.net/Tmj7g/4/) angeschaut, aber wenn ich versuche, dies in meinen eigenen Code zu implementieren, reagiert es nicht oder landet Clustering alle Knoten in der oberen linken Ecke (??).

ist Jedwedes Eingang sehr geschätzt, und fühlt sich hier frei, meine Geige zu bearbeiten: https://jsfiddle.net/lilyelle/496c2bmr/

Ich weiß auch, dass alle meine Sprache nicht ganz konsistent ist oder der einfachste Weg, um den D3 Code zu schreiben - das ist, weil Ich habe viele Dinge aus verschiedenen Quellen kopiert und zusammengefügt und versuche immer noch herauszufinden, wie ich das am besten selbst schreiben kann. Jeder Rat in dieser Hinsicht wird ebenfalls geschätzt.

Antwort

0

1) Kollisionserkennung: Hier ist eine aktualisierte, Arbeits jsFiddle, die von this example von mbostock geführt wurde. Das Hinzufügen einer Kollisionserkennung war größtenteils ein Kopieren/Einfügen der wichtigen Bits. Insbesondere wird in der tick Funktion, habe ich den Code, der über all diesen Knoten Loops und zwickt ihre Positionen, wenn sie kollidieren:

var q = d3.geom.quadtree(nodes), 
    i = 0, 
    n = nodes.length; 

while (++i < n) q.visit(collide(nodes[i])); 

Da Ihr jsFiddle keine Variable nodes Satz hatte ich hinzugefügt direkt über die letzter snipped:

var nodes = force.nodes() 

auch diese Schleife die Funktion erfordert collide definiert werden, so wie es in Bostock Vorbild ist, so dass ich fügte hinzu, dass auf Ihre jsFiddle auch:

function collide(node) { 
    var r = node.radius + 16, 
     nx1 = node.x - r, 
     nx2 = node.x + r, 
     ny1 = node.y - r, 
     ny2 = node.y + r; 
    return function(quad, x1, y1, x2, y2) { 
    if (quad.point && (quad.point !== node)) { 
     var x = node.x - quad.point.x, 
      y = node.y - quad.point.y, 
      l = Math.sqrt(x * x + y * y), 
      r = node.radius + quad.point.radius; 
     if (l < r) { 
     l = (l - r)/l * .5; 
     node.x -= x *= l; 
     node.y -= y *= l; 
     quad.point.x += x; 
     quad.point.y += y; 
     } 
    } 
    return x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1; 
    }; 
} 

Das letzte notwendige Bit kommt von der Tatsache, dass das Erkennen der Knoten-Kollision ihre Größe kennt. Bostocks Code greift auf node.radius innerhalb der collide Funktion oben zu. In Ihrem Beispiel werden die Radien der Knoten nicht festgelegt, so dass collide die Funktion node.radiusundefined ist. Eine Möglichkeit, diesen Radius festzulegen, besteht darin, ihn zu Ihrem json hinzuzufügen, z.

{radius: 30, "name":"AATF", "full_name":"African Agricultural Technology Foundation", "type":1} 

Wenn alle Knoten den gleichen Radius haben, das ist übertrieben.

Eine andere Möglichkeit besteht darin, die 2 Vorkommen von node.radius durch eine fest codierte Zahl wie 30 zu ersetzen.

wählte ich etwas zwischen diesen beiden Möglichkeiten zu tun: ein konstantes node.radius zu jedem Knoten zuweisen, indem Sie über die geladene json Looping:

json.nodes.forEach(function(node) { 
    node.radius = 30; 
}) 

Das es Arbeits für das Erhalten Kollisionserkennung ist. Ich habe einen Radius von 30 verwendet, da dies der Radius ist, den Sie zum Rendern dieser Knoten verwendet haben, wie in .attr("r", 30). Dadurch bleiben alle Knoten zusammengeballt - nicht überlappend, aber immer noch einander berührend. Sie können mit einem größeren Wert für node.radius experimentieren, um etwas Leerraum zwischen ihnen zu erhalten.

2) Textumhüllung: Das ist eine schwierige Frage. Es gibt keine einfache Möglichkeit, das SVG <text> in eine bestimmte Breite zu bringen. Nur reguläre HTML div/span kann dies automatisch tun, aber auch HTML-Elemente können nicht um einen Kreis zu passen, nur um eine konstante Breite.

Sie könnten in der Lage sein, einen Kompromiss zu finden, mit dem Sie immer etwas Text anpassen können. Wenn Ihre Daten beispielsweise alle im Voraus bekannt sind und die Größe der Kreise immer den gleichen festen Wert hat, wissen Sie im Voraus, welche Beschriftungen passen und welche nicht. Diejenigen, die das nicht tun, können Sie entweder kürzen, indem Sie jedem Knoten in Ihrem JSON ein short_name Attribut hinzufügen und es auf etwas einstellen, das definitiv passt. Alternativ können Sie, wenn die Größe und die Beschriftungen im Voraus bekannt sind, vorab festlegen, wie die Beschriftungen in mehrere Zeilen aufgeteilt und in Ihrem JSON fest codiert werden. Wenn Sie dann rendern, können Sie diesen Text mit mehreren SVG <text> Elementen rendern, die Sie manuell als mehrere Zeilen positionieren. Schließlich, wenn nichts vorher bekannt ist, dann können Sie vielleicht eine gute Lösung erhalten, indem Sie den Text als absolut positionierte divs über (und außerhalb von) dem SVG darstellen, mit einer Breite, die den Breiten der Kreise entspricht , sodass der Text automatisch umbrochen wird.

+0

Oh. Meine. Güte. DANKE für diese fantastische Antwort und für die Erklärungen! Es macht jetzt viel mehr Sinn. Ich verstehe, was Sie mit dem Textumbruch meinen - ich denke, es ist etwas, das ich nur manuell anpassen muss. Jetzt, wo die Kollisionserkennung funktioniert, wird es einfacher. – lilyelle

+0

@lilyelle, super - gerne helfen! Eine Sache: Wenn meine Antwort Ihr Problem löst, klicken Sie bitte auf das Häkchen-Symbol, um es zu akzeptieren. StackOverflow läuft auf Punkten, und indem Sie meine Antwort akzeptieren, bekomme ich einen Punkt dafür :) – meetamit