我正在尝试执行强制定向布局,其中链接是指向节点的箭头(如显示here和here中所示的示例),并且其中有子节点的节点也是可折叠的(如Mike Bostock的例子所示:here或here)。
到目前为止折叠节点工作正常,但我无法理解箭头如何包含在路径中。以下是基于以上示例的代码的一部分:
force.nodes(nodes)
.links(links)
.gravity(0.05)
.charge(-1500)
.linkDistance(100)
.friction(0.5)
.linkStrength(function(l, i) {return 1 })
.size([w, h])
.start();
// Append markers
vis.append("svg:defs").selectAll("marker")
.data(["end"])
.enter().append("svg:marker")
.attr("id", String)
.attr("viewBox", "0 -5 10 10")
.attr("refX", 15)
.attr("refY", -1.5)
.attr("markerWidth", 6)
.attr("markerHeight", 6)
.attr("orient", "auto")
//.append("svg:path") // <-- I not sure what this does
//.attr("d", "M0,-5L10,0L0,5");
var path = vis.selectAll("path")
.data(force.links());
// Enter new paths
path.enter().insert("svg:path")
.attr("class", "link")
.attr("marker-end", "url(#end)")
.style("stroke", "#ccc");
// Exit any old paths.
path.exit().remove();
// Update the nodes…
var node = vis.selectAll("g.node")
.data(nodes, function(d) { return d.id; })
node.select("circle")
.style("fill", color);
// Enter any new nodes.
var nodeEnter = node.enter().append("svg:g")
.attr("class", "node")
.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; })
.on("click", click)
.call(force.drag);
//Add an immage to the node
nodeEnter.append("svg:image")
.attr("xlink:href", function(d) { return d.image;})
.attr("x", function(d) { return (0 - Math.sqrt(d.size)) / 10 || 4.5;})
.attr("y", function(d) { return (0 - Math.sqrt(d.size)) / 10 || 4.5;})
.attr("height", 16)
.attr("width", 16);
// Exit any old nodes.
node.exit().remove();
// Re-select for update.
node = vis.selectAll("g.node");
path = vis.selectAll("path")
force.on("tick", function() {
// Draw curved links
path.attr("d", function(d) {
var dx = d.target.x - d.source.x,
dy = d.target.y - d.source.y,
dr = Math.sqrt(dx * dx + dy * dy);
return "M" + d.source.x + ","
+ d.source.y
+ "A" + dr + ","
+ dr + " 0 0,1 "
+ d.target.x + ","
+ d.target.y;
});
node.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
});
我的理解是下面的代码片段负责绘制箭头,指定箭头应指向的块(例如.data(["end"])
)
vis.append("svg:defs").selectAll("marker")
.data(["end"])
.enter().append("svg:marker")
.attr("id", String)
.attr("viewBox", "0 -5 10 10")
.attr("refX", 15)
.attr("refY", -1.5)
.attr("markerWidth", 6)
.attr("markerHeight", 6)
.attr("orient", "auto")
.append("svg:path")
.attr("d", "M0,-5L10,0L0,5");
然后在输入路径时引用它(即.attr("marker-end", "url(#end)");
)。
但我可能会遗漏一些东西,因为在我的图表中显示了路径,但没有显示箭头。
感谢您的帮助!
答案 0 :(得分:0)
我找到了“几乎”正常工作的解决方案。以下是完整的代码,以及对底部仍然失败的简短说明:
var w = 1280,
h = 800,
root,
vis;
var force = d3.layout.force()
.gravity(200)
.charge(-1500)
.linkDistance(100)
.friction(0.01)
.size([w, h])
;
$(document).ready(function() {
var newHeight = '100%';
$("#svgdiv").html("<svg id='graph' xmlns='http://www.w3.org/2000/svg' width='100%' height='100%'></svg>");
vis = d3.select("svg");
d3.json("../json/flare.json", function(json) {
root = json;
root.fixed = true;
root.x = w / 2;
root.y = h / 2;
// Build the arrow
var defs = vis.insert("svg:defs").selectAll("marker")
.data(["end"]);
defs.enter().append("svg:marker")
.attr("id", String)
.attr("viewBox", "0 -5 15 15")
.attr("refX", 15)
.attr("refY", -1.5)
.attr("markerWidth", 6)
.attr("markerHeight", 6)
.attr("orient", "auto")
.append("svg:path")
.attr("d", "M0,-5L10,0L0,5");
update();
});
});
/**
*
*/
function update() {
var nodes = flatten(root),
links = d3.layout.tree().links(nodes);
// Restart the force layout.
force.nodes(nodes)
.links(links)
.gravity(0.05)
.charge(-1500)
.linkDistance(100)
.friction(0.5)
.linkStrength(function(l, i) {return 1 })
.size([w, h])
.start();
var path = vis.selectAll("path.link") // <-- THIS WAS CHANGED TO "path.links"
.data(links, function(d) { return d.target.id; });
path.enter().insert("svg:path")
.attr("class", "link")
.attr("marker-end", "url(#end)")
.style("stroke", "#ccc");
// Exit any old paths.
path.exit().remove();
// Update the nodes…
var node = vis.selectAll("g.node")
.data(nodes, function(d) { return d.id; });
// Enter any new nodes.
var nodeEnter = node.enter().insert("svg:g")
.attr("class", "node")
.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; })
.on("click", click)
.call(force.drag);
node.select("circle")
.style("fill", color);
nodeEnter.append("svg:circle")
.attr("r", function(d) { return Math.sqrt(d.size) / 10 || 4.5; })
.style("fill", color);
// Add text to the node (as defined by the json file)
nodeEnter.append("svg:text")
.attr("text-anchor", "middle")
.attr("dx", function(d) { return Math.sqrt(d.size) / 10 || 4.5; })
.attr("dy", ".35em")
.text(function(d) { return d.name; });
/* */
//Add an image to the node
nodeEnter.append("svg:image")
.attr("xlink:href", function(d) { return d.logo;})
.attr("x", function(d) { return (0 - Math.sqrt(d.size)) / 10 || 4.5;})
.attr("y", function(d) { return (0 - Math.sqrt(d.size)) / 10 || 4.5;})
.attr("height", 16)
.attr("width", 16);
/* */
// Exit any old nodes.
node.exit().remove();
// Re-select for update.
node = vis.selectAll("g.node");
path = vis.selectAll("path.link"); // <-- THIS WAS CHANGED TO "path.link"
force.on("tick", function() {
path.attr("d", function(d) {
var dx = d.target.x - d.source.x,
dy = d.target.y - d.source.y,
dr = Math.sqrt(dx * dx + dy * dy);
return "M" + d.source.x + ","
+ d.source.y
+ "A" + dr + ","
+ dr + " 0 0,1 "
+ d.target.x + ","
+ d.target.y;
});
node.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
});
}
// Color leaf nodes orange, and packages white or blue.
function color(d) {
return d._children ? "#3182bd" : d.children ? "#c6dbef" : "#fd8d3c";
}
// Toggle children on click.
function click(d) {
if (d.children) {
d._children = d.children;
d.children = null;
} else {
d.children = d._children;
d._children = null;
}
update();
}
// Returns a list of all nodes under the root.
function flatten(root) {
var nodes = [];
var i = 0;
function recurse(node) {
if (node.children)
node.children.forEach(recurse);
if (!node.id)
node.id = ++i;
nodes.push(node);
}
recurse(root);
return nodes;
}
我认为箭头未显示的原因之一是我在路径中给了一个类,如path.enter().insert("svg:path").attr("class", "link")
,但是当选择路径时我没有正确引用它,因此它没有画出它
(即我有:
var path = vis.selectAll("path")
应该在哪里:
var path = vis.selectAll("path.link").)
然后我还发现箭头标记的defs应该在update()函数之外定义,该函数在折叠和展开节点时调用。否则,每次单击节点时它都会将其附加到svg,这不是很有效。
所以现在节点崩溃onclick并绘制箭头(尽管它们很难看)。但是,还有一个问题让我很困惑:过了一段时间,没有任何明显的时间间隔或点击模式,图表冻结并在浏览器中调试Cannot read property 'target' of undefined
。在“tick”函数中触发此错误,其中定义了弯曲路径:
path.attr("d", function(d) {
var dx = d.target.x - d.source.x,
dy = d.target.y - d.source.y,
dr = Math.sqrt(dx * dx + dy * dy);
return "M" + d.source.x + ","
+ d.source.y
+ "A" + dr + ","
+ dr + " 0 0,1 "
+ d.target.x + ","
+ d.target.y;
});
找不到d.target
,因为在输入path = vis.selectAll("path.link");
force.on("tick", function() ...
)
奇怪的是,它在开始时起作用,并且它会在随机的时间突然停止工作! 所以,任何机构都知道可能发生的事情吗?
编辑:
我现在知道出了什么问题。出于某种原因,我使用的是我在某处找到的脚本d3.layout.js,我认为这是崩溃树所需要的。我删除了那个库,并使用了正常的d3.v3.js,一切都像它应该的那样...只是箭头是丑陋的。因此,上面的脚本应该有效,具有可折叠节点和有向路径。