![](/img/trans.png)
[英]Why do I have to re-assign the links and nodes when updating the d3.js force layout
[英]D3.js how do I arrange nodes of a force layout to be on a circle
我已經開發了一種力量布局來表示社會群體之間的關系。 現在我想讓節點分布在一個帶有連接它們的鏈接的圓圈中。 做這個的最好方式是什么?
代碼的完整版本(沒有數據)在這里http://jsfiddle.net/PatriciaW/zZSJT/ (為什么我也必須在這里包含代碼?這是主要部分)
d3.json("/relationships?nocache=" + (new Date()).getTime(),function(error,members){
var links=members.organizations.map(function(members) {
return members.member;
});
var nodes = {};
links.forEach(function(link) {
link.source = nodes[link.xsource] || (nodes[link.xsource] = {source: link.xsource, name: link.xsource, category: link.categorysource, path: link.pathsource, desc: link.descsource, title: link.titlesource});
link.target = nodes[link.xtarget] || (nodes[link.xtarget] = {target: link.xtarget, name: link.xtarget, category: link.categorytarget, path: link.pathtarget, desc: link.desctarget, title: link.titletarget});
});
force = d3.layout.force()
.nodes(d3.values(nodes))
.links(links)
.size([width, height])
.charge(-120)
.linkDistance(function() {return (Math.random() * 200) + 100;})
.linkStrength(0.5)
.on("tick", tick)
.start();
var link = svg.selectAll(".link")
.data(force.links())
.enter().append("line")
.attr("class", "link");
var node_drag = d3.behavior.drag()
.on("dragstart", dragstart)
.on("drag", dragmove)
.on("dragend", dragend);
var loading = svg.append("text")
.attr("x", width / 2)
.attr("y", height / 2)
.attr("dy", ".35em")
.style("text-anchor", "middle")
.text("Simulating. One moment please…");
function dragstart(d, i) {
force.stop() // stops the force auto positioning before you start dragging
}
function dragmove(d, i) {
d.px += d3.event.dx;
d.py += d3.event.dy;
d.x += d3.event.dx;
d.y += d3.event.dy;
tick(); // this is the key to make it work together with updating both px,py,x,y on d !
}
function dragend(d, i) {
d.fixed = true; // of course set the node to fixed so the force doesn't include the node in its auto positioning stuff
tick();
force.resume();
};
var node = svg.selectAll(".node")
.data(force.nodes())
.enter().append("g")
.attr("class", "node")
.on("mouseover", mouseover)
.on("mouseout", mouseout)
.on("click", clickAlert)
.call(node_drag);
node.append("circle")
.attr("r", 8)
.style("fill", function(d) {
return categoryColour [d.category];
})
// add an image marker
node.append("image")
.attr("x",-8)
.attr("y",-8)
.attr("width", 16)
.attr("height", 16)
.attr("xlink:href", function(d) {
return categoryImage [d.category]
})
.on("click", clickAlert)
.style("cursor", "pointer")
node.append("text")
.attr("x", 12)
.attr("dy", ".35em")
.text(function(d) {
return d.name;
});
// Use a timeout to allow the rest of the page to load first.
setTimeout(function() {
// Run the layout a fixed number of times.
// The ideal number of times scales with graph complexity.
force.start();
for (var i = n * n; i > 0; --i) force.tick();
force.stop();
svg.selectAll("line")
.data(links)
.enter().append("line")
.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; });
svg.selectAll("circle")
.data(nodes)
.enter().append("circle")
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; })
.attr("r", 4.5);
loading.remove();
}, 10);
function tick() {
link
.attr("x1", function(d) {
return d.source.x + xadj; })
.attr("y1", function(d) {
return d.source.y + yadj; })
.attr("x2", function(d) {
return d.target.x +xadj; })
.attr("y2", function(d) {
return d.target.y +yadj; });
node
.attr("transform", function(d) {
return "translate(" + (d.x + xadj) + "," + (d.y + yadj) + ")";
});
};
function mouseover() {
d3.select(this).select("circle").transition()
.duration(750)
.attr("r", 16);
d3.select(this).select("text")
.attr("font-size","34px")
.style("font-weight", "bold");
};
function mouseout() {
d3.select(this).select("circle").transition()
.duration(750)
.attr("r", 8);
d3.select(this).select("text")
.attr("font-size","12px")
.style("font-weight", "normal");
};
}) // end json
這是其他人的解決方案 :
此網絡圖使用D3力布局繪制節點和鏈接,但不是使用d3.force()來查找最佳節點位置,而是繪制不可見的弧並沿圓周均勻放置節點。
<!DOCTYPE html>
<html>
<head>
<script src="http://d3js.org/d3.v3.min.js"></script>
<meta charset="utf-8">
<title>JS Bin</title>
<style>
line.node-link, path.node-link {
fill: none;
stroke: black
}
circle.node {
fill: white;
stroke: black
}
circle.node+text {
text-anchor: middle;
}
text {
font-family: sans-serif;
pointer-events: none;
}
</style>
</head>
<body>
<script type="text/javascript">
// number of random nodes (gets crowded at >25 unless you change node diameter)
var num = 20;
// returns random int between 0 and num
function getRandomInt() {return Math.floor(Math.random() * (num));}
// nodes returns a [list] of {id: 1, fixed:true}
var nodes = d3.range(num).map(function(d) { return {id: d}; });
// links returns a [list] of {source: 0, target: 1} (values refer to indicies of nodes)
var links = d3.range(num).map(function(d) { return {source: getRandomInt(), target: getRandomInt()}; });
var width = 500,
height = 500;
var force = d3.layout.force()
.nodes(nodes)
.links(links)
.size([width, height]);
// evenly spaces nodes along arc
var circleCoord = function(node, index, num_nodes){
var circumference = circle.node().getTotalLength();
var pointAtLength = function(l){return circle.node().getPointAtLength(l)};
var sectionLength = (circumference)/num_nodes;
var position = sectionLength*index+sectionLength/2;
return pointAtLength(circumference-position)
}
// fades out lines that aren't connected to node d
var is_connected = function(d, opacity) {
lines.transition().style("stroke-opacity", function(o) {
return o.source === d || o.target === d ? 1 : opacity;
});
}
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
// invisible circle for placing nodes
// it's actually two arcs so we can use the getPointAtLength() and getTotalLength() methods
var dim = width-80
var circle = svg.append("path")
.attr("d", "M 40, "+(dim/2+40)+" a "+dim/2+","+dim/2+" 0 1,0 "+dim+",0 a "+dim/2+","+dim/2+" 0 1,0 "+dim*-1+",0")
.style("fill", "#f5f5f5");
force.start();
// set coordinates for container nodes
nodes.forEach(function(n, i) {
var coord = circleCoord(n, i, nodes.length)
n.x = coord.x
n.y = coord.y
});
// use this one for straight line links...
// var lines = svg.selectAll("line.node-link")
// .data(links).enter().append("line")
// .attr("class", "node-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; });
// ...or use this one for curved line links
var lines = svg.selectAll("path.node-link")
.data(links).enter().append("path")
.attr("class", "node-link")
.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;
});
var gnodes = svg.selectAll('g.gnode')
.data(nodes).enter().append('g')
.attr("transform", function(d) {
return "translate("+d.x+","+d.y+")"
})
.classed('gnode', true);
var node = gnodes.append("circle")
.attr("r", 25)
.attr("class", "node")
.on("mouseenter", function(d) {
is_connected(d, 0.1)
node.transition().duration(100).attr("r", 25)
d3.select(this).transition().duration(100).attr("r", 30)
})
.on("mouseleave", function(d) {
node.transition().duration(100).attr("r", 25);
is_connected(d, 1);
});
var labels = gnodes.append("text")
.attr("dy", 4)
.text(function(d){return d.id})
</script>
</body>
</html>
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.