|
<!DOCTYPE html> |
|
<meta charset="utf-8"> |
|
<body> |
|
<script src="//d3js.org/d3.v3.min.js"></script> |
|
<script> |
|
|
|
/////////////////////////////////////////////////////////////////////////// |
|
/////////////////////////////// Set-up //////////////////////////////////// |
|
/////////////////////////////////////////////////////////////////////////// |
|
|
|
var width = 960, |
|
height = 500; |
|
|
|
var nodes = d3.range(150).map(function() { return {radius: Math.random() * 14 + 8}; }), |
|
root = nodes[0]; |
|
|
|
root.radius = 0; |
|
root.fixed = true; |
|
|
|
var force = d3.layout.force() |
|
.gravity(0.05) |
|
.charge(function(d, i) { return i ? 0 : -2000; }) |
|
.nodes(nodes) |
|
.size([width, height]); |
|
|
|
force.start(); |
|
|
|
var svg = d3.select("body").append("svg") |
|
.attr("width", width) |
|
.attr("height", height); |
|
|
|
/////////////////////////////////////////////////////////////////////////// |
|
///////////////////////////// Create filter /////////////////////////////// |
|
/////////////////////////////////////////////////////////////////////////// |
|
|
|
//SVG filter for the gooey effect |
|
//Code taken from http://tympanus.net/codrops/2015/03/10/creative-gooey-effects/ |
|
var defs = svg.append("defs"); |
|
var filter = defs.append("filter").attr("id","gooeyCodeFilter"); |
|
filter.append("feGaussianBlur") |
|
.attr("in","SourceGraphic") |
|
.attr("stdDeviation","10") |
|
//to fix safari: http://stackoverflow.com/questions/24295043/svg-gaussian-blur-in-safari-unexpectedly-lightens-image |
|
.attr("color-interpolation-filters","sRGB") |
|
.attr("result","blur"); |
|
filter.append("feColorMatrix") |
|
.attr("in","blur") |
|
.attr("mode","matrix") |
|
.attr("values","1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 18 -7") |
|
.attr("result","gooey"); |
|
//If you want the end shapes to be exactly the same size as without the filter |
|
//add the feComposite below. However this will result in a less beautiful gooey effect |
|
//filter.append("feBlend") |
|
// .attr("in","SourceGraphic") |
|
// .attr("in2","gooey"); |
|
//Instead of the feBlend, you can do feComposite. This will also place a sharp image on top |
|
//But it will result in smaller circles |
|
//filter.append("feComposite") //feBlend |
|
// .attr("in","SourceGraphic") |
|
// .attr("in2","gooey") |
|
// .attr("operator","atop"); |
|
|
|
/////////////////////////////////////////////////////////////////////////// |
|
///////////////////////////// Create circles ////////////////////////////// |
|
/////////////////////////////////////////////////////////////////////////// |
|
|
|
var circleWrapper = svg.append("g") |
|
.attr("class", "circleWrapper") |
|
.style("filter", "url(#gooeyCodeFilter)"); |
|
|
|
circleWrapper.selectAll("circle") |
|
.data(nodes.slice(1)) |
|
.enter().append("circle") |
|
.attr("r", function(d) { return d.radius; }) |
|
.style("fill", "#EE3E64"); |
|
|
|
/////////////////////////////////////////////////////////////////////////// |
|
/////////////////////////////// Functions ///////////////////////////////// |
|
/////////////////////////////////////////////////////////////////////////// |
|
|
|
force.on("tick", function(e) { |
|
var q = d3.geom.quadtree(nodes), |
|
i = 0, |
|
n = nodes.length; |
|
|
|
while (++i < n) q.visit(collide(nodes[i])); |
|
|
|
svg.selectAll("circle") |
|
.attr("cx", function(d) { return d.x; }) |
|
.attr("cy", function(d) { return d.y; }); |
|
}); |
|
|
|
svg.on("mousemove", function() { |
|
var p1 = d3.mouse(this); |
|
root.px = p1[0]; |
|
root.py = p1[1]; |
|
force.resume(); |
|
}); |
|
|
|
function collide(node) { |
|
var r = node.radius + 16, |
|
nx1 = node.x - r, |
|
nx2 = node.x + r, |
|
ny1 = node.y - r, |
|
ny2 = node.y + r; |
|
return function(quad, x1, y1, x2, y2) { |
|
if (quad.point && (quad.point !== node)) { |
|
var x = node.x - quad.point.x, |
|
y = node.y - quad.point.y, |
|
l = Math.sqrt(x * x + y * y), |
|
r = node.radius + quad.point.radius; |
|
if (l < r) { |
|
l = (l - r) / l * .5; |
|
node.x -= x *= l; |
|
node.y -= y *= l; |
|
quad.point.x += x; |
|
quad.point.y += y; |
|
} |
|
} |
|
return x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1; |
|
}; |
|
} |
|
|
|
</script> |