2👍
just a vanilla JS, Chart.JS and D3 solution…
uses the D3 layout.pack algorithm (which seems to have been deprecated, so this is only a stop-gap) but … it calls the D3 packing routine to calculate x,y and scaled radius for each point and then extracts that into a data structure the ChartJS bubble chart can consume.
var w = document.getElementById("c").offsetWidth,
h = document.getElementById("c").offsetHeight;
// build random set of circles for the d3 packing
var data = {
"name": "root",
"children": []
}
for (var i = 1; i <= 15; i++) {
data["children"].push({
"name": i,
"size": Math.floor((Math.random() * 200) + 1)
})
}
// use D3 to pack into the canvas size, with a little bit of padding
var nodes = d3.layout.pack()
.value(function(d) {
return d.size;
})
.size([w, h])
.padding(4)
.nodes(data);
// Get rid of root node
nodes.shift();
// take the packed nodes data, and push to format ChartJS expects for bubbles
nodedata = [];
for (i = 0; i < nodes.length; i++) {
node = nodes[i]
nodedata.push({
"x": node.x,
"y": node.y,
"r": node.r
})
}
// create a bubble chart with no labels, border lines etc
var bubbleChart = new Chart(document.getElementById("cc"), {
type: 'bubble',
data: {
datasets: [{
data: nodedata,
backgroundColor: "rgba(212,121,212,1)"
}]
},
options: {
scales: {
x: {
ticks: {
display: false,
},
grid: {
display: false
},
border: {
display: false
}
},
y: {
display: false,
ticks: {
display: false,
},
grid: {
display: false,
},
border: {
display: false
}
}
},
maintainAspectRatio: false,
plugins: {
legend: {
display: false
},
}
}
});
#c {
border: 1px solid black;
width: 380px;
height: 280px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<div id="c"><canvas id="cc"></canvas></div>
Issues:
- sometimes we get circles slightly overlapping
- the packing is optimized to fill a circle, not efficiently use the full rectangle
- no center of gravity to improve the clustering
- as mentioned, relies on older D3 version
I suspect most of these issues could be addressed via a more sophisticated D3 force model application (allow it to run for a few ticks until the circles are positioned and then pass to the chart)
credit for the above D3 part of the solution to seliopou.
opened a new question based on this answer to see if anyone has a clever solution using forceSimulation features of D3.js
Source:stackexchange.com