Skip to content

Instantly share code, notes, and snippets.

@milroc
Last active December 16, 2015 17:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save milroc/5468998 to your computer and use it in GitHub Desktop.
Save milroc/5468998 to your computer and use it in GitHub Desktop.
Bubble Chart
<!DOCTYPE html>
<head>
<meta charset="utf-8">
<link type="text/css" rel="stylesheet" href="style.css"/>
</head>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="src.js"></script>
<div id="chart"></div>
<script>
var data = [];
var categories = ["A", "B", "C", "D", "E", "F"];
for (var i = 0; i < 7; i++) {
data.push({
radius: i,
color: categories[Math.floor(Math.random()*categories.length)],
});
}
var ext = d3.extent(data, function(d) { return d.radius; });
var bubbles = charts.bubble()
.width(500)
.height(500)
// .rValue(30)
.r(d3.scale.sqrt().domain(ext).range([5,30]))
.colors(d3.scale.category20())
.outline(.8)
.force(d3.layout.force()
.gravity(1)
// .charge(function(d) { return -Math.pow(d.r,2)/6; })
);
// d3.timer(function() { bubbles.force().resume(); });
bubbles.nameValue(bubbles.colorValue())
.typeValue(bubbles.colorValue());
d3.select("#chart")
.datum(data)
.call(bubbles);
var inter = 0;
setInterval(function() {
// ENTER
if (inter === 0) {
data.push({
radius: Math.random()*25+25,
color: "F"
});
inter++;
}
// UPDATE
else if (inter === 1) {
data[0].radius = 0;
data[0].color = "NEW WORD";
inter++;
}
// DELETE
else {
data.pop();
inter = 0;
}
d3.select("#chart")
.datum(data)
.call(bubbles);
}, 3000);
</script>
</body>
</html>
var charts = {};
charts.bubble = function() {
if (!bubble.id) bubble.id = 0;
// overall size info
var width = 800,
height = 600,
margin = {top: 0, bottom: 0, left: 0, right: 0},
// scales
colors = d3.scale.category20c(),
r = d3.scale.sqrt(),
// accessors
rValue = function(d) { return d.radius; },
colorValue = function(d) { return d.color; },
nameValue = function(d) { return d.name; },
typeValue = function(d) { return d.type; },
// chart enhancements
center = null,
centers = null,
formatNumber = d3.format(",d"),
outline = 0,
force = d3.layout.force(),
called = false,
id = bubble.id++;
var div = d3.select("body").append("div")
.attr("class", "tooltip bubble")
.style("opacity", 0)
.attr("id", "bubble-tooltip-"+id);
function bubble(selection) {
selection.each(function(data) {
var w = width - margin.left - margin.right,
h = height - margin.top - margin.bottom;
if (center === null) center = {x: w/2, y: h/2};
data = data.map(function(d, i) {
return {
r: r(rValue.call(data, d, i)),
color: colorValue.call(data, d, i),
name: nameValue.call(data, d, i),
type: typeValue.call(data, d, i),
};
});
// if (!called) {
force
.nodes(data)
.size([w,h]);
// called = true;
// }
force.start();
console.log("LOG:",data);
var svg = d3.select(this)
.selectAll("svg")
.data([data]);
var svgEnter = svg.enter()
.append("svg")
.attr("width", w)
.attr("height", h)
.append("g");
var g = svg.select('g'),
node = g.selectAll(".node");
var nodes = node.data(data);
nodes.enter()
// .transition()
// .duration(500)
.append("circle")
.attr("class", "node")
.on("mouseover", function(d) {
div.transition()
.duration(200)
.style("opacity", .9);
div.html(d.name + "<br/>" + d.r.toFixed(2))
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 28) + "px");
})
.on("mouseout", function(d) {
div.transition()
.duration(500)
.style("opacity", 0);
});
nodes
// .transition()
// .duration(500)
.attr("r", function(d) { return d.r; })
.style("fill", function(d) { return colors(d.color); })
.style("stroke", function(d) { return d3.rgb(colors(d.color)).darker(outline); })
.call(force.drag);
nodes.exit()
.transition()
.duration(500)
.attr("r", 0)
.style("opacity", 0)
.remove();
force.on("tick", function(e) {
//TODO centers code
var q = d3.geom.quadtree(data),
i = 0,
n = data.length;
while (++i < n) {
q.visit(collide(data[i]));
}
nodes
.attr("cx", function(d) { return d.x = Math.max(d.r, Math.min(w - d.r, d.x)); })
.attr("cy", function(d) { return d.y = Math.max(d.r, Math.min(h - d.r, d.y)); });
});
});
}
function collide(node) {
var r = node.r + 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.r + quad.point.r;
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;
};
}
// overall size info
bubble.width = function(_) {
if (!arguments.length) return width;
width = _;
return bubble;
};
bubble.height = function(_) {
if (!arguments.length) return height;
height = _;
return bubble;
};
bubble.margin = function(_) {
if (!arguments.length) return margin;
margin = _;
return bubble;
};
// scales
bubble.colors = function(_) {
if (!arguments.length) return colors;
colors = _;
return bubble;
};
bubble.r = function(_) {
if (!arguments.length) return r;
r = _;
return bubble;
};
// accessors
bubble.colorValue = function(_) {
if (!arguments.length) return colorValue;
colorValue = d3.functor(_);
return bubble;
};
bubble.rValue = function(_) {
if (!arguments.length) return rValue;
rValue = d3.functor(_);
return bubble;
};
bubble.nameValue = function(_) {
if (!arguments.length) return nameValue;
nameValue = d3.functor(_);
return bubble;
};
bubble.typeValue = function(_) {
if (!arguments.length) return typeValue;
typeValue = d3.functor(_);
return bubble;
};
// chart enhancements
bubble.center = function(_) {
if (!arguments.length) return center;
center = _;
return bubble;
};
bubble.centers = function(_) {
if (!arguments.length) return centers;
centers = _;
return bubble;
};
bubble.minRadius = function(_) {
if (!arguments.length) return minRadius;
minRadius = _;
return bubble;
};
bubble.formatNumber = function(_) {
if (!arguments.length) return formatNumber;
formatNumber = _;
return bubble;
};
bubble.outline = function(_) {
if (!arguments.length) return outline;
outline = _;
return bubble;
};
bubble.force = function(_) {
if (!arguments.length) return force;
force = _;
return bubble;
};
return bubble;
};
body {
font: 10px sans-serif;
color: #fff;
background-color: #333;
}
svg {
fill: solid 1px #ccc;
font: 10px sans-serif;
}
div.tooltip {
position: absolute;
text-align: center;
width: 60px;
height: 28px;
padding: 2px;
font: 12px sans-serif;
background: #111;
border: 0px;
border-radius: 8px;
pointer-events: none;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment