|
<html> |
|
<head> |
|
<title>D3 Force Layout Example 1</title> |
|
<meta charset="UTF-8"> |
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
<link rel="stylesheet" href="main.css"> |
|
<script type="text/javascript" src="http://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script> |
|
<style> |
|
path.link { |
|
fill: none; |
|
stroke: #666; |
|
stroke-width: 1.5px; |
|
} |
|
|
|
circle { |
|
fill: #ccc; |
|
stroke: #fff; |
|
stroke-width: 1.5px; |
|
} |
|
|
|
text { |
|
fill: #000; |
|
font: 10px sans-serif; |
|
pointer-events: none; |
|
} |
|
|
|
</style> |
|
</head> |
|
<body> |
|
<script> |
|
var w = 800; |
|
var h = 450; |
|
var margin = { |
|
top: 20, |
|
bottom: 20, |
|
left: 20, |
|
right: 20 |
|
}; |
|
var width = w - margin.left - margin.right; |
|
var height = h - margin.top - margin.bottom; |
|
|
|
var svg = d3.select("body") |
|
.append("svg") |
|
.attr("id", "chart") |
|
.attr("height", h) |
|
.attr("width", w); |
|
|
|
var linkScale = d3.scale.linear() |
|
.range([0.25, 1.0]); |
|
|
|
var colorScale = d3.scale.category20(); |
|
|
|
d3.csv("data.csv", function (error, links) { |
|
var nodes = {}; |
|
|
|
links.forEach(function (link) { |
|
link.source = nodes[link.source] || (nodes[link.source] = {name: link.source}); |
|
link.target = nodes[link.target] || (nodes[link.target] = {name: link.target}); |
|
link.value = +link.value; |
|
}); |
|
|
|
linkScale.domain(d3.extent(links, function (d) { |
|
return d.value; |
|
})); |
|
|
|
var force = d3.layout.force() |
|
.nodes(d3.values(nodes)) |
|
.links(links) |
|
.size([width, height]) |
|
.linkDistance(60) |
|
.charge(-300) |
|
.on("tick", tick) |
|
.start(); |
|
|
|
svg.append("svg:defs").selectAll('marker') |
|
.data(['end']) |
|
.enter() |
|
.append("svg:marker") |
|
.attr('id', function (d) { |
|
return d; |
|
}) |
|
.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"); |
|
|
|
var path = svg.append("svg:g").selectAll("path") |
|
.data(force.links()) |
|
.enter() |
|
.append("svg:path") |
|
.classed("link", true) |
|
.attr("marker-end", "url(#end)"); |
|
|
|
var node = svg.selectAll(".node") |
|
.data(force.nodes()) |
|
.enter() |
|
.append("g") |
|
.classed("node", true) |
|
.on("click", click) |
|
.on("dblclick", dblclick) |
|
.call(force.drag()); |
|
|
|
node.append("circle") |
|
.attr("r", 5) |
|
.style("fill", function (d) { |
|
return colorScale(d.name) |
|
}); |
|
|
|
|
|
node.append("text") |
|
.attr("x", 12) |
|
.attr("dy", ".35em") |
|
.text(function (d) { |
|
return d.name; |
|
}); |
|
|
|
function 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; |
|
}).style("opacity", function (d) { |
|
return linkScale(d.value); |
|
}); |
|
|
|
node.attr("transform", function (d) { |
|
return "translate(" + d.x + "," + d.y + ")"; |
|
}); |
|
} |
|
|
|
function click() { |
|
d3.select(this).select("text").transition() |
|
.duration(750) |
|
.attr("x", 22) |
|
.style("fill", "steelblue") |
|
.style("stroke", "lightsteelblue") |
|
.style("stroke-width", ".5px") |
|
.style("font", "20px sans-serif"); |
|
|
|
d3.select(this).select("circle").transition() |
|
.duration(750) |
|
.attr("r", 16); |
|
} |
|
|
|
function dblclick() { |
|
d3.select(this).select("text").transition() |
|
.duration(750) |
|
.attr("x", 12) |
|
.style("fill", "black") |
|
.style("stroke", "none") |
|
.style("stroke-width", "none") |
|
.style("font", "10px sans-serif"); |
|
|
|
d3.select(this).select("circle").transition() |
|
.duration(750) |
|
.attr("r", 6); |
|
} |
|
}); |
|
</script> |
|
</body> |
|
</html> |