我有基于
this tutorial的气泡图.
我已经使用以下代码启用了气泡的拖动.这使得个别圈子可以拖动,但是在拖动圈子时,其他圈子不会自动调整.我使用的是包圆算法,请让我知道这个算法是可能的.
这是我的拖拽代码:
// 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",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); }
解决方法
这里有一些代码可能会达到您要查找的效果.这主要归功于Mike Bostock的
Bubble Chart和
Collision Detection示例.
这使用D3的pack layout来初始定位节点.然后,force layout用于在拖动节点时“自动调整”其他圆.
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();