我已经制作了一个力导向图,我想改变包含"entity":"company"
的数据的节点形状,因此它们将具有矩形形状,而没有这部分数据的其他数据将是圆圈,因为它们现在是
您可以在此处查看仅包含圈子节点的工作示例:http://jsfiddle.net/dzorz/uWtSk/
我尝试在代码的一部分中添加带有if else
语句的矩形,其中我将形状附加到节点,如下所示:
function(d)
{
if (d.entity == "company")
{
node.append("rect")
.attr("class", function(d){ return "node type"+d.type})
.attr("width", 100)
.attr("height", 50)
.call(force.drag);
}
else
{
node.append("circle")
.attr("class", function(d){ return "node type"+d.type})
.attr("r", function(d) { return radius(d.value) || 10 })
//.style("fill", function(d) { return fill(d.type); })
.call(force.drag);
}
}
但是在任何节点上我都没有任何形状。
设置此方法的正确方法是什么?
整个代码如下所示: 脚本:
var data = {"nodes":[
{"name":"Action 4", "type":5, "slug": "", "value":265000},
{"name":"Action 5", "type":6, "slug": "", "value":23000},
{"name":"Action 3", "type":4, "slug": "", "value":115000},
{"name":"Yahoo", "type":1, "slug": "www.yahoo.com", "entity":"company"},
{"name":"Google", "type":1, "slug": "www.google.com", "entity":"company"},
{"name":"Action 1", "type":2, "slug": "",},
{"name":"Action 2", "type":3, "slug": "",},
{"name":"Bing", "type":1, "slug": "www.bing.com", "entity":"company"},
{"name":"Yandex", "type":1, "slug": "www.yandex.com)", "entity":"company"}
],
"links":[
{"source":0,"target":3,"value":10},
{"source":4,"target":3,"value":1},
{"source":1,"target":7,"value":10},
{"source":2,"target":4,"value":10},
{"source":4,"target":7,"value":1},
{"source":4,"target":5,"value":10},
{"source":4,"target":6,"value":10},
{"source":8,"target":4,"value":1}
]
}
var w = 560,
h = 500,
radius = d3.scale.log().domain([0, 312000]).range(["10", "50"]);
var vis = d3.select("body").append("svg:svg")
.attr("width", w)
.attr("height", h);
vis.append("defs").append("marker")
.attr("id", "arrowhead")
.attr("refX", 17 + 3) /*must be smarter way to calculate shift*/
.attr("refY", 2)
.attr("markerWidth", 6)
.attr("markerHeight", 4)
.attr("orient", "auto")
.append("path")
.attr("d", "M 0,0 V 4 L6,2 Z"); //this is actual shape for arrowhead
//d3.json(data, function(json) {
var force = self.force = d3.layout.force()
.nodes(data.nodes)
.links(data.links)
.distance(100)
.charge(-1000)
.size([w, h])
.start();
var link = vis.selectAll("line.link")
.data(data.links)
.enter().append("svg:line")
.attr("class", function (d) { return "link" + d.value +""; })
.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; })
.attr("marker-end", function(d) {
if (d.value == 1) {return "url(#arrowhead)"}
else { return " " }
;});
function openLink() {
return function(d) {
var url = "";
if(d.slug != "") {
url = d.slug
} //else if(d.type == 2) {
//url = "clients/" + d.slug
//} else if(d.type == 3) {
//url = "agencies/" + d.slug
//}
window.open("//"+url)
}
}
var node = vis.selectAll("g.node")
.data(data.nodes)
.enter().append("svg:g")
.attr("class", "node")
.call(force.drag);
node.append("circle")
.attr("class", function(d){ return "node type"+d.type})
.attr("r", function(d) { return radius(d.value) || 10 })
//.style("fill", function(d) { return fill(d.type); })
.call(force.drag);
node.append("svg:image")
.attr("class", "circle")
.attr("xlink:href", function(d){ return d.img_href})
.attr("x", "-16px")
.attr("y", "-16px")
.attr("width", "32px")
.attr("height", "32px")
.on("click", openLink());
node.append("svg:text")
.attr("class", "nodetext")
.attr("dx", 0)
.attr("dy", ".35em")
.attr("text-anchor", "middle")
.text(function(d) { return d.name });
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 + ")"; });
});
//});
的CSS:
.link10 { stroke: #ccc; stroke-width: 3px; stroke-dasharray: 3, 3; }
.link1 { stroke: #000; stroke-width: 3px;}
.nodetext { pointer-events: none; font: 10px sans-serif; }
.node.type1 {
fill:brown;
}
.node.type2 {
fill:#337147;
}
.node.type3 {
fill:blue;
}
.node.type4 {
fill:red;
}
.node.type5 {
fill:#1BC9E0;
}
.node.type6 {
fill:#E01B98;
}
image.circle {
cursor:pointer;
}
您可以编辑我在帖子开头链接的jsfiddle ...
答案 0 :(得分:6)
此处的解决方案:http://jsfiddle.net/Bull/4btFx/1/
我通过向每个节点添加一个类,然后使用“selectAll”为每个类添加形状来实现此功能。在下面的代码中,我添加了一个类“node”和一个由我的JSON(d.type)返回的类,它是“rect”或“ellipse”。
var node = container.append("g")
.attr("class", "nodes")
.selectAll(".node")
.data(graph.nodes)
.enter().append("g")
.attr("class", function(d) {
return d.type + " node";
})
.call(drag);
然后,您可以为每个类的所有元素添加形状:
d3.selectAll(".rect").append("rect")
.attr("width", window.nodeWidth)
.attr("height", window.nodeHeight)
.attr("class", function(d) {
return "color_" + d.class
});
d3.selectAll(".ellipse").append("rect")
.attr("rx", window.nodeWidth*0.5)
.attr("ry", window.nodeHeight*0.5)
.attr("width", window.nodeWidth)
.attr("height", window.nodeHeight)
.attr("class", function(d) {
return "color_" + d.class
});
在上面的例子中,我使用带有半径的矩形来绘制椭圆,因为它以与矩形相同的方式使它们居中。但它也适用于其他形状。在我链接的jsfiddle中,中心关闭,但形状是正确的。
答案 1 :(得分:1)
我比你领先一步:)
我使用“路径”而不是“圈子”或“矩形”解决了您的问题,您可以查看我的解决方案,也许可以帮助我解决我遇到的问题......
答案 2 :(得分:1)
这是一个较旧的文章,但是在2020年7月尝试使该概念与D3 v5一起使用时,我遇到了同样的麻烦。这是我的解决方案,以防其他人尝试构建力导向图,圆形和矩形元素代表不同类型的节点:
方法是创建元素,然后在调用力模拟时分别放置它们(因为圆具有cx,cy和r属性,而rect具有x,y,宽度和高度)。该代码中的大部分内容都遵循以下博客文章中的示例:https://medium.com/ninjaconcept/interactive-dynamic-force-directed-graphs-with-d3-da720c6d7811
仅供参考,我之前已将svg声明为d3.select(“具有ID或类的某些div”),以及一些未显示的读取数据的帮助函数(setNodeSize,setNodeColor)。我已经使用D3.filter方法检查数据中的布尔字段-节点是初始的还是否?
强制模拟实例:
const simulation = d3.forceSimulation()
//the higher the strength (if negative), greater distance between nodes.
.force('charge', d3.forceManyBody().strength(-120))
//places the chart in the middle of the content area...if not it's top-left
.force('center', d3.forceCenter(width / 2, height / 2))
创建圆形节点:
const nodeCircles = svg.append('g')
.selectAll('circle')
.data(nodes)
.enter()
.filter(d => d.initial)
.append('circle')
.attr('r', setNodeSize)
.attr('class', 'node')
.attr('fill', setNodeColor)
.attr('stroke', '#252525')
.attr('stroke-width', 2)
然后创建矩形节点:
const nodeRectangles = svg.append('g')
.selectAll('rect')
.data(nodes)
.enter()
.filter(d => !d.initial)
.append('rect')
.attr('width', setNodeSize)
.attr('height', setNodeSize)
.attr('class', 'node')
.attr('fill', setNodeColor)
.attr('stroke', '#252525')
.attr('stroke-width', 2)
然后在调用模拟时:
simulation.nodes(nodes).on("tick", () => {
nodeCircles
.attr("cx", node => node.x)
.attr("cy", node => node.y)
nodeRectangles
.attr('x', node => node.x)
.attr('y', node => node.y)
.attr('transform', 'translate(-10, -7)')
当然,还有更多添加行/链接,文本标签等的功能。请随时ping我以获取更多代码。上面列出的中等帖子非常有帮助!
答案 3 :(得分:0)
我使用从Filtering in d3.js on bl.ocks.org收集的filter
方法实现了此行为。
initGraphNodeShapes() {
let t = this;
let graphNodeCircles =
t.graphNodesEnter
.filter(d => d.shape === "circle")
.append("circle")
.attr("r", 15)
.attr("fill", "green");
let graphNodeRects =
t.graphNodesEnter
.filter(d => d.shape === "rect")
.append("rect")
.attr("width", 20)
.attr("height", 10)
.attr("x", -10) // -1/2 * width
.attr("y", -5) // -1/2 * height
.attr("fill", "blue");
return graphNodeCircles.merge(graphNodeRects);
}
我在initGraphNodeShapes
调用中有这个,因为我的代码相对较大且重构了。 t.graphNodesEnter
是数据加入enter()
调用其他地方后对数据选择的引用。如果您需要更多上下文,请告诉我。另外,我使用d => ...
版本,因为我使用ES6来启用lambdas。如果您使用的是ES6之前的版本,那么您必须将其更改为function(d)...
表单。