I'm trying to create a dynamic bubble chart where bubbles are added/removed based on selected filters. Eventually the data is going to be called from my server through Ajax, but currently I just have a set array of objects that i add to or remove from with buttons.
I have two problems that i cannot figure out at the moment.
- When I click the Add Bubble button a bubble is added but the it doesn't appear to be added to the force simulation?
- When i click the Remove Bubble button the bubble with the data that is removed isn't being removed with exit.remove()
The full code can be found here https://jsfiddle.net/codered1988/s2z63crL/
function drawBubbles() {
let root = d3.hierarchy({ children: data })
.sum(d => d.value);
let nodes = pack(root).leaves().map(node => {
const data = node.data;
return {
x: centerX + (node.x - centerX) * 3,
y: centerY + (node.y - centerY) * 3,
r: 10, // for tweening
radius: node.r, //original radius
id: data.id,
type: data.type,
name: data.name,
value: data.value,
}
});
simulation.nodes(nodes).on('tick', ticked);
node = svg.selectAll('.node')
.data(nodes, d => d.id)
.join(
function(n){ // enter
enter = n.append('g')
.attr('class', 'node')
.call(d3.drag()
.on('start', (event, d) => {
if (!event.active) simulation.alphaTarget(0.2).restart();
d.fx = d.x;
d.fy = d.y;
})
.on('drag', (event, d) => {
d.fx = event.x;
d.fy = event.y;
})
.on('end', (event, d) => {
if (!event.active) simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
}))
// Create Circles
enter.append('circle')
.attr('id', d => d.id)
.attr('r', d => d.radius)
//.style('fill', d => scaleColor(d.type))
.style('fill', d => fill(d.type))
.attr("stroke", "black")
.style("stroke-width", 2)
.transition().duration(2000).ease(d3.easeElasticOut)
.tween('circleIn', (d) => {
console.log('tween');
let i = d3.interpolateNumber(d.r, d.radius);
return (t) => {
d.r = i(t);
simulation.force('collide', forceCollide);
}
})
enter.append('clipPath')
.attr('id', d => `clip-${d.id}`)
.append('use')
.attr('xlink:href', d => `#${d.id}`);
// display text as circle icon
enter.filter(d => !String(d.name).includes('img/'))
.append('text')
.classed('node-icon', true)
.attr('clip-path', d => `url(#clip-${d.id})`)
.selectAll('tspan')
.data(d => d.name.split(';'))
.enter()
.append('tspan')
.attr('x', 0)
.attr('y',(d, i, nodes) => (13 + (i - nodes.length / 2 - 0.5) * 10))
.text(name => name);
return enter;
},
update => update
.attr("fill", "black")
.attr("y", 0)
.call(update => update.transition(t)
.attr("x", (d, i) => i * 16)),
exit => exit
.attr("fill", "brown")
.call(exit => exit.transition(t)
.attr("y", 30)
.remove())
);
//simulation.nodes(nodes).alpha(1).restart();
}