Playing with the Beeswarm along a path approach, forked from 1wheel's block: beeswarm-path, which was suggested by Arman.
The 'square' path needs to start half way up the left hand side (50, 400) so the start and end of the path lines up with the final circle path.
It uses Flubber (https://github.com/veltman/flubber) to create a nice transition between the square and circle.
forked from tomshanley's block: beeswarm on square and circle path
forked from tomshanley's block: beeswarm on square and circle path
forked from tomshanley's block: beeswarm on square and circle path
xxxxxxxxxx
<meta charset='utf-8'>
<script src="https://unpkg.com/flubber@0.3.0"></script>
<style>
path {
fill: none;
stroke: #5675fe;
stroke-linecap: round;
stroke-width: 40;
opacity: .2;
}
body{
font-family: monaco, Consolas, 'Lucida Console', monospace;
margin: 0px;
}
</style>
<button type="button" onclick="changeShape()">Switch</button>
<div id='graph'></div>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
var r = 3
var w = 800, h = 800
var data = d3.range(300).map(x => {
var val = Math.random() * 400
return {val}
})
var colour = d3.scaleSequential(d3.interpolateRainbow)
.domain([0, d3.max(data, function(d){ return d.val })])
var svg = d3.select('#graph')
.html('')
.append('svg')
.attr("width", w)
.attr("height", h)
//square path starts at mid point on the left side
var square = 'M 50,400 L50,50 L750,50 L750,750 50,750 z'
var shape = 'square'
var squareToCircle = flubber.toCircle(square, 400, 400, 150)
var circleToSquare = flubber.fromCircle(400, 400, 150, square)
var path = svg.append('path')
.attr("d", square)
var node = path.node()
var scaleLength = d3.scaleLinear()
.domain([0, d3.max(data, function(d){ return d.val })])
.range([0, node.getTotalLength()])
var scaleX = function(d){
return node.getPointAtLength(scaleLength(d.val)).x
}
var scaleY = function(d){
return node.getPointAtLength(scaleLength(d.val)).y
}
var bees = svg.append("g").selectAll('.bee')
.data(data)
.enter()
.append('circle')
.attr("class", "bee")
.attr("r", r)
.style("fill", function(d){ return colour(d.val) })
updateSwarm(120);
function changeShape() {
path
.transition()
.duration(5000)
.attrTween("d", function(){
if (shape == 'square') {
shape = 'circle'
return squareToCircle;
} else {
shape = 'square'
return circleToSquare;
}
})
.tween("attr", function(){
return function(t) {
updateSwarm(5)
}
})
}
function updateSwarm(iterations) {
scaleLength.range([0, node.getTotalLength()])
var simulation = d3.forceSimulation(data)
.force('collide', d3.forceCollide(r + 1))
.force('x', d3.forceX(d => scaleX(d)))
.force('y', d3.forceY(d => scaleY(d)))
.stop()
for (var i = 0; i < iterations; ++i) simulation.tick()
bees
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
}
</script>
https://unpkg.com/flubber@0.3.0
https://d3js.org/d3.v4.min.js