Erste Frage zum Stapelüberlauf, also nimm sie mit! Ich bin neu bei d3.js, war aber immer wieder erstaunt darüber, was andere damit erreichen können ... und fast genauso erstaunt darüber, wie wenig Fortschritte ich selbst damit machen konnte! Klar, ich mache mir nichts vor, also hoffe ich, dass die freundlichen Seelen hier mir das Licht zeigen können.
Meine Absicht ist es, eine wiederverwendbare Javascript-Funktion zu erstellen, die einfach Folgendes bewirkt:
- Erstellt ein leeres, kraftgerichtetes Diagramm in einem angegebenen DOM-Element
- Ermöglicht das Hinzufügen und Löschen von beschrifteten, bildtragenden Knoten zu diesem Diagramm unter Angabe von Verbindungen zwischen diesen
Ich habe http://bl.ocks.org/950642 als Ausgangspunkt genommen, da dies im Wesentlichen die Art von Layout ist, die ich erstellen möchte:
So sieht mein Code aus:
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="jquery.min.js"></script>
<script type="text/javascript" src="underscore-min.js"></script>
<script type="text/javascript" src="d3.v2.min.js"></script>
<style type="text/css">
.link { stroke: #ccc; }
.nodetext { pointer-events: none; font: 10px sans-serif; }
body { width:100%; height:100%; margin:none; padding:none; }
#graph { width:500px;height:500px; border:3px solid black;border-radius:12px; margin:auto; }
</style>
</head>
<body>
<div id="graph"></div>
</body>
<script type="text/javascript">
function myGraph(el) {
// Initialise the graph object
var graph = this.graph = {
"nodes":[{"name":"Cause"},{"name":"Effect"}],
"links":[{"source":0,"target":1}]
};
// Add and remove elements on the graph object
this.addNode = function (name) {
graph["nodes"].push({"name":name});
update();
}
this.removeNode = function (name) {
graph["nodes"] = _.filter(graph["nodes"], function(node) {return (node["name"] != name)});
graph["links"] = _.filter(graph["links"], function(link) {return ((link["source"]["name"] != name)&&(link["target"]["name"] != name))});
update();
}
var findNode = function (name) {
for (var i in graph["nodes"]) if (graph["nodes"][i]["name"] === name) return graph["nodes"][i];
}
this.addLink = function (source, target) {
graph["links"].push({"source":findNode(source),"target":findNode(target)});
update();
}
// set up the D3 visualisation in the specified element
var w = $(el).innerWidth(),
h = $(el).innerHeight();
var vis = d3.select(el).append("svg:svg")
.attr("width", w)
.attr("height", h);
var force = d3.layout.force()
.nodes(graph.nodes)
.links(graph.links)
.gravity(.05)
.distance(100)
.charge(-100)
.size([w, h]);
var update = function () {
var link = vis.selectAll("line.link")
.data(graph.links);
link.enter().insert("line")
.attr("class", "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; });
link.exit().remove();
var node = vis.selectAll("g.node")
.data(graph.nodes);
node.enter().append("g")
.attr("class", "node")
.call(force.drag);
node.append("image")
.attr("class", "circle")
.attr("xlink:href", "https://d3nwyuy0nl342s.cloudfront.net/images/icons/public.png")
.attr("x", "-8px")
.attr("y", "-8px")
.attr("width", "16px")
.attr("height", "16px");
node.append("text")
.attr("class", "nodetext")
.attr("dx", 12)
.attr("dy", ".35em")
.text(function(d) { return d.name });
node.exit().remove();
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; });
node.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
});
// Restart the force layout.
force
.nodes(graph.nodes)
.links(graph.links)
.start();
}
// Make it all go
update();
}
graph = new myGraph("#graph");
// These are the sort of commands I want to be able to give the object.
graph.addNode("A");
graph.addNode("B");
graph.addLink("A", "B");
</script>
</html>
Jedes Mal, wenn ich einen neuen Knoten hinzufüge, werden alle vorhandenen Knoten neu beschriftet. Diese stapeln sich übereinander und die Dinge beginnen hässlich zu werden. Ich verstehe, warum dies so ist: Wenn ich die update()
Funktionsfunktion beim Hinzufügen eines neuen Knotens aufrufe, wird dies node.append(...)
für den gesamten Datensatz ausgeführt. Ich kann nicht herausfinden, wie dies nur für den Knotennode.enter()
geschieht, den ich hinzufüge ... und ich kann anscheinend nur ein einzelnes neues Element erstellen, sodass dies für die zusätzlichen Elemente, die ich an den Knoten binden muss, nicht funktioniert . Wie kann ich das beheben?
Vielen Dank für jede Anleitung, die Sie zu diesem Thema geben können!
Bearbeitet, weil ich schnell eine Quelle für mehrere andere Fehler behoben habe, die zuvor erwähnt wurden
force.start()
anstelle desforce.resume()
Hinzufügens neuer Daten war der Schlüssel. Vielen Dank!