Prima domanda su Stack Overflow, quindi portami con me! Sono nuovo di d3.js, ma sono stato costantemente stupito da ciò che gli altri sono in grado di realizzare con esso ... e quasi sorpreso da quanto poco ho potuto fare con me stesso! Chiaramente non sto facendo qualcosa, quindi spero che le anime gentili qui possano mostrarmi la luce.Aggiunta di nuovi nodi al layout Force-direct
La mia intenzione è quella di fare una funzione javascript che fa semplicemente il seguente riutilizzabile:
- Crea un grafico forza-diretto in bianco in un elemento DOM specificato
- Consente di aggiungere ed eliminare l'etichetta, immagine- cuscinetto nodi a tale grafico, specificando connessioni tra loro
ho preso http://bl.ocks.org/950642 come punto di partenza, dato che è essenzialmente il tipo di configurazione voglio essere in grado di creare:
Ecco ciò che il mio codice è simile:
<!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>
Ogni volta aggiungo un nuovo nodo, ri-label tutti i nodi esistenti; questi si accumulano l'uno sopra l'altro e le cose cominciano a diventare brutte. Capisco perché questo è: perché quando chiamo la funzione funzione update()
all'aggiunta di un nuovo nodo, fa un node.append(...)
all'intero set di dati. Non riesco a capire come fare questo per solo il nodo che sto aggiungendo ... e posso solo apparentemente usare node.enter()
per creare un singolo nuovo elemento, in modo che non funzioni per gli elementi aggiuntivi che ho bisogno di rilegare al nodo. Come posso risolvere questo?
Grazie per tutte le indicazioni che puoi fornire in merito a questo problema!
cura perché ho risolto rapidamente una fonte di molti altri bug che sono stati precedentemente menzionati
Sì, signore. Btw, questa è davvero una buona risposta! – Maziyar
L'uso di 'force.start()' invece di 'force.resume()' quando si aggiungono nuovi dati è la chiave. Molte grazie! – Mouagip
Questo è fantastico. Stai calmo se ha scalato automaticamente il livello di zoom (magari riducendo la carica finché tutto si adatta?), Quindi tutto si è adattato alle dimensioni della scatola che stava disegnando. –