Pertanyaan pertama tentang Stack Overflow, jadi bersabarlah! Saya baru mengenal d3.js, tetapi secara konsisten kagum dengan apa yang orang lain dapat capai dengannya ... dan hampir sama kagumnya dengan betapa sedikit kemajuan yang dapat saya lakukan dengan itu sendiri! Jelas saya tidak menggerogoti sesuatu, jadi saya harap jiwa-jiwa yang baik hati di sini dapat menunjukkan terang kepada saya.
Tujuan saya adalah membuat fungsi javascript yang dapat digunakan kembali yang hanya melakukan hal berikut:
- Membuat grafik arah paksa kosong di elemen DOM yang ditentukan
- Memungkinkan Anda menambah dan menghapus node berlabel bantalan gambar ke grafik itu, menentukan hubungan di antara mereka
Saya telah menggunakan http://bl.ocks.org/950642 sebagai titik awal, karena pada dasarnya itulah jenis tata letak yang ingin saya buat:
Inilah tampilan kode saya:
<!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>
Setiap kali saya menambahkan node baru, itu memberi label ulang semua node yang ada; tumpukan ini di atas satu sama lain dan hal-hal mulai menjadi buruk. Saya mengerti mengapa ini terjadi: karena ketika saya memanggil update()
fungsi fungsi setelah menambahkan node baru, ia melakukan a node.append(...)
ke seluruh kumpulan data. Saya tidak tahu bagaimana melakukan ini hanya untuk node yang saya tambahkan ... dan saya hanya dapat menggunakan node.enter()
untuk membuat satu elemen baru, jadi itu tidak berfungsi untuk elemen tambahan yang perlu saya ikat ke node . Bagaimana cara memperbaikinya?
Terima kasih atas panduan yang dapat Anda berikan tentang masalah ini!
Diedit karena saya dengan cepat memperbaiki sumber dari beberapa bug lain yang telah disebutkan sebelumnya
sumber
force.start()
alih-alihforce.resume()
saat data baru ditambahkan adalah kuncinya. Terima kasih banyak!