I have bubble chart based on this tutorial.
I have enabled dragging of bubbles with following code. This makes individual circles draggable, but while dragging a circle other circles don't get auto adjusted. I am using pack circle algorithm, please let me know is that possible with this algorithm.
This is my code for dragging:
// draggable
if(this.dragging){
var drag = d3.behavior.drag()
.on("drag", function( d, i) {
var selection = d3.selectAll( '.selected');
if( selection[0].indexOf( this)==-1) {
selection.classed( "selected", false);
selection = d3.select( this);
selection.classed( "selected", true);
}
selection.attr("transform", function( d, i) {
d.x += d3.event.dx;
d.y += d3.event.dy;
return "translate(" + [ d.x,d.y ] + ")"
})
// reappend dragged element as last
// so that its stays on top
this.parentNode.appendChild( this);
d3.event.sourceEvent.stopPropagation();
});
node.call( drag);
}
Here's some code that might achieve the effect you're looking for. It's largely attributable to Mike Bostock's Bubble Chart and Collision Detection examples.
This uses D3's pack layout to initially position the nodes. Then a force layout is used to "auto adjust" the other circles when dragging a node.
Working example at bl.ocks.org
var width = 900;
var height = 500;
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var nodes = d3.range(128).map(function () { return {radius: Math.random() * 16 + 8}; });
var nodesCopy = $.extend(true, [], nodes);
function dblclick(d) {
d3.select(this).classed("fixed", d.fixed = false);
}
function dragstart(d) {
d3.select(this).classed("fixed", d.fixed = true);
}
function collide(node) {
var r = node.radius + 16;
var nx1 = node.x - r;
var nx2 = node.x + r;
var ny1 = node.y - r;
var ny2 = node.y + r;
return function (quad, x1, y1, x2, y2) {
if (quad.point && (quad.point !== node)) {
var x = node.x - quad.point.x;
var y = node.y - quad.point.y;
var l = Math.sqrt(x * x + y * y);
var npr = node.radius + quad.point.radius;
if (l < npr) {
l = (l - npr) / l * 0.5;
x *= l;
node.x -= x;
y *= l;
node.y -= y;
quad.point.x += x;
quad.point.y += y;
}
}
return x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1;
};
}
function packup() {
var pack = d3.layout.pack()
.sort(null)
.size([width, height])
.padding(0)
.value(function (d) { return d.radius; });
svg.selectAll(".node")
.data(pack.nodes({"children": nodes})
.filter(function (d) { return !d.children; }))
.enter().append("circle")
.attr("r", function (d) { return d.radius; })
.attr("cx", function (d) { return d.x; })
.attr("cy", function (d) { return d.y; });
}
function forceup() {
var force = d3.layout.force()
.nodes(nodes)
.gravity(0.05)
.charge(0)
.size([width, height])
.start();
var drag = force.drag().on("dragstart", dragstart);
force.on("tick", function () {
var q = d3.geom.quadtree(nodes);
var i = 0;
var n = nodes.length;
while (++i < n) {
q.visit(collide(nodes[i]));
}
svg.selectAll("circle")
.attr("cx", function (d) { return d.x; })
.attr("cy", function (d) { return d.y; });
});
d3.selectAll("circle")
.on("dblclick", dblclick)
.call(drag);
}
function reset() {
svg.selectAll("*").remove();
nodes = $.extend(true, [], nodesCopy);
packup();
forceup();
}
packup();
forceup();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With