From Mike Bostock's beeswarm block:
A static beeswarm plot implemented using d3-force’s collision constraint. A Voronoi overlay improves hover interaction.
license: gpl-3.0 |
From Mike Bostock's beeswarm block:
A static beeswarm plot implemented using d3-force’s collision constraint. A Voronoi overlay improves hover interaction.
<html> | |
<head> | |
<style> | |
body { | |
margin: 0; | |
} | |
.cells path { | |
fill: none; | |
pointer-events: all; | |
} | |
</style> | |
</head> | |
<body> | |
<script src="https://d3js.org/d3.v4.min.js"></script> | |
<script src="https://unpkg.com/[email protected]/build/d3-marcon.min.js"></script> | |
<script> | |
var alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNLOPQRSTUVWXYZ".split(""); | |
var m = d3.marcon().top(30).right(30).bottom(30).left(30).width(window.innerWidth).height(window.innerHeight); | |
m.render() | |
var width = m.innerWidth(), height = m.innerHeight(), svg = m.svg(), margin = {top: m.top(), left: m.left(), right: m.right()}; | |
var axisScale = d3.scaleLinear() | |
.rangeRound([0, width]) | |
.domain([-100, 100]); | |
var sizeScale = d3.scaleLinear() | |
.range([5, 50]); | |
function redraw(data) { | |
sizeScale.domain(d3.extent(data, function(d) { return d.size; })); | |
var simulation = d3.forceSimulation(data) | |
.force("x", d3.forceX(function(d) { return axisScale(d.change); }).strength(1)) | |
.force("y", d3.forceY(height / 2)) | |
.force("collide", d3.forceCollide(function(d, i){ return sizeScale(d.size) + 1; })) | |
.stop(); | |
for (var i = 0; i < 250; ++i) simulation.tick(); | |
svg.append("g") | |
.attr("class", "axis") | |
.attr("transform", "translate(0," + (height) + ")") | |
.call(d3.axisBottom(axisScale).ticks(20)); | |
// JOIN | |
var cell = svg.append("g") | |
.attr("class", "cells") | |
.selectAll("g").data(d3.voronoi() | |
.extent([[0, 0], [width, height]]) | |
.x(function(d) { return d.x; }) | |
.y(function(d) { return d.y; }) | |
.polygons(data)).enter().append("g") | |
.on("mouseover", function(d){ d3.select(this).attr("fill", "red"); }) | |
.on("mouseout", function(){ d3.select(this).attr("fill", "black"); }); | |
// voronoi | |
var voronoi = cell.append("path") | |
.attr("d", function(d) { return "M" + d.join("L") + "Z"; }); | |
// circle | |
cell.append("circle") | |
.attr("r", function(d) { return sizeScale(d.data.size); }) | |
.attr("cx", function(d) { return d.data.x; }) | |
.attr("cy", function(d) { return d.data.y; }) | |
} | |
redraw(randomizeData()); | |
/*FUNCTIONS*/ | |
function randomizeData(){ | |
return shuffle(alphabet).map(function(d){ return {name: d, change: random(-100, 100), size: random(1, 100)} }); | |
} | |
function random(min, max) { | |
return Math.floor(Math.random() * (max - min + 1)) + min; | |
} | |
function shuffle(array) { | |
var m = array.length, t, i; | |
// While there remain elements to shuffle… | |
while (m) { | |
// Pick a remaining element… | |
i = Math.floor(Math.random() * m--); | |
// And swap it with the current element. | |
t = array[m]; | |
array[m] = array[i]; | |
array[i] = t; | |
} | |
return array; | |
} | |
</script> | |
</body> | |
</html> |