Javascript 向强制定向布局添加新节点
关于堆栈溢出的第一个问题,请容忍我!我是d3.js新手,但我一直对其他人能用它完成的事情感到惊讶。。。几乎同样惊讶于我自己在这方面所取得的进展是如此之小!很明显,我不是在摸索什么,所以我希望这里善良的灵魂能给我光明 我的目的是制作一个可重用的javascript函数,它只需执行以下操作:Javascript 向强制定向布局添加新节点,javascript,d3.js,force-layout,Javascript,D3.js,Force Layout,关于堆栈溢出的第一个问题,请容忍我!我是d3.js新手,但我一直对其他人能用它完成的事情感到惊讶。。。几乎同样惊讶于我自己在这方面所取得的进展是如此之小!很明显,我不是在摸索什么,所以我希望这里善良的灵魂能给我光明 我的目的是制作一个可重用的javascript函数,它只需执行以下操作: 在指定的DOM元素中创建空白的力定向图 允许您向该图形添加和删除带标签的、带有图像的节点,并指定它们之间的连接 我把它作为一个起点,因为这是我想要创建的基本布局: 我的代码是这样的: <!DOCTY
- 在指定的DOM元素中创建空白的力定向图
- 允许您向该图形添加和删除带标签的、带有图像的节点,并指定它们之间的连接
<!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>
.link{stroke:#ccc;}
.nodetext{指针事件:无;字体:10px无衬线;}
正文{宽度:100%;高度:100%;边距:无;填充:无;}
#图形{宽度:500px;高度:500px;边框:3px纯黑色;边框半径:12px;边距:自动;}
函数图(el){
//初始化图形对象
var graph=this.graph={
“节点”:[{“名称”:“原因”},{“名称”:“效果”}],
“链接”:[{“源”:0,“目标”:1}]
};
//在图形对象上添加和删除元素
this.addNode=函数(名称){
图[“节点”].push({“name”:name});
更新();
}
this.removeNode=函数(名称){
图[“节点”]=过滤器(图[“节点”]、函数(节点){return(节点[“名称”]!=name)});
图[“链接”]=过滤器(图[“链接”]、函数(链接){return((链接[“源”][“名称”]!=name)&&(链接[“目标”][“名称”!=name)));
更新();
}
var findNode=函数(名称){
for(图[“节点”]中的变量i)if(图[“节点”][i][“名称”]==name)返回图[“节点”][i];
}
this.addLink=函数(源、目标){
图形[“链接]。推送({“源”:findNode(源),“目标”:findNode(目标)});
更新();
}
//在指定元素中设置D3可视化
var w=$(el).innerWidth(),
h=$(el).innerHeight();
var vis=d3.select(el).append(“svg:svg”)
.attr(“宽度”,w)
.attr(“高度”,h);
var-force=d3.layout.force()
.nodes(图.nodes)
.links(graph.links)
.重力(.05)
.距离(100)
。收费(-100)
.尺寸([w,h]);
变量更新=函数(){
var link=vis.selectAll(“line.link”)
.数据(图表链接);
link.enter().insert(“行”)
.attr(“类”、“链接”)
.attr(“x1”,函数(d){返回d.source.x;})
.attr(“y1”,函数(d){返回d.source.y;})
.attr(“x2”,函数(d){返回d.target.x;})
.attr(“y2”,函数(d){返回d.target.y;});
link.exit().remove();
var node=vis.selectAll(“g.node”)
.数据(图形节点);
node.enter().append(“g”)
.attr(“类”、“节点”)
.呼叫(强制拖动);
node.append(“图像”)
.attr(“类”、“圈”)
.attr(“xlink:href,”https://d3nwyuy0nl342s.cloudfront.net/images/icons/public.png")
.attr(“x”、“-8px”)
.attr(“y”、“-8px”)
.attr(“宽度”,“16px”)
.attr(“高度”、“16px”);
node.append(“文本”)
.attr(“类”、“节点文本”)
.attr(“dx”,12)
.attr(“dy”,“.35em”)
.text(函数(d){返回d.name});
node.exit().remove();
force.on(“勾号”,函数(){
attr(“x1”,函数(d){返回d.source.x;})
.attr(“y1”,函数(d){返回d.source.y;})
.attr(“x2”,函数(d){返回d.target.x;})
.attr(“y2”,函数(d){返回d.target.y;});
attr(“transform”,函数(d){return“translate”(“+d.x+”,“+d.y+”)”);});
});
//重新启动强制布局。
力
.nodes(图.nodes)
.links(graph.links)
.start();
}
//让一切顺利
更新();
}
图=新的myGraph(“图”);
//这些是我希望能够为对象提供的命令类型。
图.addNode(“A”);
图.addNode(“B”);
图.添加链接(“A”、“B”);
每次我添加一个新节点时,它都会重新标记所有现有节点;这些堆在一起,事情开始变得丑陋起来。我理解为什么会这样:因为当我在添加新节点时调用update()
函数时,它会执行一个节点。将(…)
附加到整个数据集。我不知道如何只为我正在添加的节点执行此操作。。。显然,我只能使用node.enter()
来创建一个新元素,因此对于需要绑定到节点的其他元素来说,这是行不通的。我怎样才能解决这个问题
感谢您在此问题上提供的任何指导
编辑是因为我很快修复了前面提到的其他几个bug的来源,在长时间无法使其正常工作后,我终于偶然发现了一个我认为与任何文档都没有关联的演示: 这个演示包含了最终帮助我解决问题的关键 在一个
enter()
上添加多个对象可以通过将enter()
指定给一个变量,然后附加到该变量来完成。这是有道理的。第二个关键部分是节点和链接数组必须基于force()
——否则在删除和添加节点时,图形和模型将不同步
这是因为如果构建新阵列,它将缺少以下内容:
- 索引-节点数组中节点的从零开始的索引
- x—当前节点位置的x坐标
- y—当前节点的y坐标
<script type="text/javascript"> function myGraph(el) { // Add and remove elements on the graph object this.addNode = function (id) { nodes.push({"id":id}); update(); } this.removeNode = function (id) { var i = 0; var n = findNode(id); while (i < links.length) { if ((links[i]['source'] === n)||(links[i]['target'] == n)) links.splice(i,1); else i++; } var index = findNodeIndex(id); if(index !== undefined) { nodes.splice(index, 1); update(); } } this.addLink = function (sourceId, targetId) { var sourceNode = findNode(sourceId); var targetNode = findNode(targetId); if((sourceNode !== undefined) && (targetNode !== undefined)) { links.push({"source": sourceNode, "target": targetNode}); update(); } } var findNode = function (id) { for (var i=0; i < nodes.length; i++) { if (nodes[i].id === id) return nodes[i] }; } var findNodeIndex = function (id) { for (var i=0; i < nodes.length; i++) { if (nodes[i].id === id) return i }; } // set up the D3 visualisation in the specified element var w = $(el).innerWidth(), h = $(el).innerHeight(); var vis = this.vis = d3.select(el).append("svg:svg") .attr("width", w) .attr("height", h); var force = d3.layout.force() .gravity(.05) .distance(100) .charge(-100) .size([w, h]); var nodes = force.nodes(), links = force.links(); var update = function () { var link = vis.selectAll("line.link") .data(links, function(d) { return d.source.id + "-" + d.target.id; }); link.enter().insert("line") .attr("class", "link"); link.exit().remove(); var node = vis.selectAll("g.node") .data(nodes, function(d) { return d.id;}); var nodeEnter = node.enter().append("g") .attr("class", "node") .call(force.drag); nodeEnter.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"); nodeEnter.append("text") .attr("class", "nodetext") .attr("dx", 12) .attr("dy", ".35em") .text(function(d) {return d.id}); 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.start(); } // Make it all go update(); } graph = new myGraph("#graph"); // You can do this from the console as much as you like... graph.addNode("Cause"); graph.addNode("Effect"); graph.addLink("Cause", "Effect"); graph.addNode("A"); graph.addNode("B"); graph.addLink("A", "B"); </script>