|
<!DOCTYPE html> |
|
<html lang="en"> |
|
<head> |
|
<meta charset="utf-8"> |
|
<title>Force Layout with labels on edges</title> |
|
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script> |
|
<style type="text/css"> |
|
</style> |
|
</head> |
|
<body> |
|
|
|
<script type="text/javascript"> |
|
|
|
var w = 1200; |
|
var h = 1000; |
|
var linkDistance=100; |
|
|
|
var colors = d3.scale.category10(); |
|
|
|
var dataset = { |
|
|
|
nodes: [ |
|
{name: "Adam", d: 40}, // 0 |
|
{name: "Bob", d: 20}, // 1 |
|
{name: "Carrie", d: 33}, // 2 |
|
{name: "Donovan", d: 40}, // 3 |
|
{name: "Edward", d: 20}, // 4 |
|
{name: "Team A (85md)", d: 85}, // 5 |
|
{name: "Team B (60md)", d: 60}, // 6 |
|
{name: "Team C (8md)", d: 8}, // 7 |
|
{name: "Task 1", d: 35}, // 8 |
|
{name: "Task 2", d: 55}, // 9 |
|
{name: "Task 3", d: 35}, // 10 |
|
{name: "Task 4", d: 55}, // 11 |
|
{name: "Story I", d: 15}, // 12 |
|
{name: "Story II", d: 15} // 13 |
|
], |
|
edges: [ |
|
{source: 0, target: 5, stroke: 4, color: "#00afef", label: "80% (32hrs)"}, |
|
{source: 0, target: 6, stroke: 1, color: "#000", label: "20% (8hrs)"}, |
|
{source: 1, target: 5, stroke: 5, color: "#000", label: "100% (20hrs)"}, |
|
{source: 2, target: 5, stroke: 5, color: "#000", label: "100% (33hrs)"}, |
|
{source: 3, target: 6, stroke: 5, color: "#000", label: "100% (40hrs)"}, |
|
{source: 4, target: 6, stroke: 3, color: "#000", label: "60% (12hrs)"}, |
|
{source: 4, target: 7, stroke: 2, color: "#000", label: "40% (8hrs)"}, |
|
{source: 5, target: 8, stroke: 2, color: "#000", label: "40% (34md)"}, |
|
{source: 5, target: 9, stroke: 3, color: "#000", label: "60% (51md)"}, |
|
{source: 6, target: 8, stroke: 5, color: "#000", label: "33% (20md)"}, |
|
{source: 7, target: 9, stroke: 5, color: "#000", label: "100% (8md)"}, |
|
{source: 6, target: 10, stroke: 5, color: "#000", label: "33% (20md)"}, |
|
{source: 6, target: 11, stroke: 5, color: "#000", label: "33% (20md)"}, |
|
{source: 12, target: 9, stroke: 1, color: "#000", label: ""}, |
|
{source: 12, target: 10, stroke: 1, color: "#000", label: ""}, |
|
{source: 12, target: 11, stroke: 1, color: "#000", label: ""}, |
|
{source: 13, target: 8, stroke: 1, color: "#000", label: ""}, |
|
{source: 13, target: 10, stroke: 1, color: "#000", label: ""}, |
|
] |
|
}; |
|
|
|
|
|
var svg = d3.select("body").append("svg").attr({"width":w,"height":h}); |
|
|
|
var force = d3.layout.force() |
|
.nodes(dataset.nodes) |
|
.links(dataset.edges) |
|
.size([w,h]) |
|
.linkDistance([linkDistance + Math.random() * 100 - 50]) |
|
.charge([-3500]) |
|
.theta(0.1) |
|
.gravity(0.05) |
|
.start(); |
|
|
|
|
|
|
|
var edges = svg.selectAll("line") |
|
.data(dataset.edges) |
|
.enter() |
|
.append("line") |
|
.attr("id",function(d,i) {return 'edge'+i}) |
|
//.attr('marker-end','url(#arrowhead)') |
|
.style("stroke", function(d) { return d.color }) |
|
.style("stroke-width", function(d) { return d.stroke + 'px' }) |
|
.style("pointer-events", "none"); |
|
|
|
var nodes = svg.selectAll("circle") |
|
.data(dataset.nodes) |
|
.enter() |
|
.append("circle") |
|
.attr({"r": function(d) { return d.d }}) |
|
.style("fill",function(d,i){return colors(i);}) |
|
.call(force.drag) |
|
|
|
|
|
var nodelabels = svg.selectAll(".nodelabel") |
|
.data(dataset.nodes) |
|
.enter() |
|
.append("text") |
|
.attr({"x":function(d){return d.x;}, |
|
"y":function(d){return d.y;}, |
|
"class":"nodelabel", |
|
"stroke":"black"}) |
|
.text(function(d){return d.name;}); |
|
|
|
var edgepaths = svg.selectAll(".edgepath") |
|
.data(dataset.edges) |
|
.enter() |
|
.append('path') |
|
.attr({'d': function(d) {return 'M '+d.source.x+' '+d.source.y+' L '+ d.target.x +' '+d.target.y}, |
|
'class':'edgepath', |
|
'fill-opacity':0, |
|
'stroke-opacity':0, |
|
'fill':'blue', |
|
'stroke':'red', |
|
'id':function(d,i) {return 'edgepath'+i}}) |
|
.style("pointer-events", "none"); |
|
|
|
var edgelabels = svg.selectAll(".edgelabel") |
|
.data(dataset.edges) |
|
.enter() |
|
.append('text') |
|
.style("pointer-events", "none") |
|
.attr({'class':'edgelabel', |
|
'id':function(d,i){return 'edgelabel'+i}, |
|
'dx':80, |
|
'dy':-5, |
|
'font-size':12, |
|
'fill':'#000'}); |
|
|
|
edgelabels.append('textPath') |
|
.attr('xlink:href',function(d,i) {return '#edgepath'+i}) |
|
.style("pointer-events", "none") |
|
.text(function(d,i){return d.label}); |
|
|
|
|
|
svg.append('defs').append('marker') |
|
.attr({'id':'arrowhead', |
|
'viewBox':'-0 -5 10 10', |
|
'refX':"25px", |
|
'refY':0, |
|
'markerUnits':'userSpaceOnUse', |
|
'orient':'auto', |
|
'markerWidth':10, |
|
'markerHeight':10, |
|
'xoverflow':'visible'}) |
|
.append('svg:path') |
|
.attr('d', 'M 0,-5 L 10 ,0 L 0,5'); |
|
//.attr('fill', '#ccc') |
|
//.attr('stroke','#ccc'); |
|
|
|
|
|
force.on("tick", function(){ |
|
|
|
edges.attr({"x1": function(d){return d.source.x;}, |
|
"y1": function(d){return d.source.y;}, |
|
"x2": function(d){return d.target.x;}, |
|
"y2": function(d){return d.target.y;} |
|
}); |
|
|
|
nodes.attr({"cx":function(d){return d.x;}, |
|
"cy":function(d){return d.y;} |
|
}); |
|
|
|
nodelabels.attr("x", function(d) { return d.x; }) |
|
.attr("y", function(d) { return d.y; }); |
|
|
|
edgepaths.attr('d', function(d) { var path='M '+d.source.x+' '+d.source.y+' L '+ d.target.x +' '+d.target.y; |
|
//console.log(d) |
|
return path}); |
|
|
|
edgelabels.attr('transform',function(d,i){ |
|
if (d.target.x<d.source.x){ |
|
bbox = this.getBBox(); |
|
rx = bbox.x+bbox.width/2; |
|
ry = bbox.y+bbox.height/2; |
|
return 'rotate(180 '+rx+' '+ry+')'; |
|
} |
|
else { |
|
return 'rotate(0)'; |
|
} |
|
}); |
|
}); |
|
|
|
</script> |
|
|
|
</body> |
|
</html> |