Câu hỏi đầu tiên về Stack Overflow, vì vậy hãy chịu khó với tôi! Tôi mới làm quen với d3.js, nhưng đã luôn ngạc nhiên bởi những gì người khác có thể đạt được với nó ... và gần như ngạc nhiên trước cách mà tôi đã có thể tự mình làm được với nó! Rõ ràng là tôi không tìm kiếm thứ gì đó, vì vậy tôi hy vọng rằng những linh hồn tốt bụng ở đây có thể cho tôi thấy ánh sáng.
Ý định của tôi là tạo một hàm javascript có thể tái sử dụng, hàm này chỉ đơn giản thực hiện những việc sau:
- Tạo một biểu đồ hướng lực trống trong phần tử DOM được chỉ định
- Cho phép bạn thêm và xóa các nút mang hình ảnh, được gắn nhãn vào biểu đồ đó, chỉ định các kết nối giữa chúng
Tôi đã lấy http://bl.ocks.org/950642 làm điểm bắt đầu, vì về cơ bản đó là loại bố cục tôi muốn có thể tạo:
Đây là mã của tôi trông như thế nào:
<!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>
Mỗi khi tôi thêm một nút mới, nó sẽ gắn nhãn lại cho tất cả các nút hiện có; những thứ này chồng chất lên nhau và mọi thứ bắt đầu trở nên xấu xí. Tôi hiểu tại sao điều này là: bởi vì khi tôi gọi update()
hàm hàm khi thêm một nút mới, nó sẽ thực hiện một node.append(...)
đối với toàn bộ tập dữ liệu. Tôi không thể tìm ra cách thực hiện việc này cho chỉ nút mà tôi đang thêm ... và tôi dường như chỉ có thể sử dụng node.enter()
để tạo một phần tử mới duy nhất, vì vậy điều đó không hoạt động đối với các phần tử bổ sung mà tôi cần liên kết với nút . Làm thế nào tôi có thể sửa lỗi này?
Cảm ơn bạn vì bất kỳ hướng dẫn nào mà bạn có thể cung cấp cho bất kỳ vấn đề nào trong số này!
Đã chỉnh sửa vì tôi đã nhanh chóng sửa một nguồn của một số lỗi khác đã được đề cập trước đó
force.start()
thay vìforce.resume()
khi dữ liệu mới được thêm vào là chìa khóa. Cảm ơn rất nhiều!