Built with blockbuilder.org
Based on the Flowing Data tutorial: https://flowingdata.com/2016/08/23/make-a-moving-bubbles-chart-to-show-clustering-and-distributions/
xxxxxxxxxx
<head>
<title>Partner Allocation</title>
<meta charset="utf-8">
</head>
<body>
<div id="main-wrapper">
<div id="chart"></div>
</div><!-- @end #main-wrapper -->
<script src="https://d3js.org/d3.v3.min.js"></script>
<script>
var margin = {top: 16, right: 0, bottom: 0, left: 0},
width = 950 - margin.left - margin.right,
height = 950 - margin.top - margin.bottom;
var node_radius = 5,
padding = 1,
cluster_padding = 10,
num_nodes = 132;
var svg = d3.select("#chart").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var foci = {
"toppos": { x: 475, y: 100, color: "#99CC00" },
"leftpos": { x: 225, y: 300, color: "#006699" },
"rightpos": { x: 725, y: 300, color: "#808080" },
"bottompos": { x: 475, y: 300, color: "#800080" },
};
var nodes = d3.range(0, num_nodes).map(function(o, i) {
return {
id: "node" + i,
x: foci.toppos.x + Math.random(),
y: foci.toppos.y + Math.random(),
radius: node_radius,
choice: "toppos",
}
});
var force = d3.layout.force()
.nodes(nodes)
.size([width, height])
.gravity(0)
.charge(0.2)
.friction(0.92)
.on("tick", tick)
.start();
var circle = svg.selectAll("circle")
.data(nodes)
.enter().append("circle")
.attr("id", function(d) { return d.id; })
.attr("class", "node");
circle.transition()
.delay(function(d,i) { return i * 5; })
.attrTween("r", function(d) {
var i = d3.interpolate(0, d.radius);
return function(t) { return d.radius = i(t); };
});
svg.append("text")
.attr("x", 230)
.attr("y", 375)
.attr("text-anchor", "middle")
.style("font-size", "16px")
.style("font-family","sans-serif")
.text("Maths Tutoring");
svg.append("text")
.attr("x", 475)
.attr("y", 375)
.attr("text-anchor", "middle")
.style("font-size", "16px")
.style("font-family","sans-serif")
.text("Counselling");
svg.append("text")
.attr("x", 740)
.attr("y", 375)
.attr("text-anchor", "middle")
.style("font-size", "16px")
.style("font-family","sans-serif")
.text("English tutoring");
svg.append("rect")
.attr("x", 740)
.attr("y", 50)
.attr("width", 10)
.attr("height",10)
.style("fill","#99CC00");
svg.append("text")
.attr("x", 810)
.attr("y", 59)
.attr("text-anchor", "middle")
.style("font-size", "10px")
.style("font-family","sans-serif")
.text("Needs light touch support")
svg.append("rect")
.attr("x", 740)
.attr("y", 70)
.attr("width", 10)
.attr("height",10)
.style("fill","#006699");
svg.append("text")
.attr("x", 801)
.attr("y", 79)
.attr("text-anchor", "middle")
.style("font-size", "10px")
.style("font-family","sans-serif")
.text("Needs Maths support")
svg.append("rect")
.attr("x", 740)
.attr("y", 90)
.attr("width", 10)
.attr("height",10)
.style("fill","#808080");
svg.append("text")
.attr("x", 802)
.attr("y", 99)
.attr("text-anchor", "middle")
.style("font-size", "10px")
.style("font-family","sans-serif")
.text("Needs English support")
svg.append("rect")
.attr("x", 740)
.attr("y", 110)
.attr("width", 10)
.attr("height",10)
.style("fill","#800080");
svg.append("text")
.attr("x", 816)
.attr("y", 118)
.attr("text-anchor", "middle")
.style("font-size", "10px")
.style("font-family","sans-serif")
.text("Needs mental health support")
var timeout;
function timer() {
var choices = d3.keys(foci);
var foci_index = Math.floor(Math.random() * choices.length );
var choice = d3.keys(foci)[foci_index];
var random_index = Math.floor( Math.random() * nodes.length );
nodes[random_index].choice = choice;
force.resume();
timeout = setTimeout(timer, 100);
}
timeout = setTimeout(timer, 400);
function tick(e) {
circle
.each(gravity(.051 * e.alpha))
.each(collide(.5))
.style("fill", function(d) { return foci[d.choice].color; })
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
}
function gravity(alpha) {
return function(d) {
d.y += (foci[d.choice].y - d.y) * alpha;
d.x += (foci[d.choice].x - d.x) * alpha;
};
}
function collide(alpha) {
var quadtree = d3.geom.quadtree(nodes);
return function(d) {
var r = d.radius + node_radius + Math.max(padding, cluster_padding),
nx1 = d.x - r,
nx2 = d.x + r,
ny1 = d.y - r,
ny2 = d.y + r;
quadtree.visit(function(quad, x1, y1, x2, y2) {
if (quad.point && (quad.point !== d)) {
var x = d.x - quad.point.x,
y = d.y - quad.point.y,
l = Math.sqrt(x * x + y * y),
r = d.radius + quad.point.radius + (d.choice === quad.point.choice ? padding : cluster_padding);
if (l < r) {
l = (l - r) / l * alpha;
d.x -= x *= l;
d.y -= y *= l;
quad.point.x += x;
quad.point.y += y;
}
}
return x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1;
});
};
};
</script>
</body>
Modified http://d3js.org/d3.v3.min.js to a secure url
https://d3js.org/d3.v3.min.js