将文本添加到矩形中的圆圈

时间:2015-05-06 23:48:48

标签: javascript svg d3.js packing

我正在尝试在矩形div中绘制圆圈。我已经按照问题13339615的建议(我使用的答案也在fiddle中提供,这很有效。

然而,作为d3的全新,我无法弄清楚如何标记圆圈。我基本上想要重新创建的内容类似于此article中的可视化。

我尝试了以下对小提琴的修改:

var bubbles = bubbleGroup.selectAll("circle") .data(data)
.enter() .append("circle") .append("text").attr("dy", ".3em") .style("text-anchor", "middle").text("test");

但这打破了可视化。

关注问题13615381我也尝试过这样的事情:

var bubbles = bubbleGroup.selectAll("circle") .data(data)
.enter() .append("circle"); bubbleGroup.append("text") .attr("dx", function(d){return -20}) .text(function(d){return "test"})

但文本不显示。我成像的代码应该是这些的一些变化,但我无法弄明白。

谢谢!

2 个答案:

答案 0 :(得分:0)

通过将circletext放在g内并调整g css-transform来修复。

JSFiddle

var bubbles = bubbleGroup.selectAll("g")
    .data(data)          
    .enter().append("g").attr("class","gBubble");

bubbles.append("circle")
    .on("mouseover",function(){ 
        $(this).attr("cursor","pointer")
    })
    .on("click",function(){alert("clicked")});

bubbles.append("text").text(function(d){return d.name;}).style("opacity","1");

答案 1 :(得分:0)



(function() {
    //D3 program to fit circles of different sizes 
    //in a rectangle of fixed aspect ratio
    //as tightly as reasonable.
    //
    //By Amelia Bellamy-Royds, in response to 
    //http://stackoverflow.com/questions/13339615/packing-different-sized-circles-into-rectangle-d3-js
    
    //Based on Mike Bostock's
    //"http://bl.ocks.org/mbostock/7882658" example:
    //http://bl.ocks.org/mbostock/7882658
    
//parameters//
var N = 25; //number of nodes
var sortOrder = -1; 
   //>0 for ascending, <0 for descending, 0 for no sort 
    
//create data array//
var data = [], i = N;
var randNorm = d3.random.normal(1,0.6);
while(i--) data.push({ 
    "size": Math.max(randNorm(), 0.1) });
    //circle area will be proportional to size  
    
var dataMax = d3.max(data, function(d){return d.size;});     
var totalSize = d3.sum(data, function(d){return d.size;});
    
//________________//
    
//Set up SVG and rectangle//   
var svg = d3.select("svg");
var digits = /(\d*)/;
var margin = 50; //space in pixels from edges of SVG
var padding = 4; //space in pixels between circles

var svgStyles = window.getComputedStyle(svg.node());
var width = parseFloat(svgStyles["width"]) - 2*margin;
var height = parseFloat(svgStyles["height"]) - 2*margin;
    
var usableArea = Math.PI*
    Math.pow( Math.min(width,height)/2 ,2)*0.667;
var scaleFactor = Math.sqrt(usableArea)/
        Math.sqrt(totalSize)/Math.PI;
var rScale = d3.scale.sqrt()  
        //make radius proportional to square root of data r
        .domain([0, dataMax]) //data range
        .range([0,  Math.sqrt(dataMax)*scaleFactor]);
//The rScale range will be adjusted as necessary
//during packing.
//The initial value is based on scaling such that the total
//area of the circles is 2/3 the area of the largest circle
//you can draw within the box.
    
/*
    console.log("Dimensions: ", [height, width]);
    console.log("area", width*height);
    console.log("Usable area: ", usableArea);
    console.log("TotalSize: ", totalSize);
    console.log("Initial Scale: ", scaleFactor);
    console.log("RScale: ",rScale.domain(), rScale.range());
console.log("r(1)", rScale(1) );
//  */  

var box = svg.append("rect")
            .attr({ "height": height, "width":width,
                   "x":margin, "y":margin,
                   "class":"box"
            });
    
var bubbleGroup = svg.append("g")
        .attr("class", "bubbles")
        .attr("transform", 
              "translate(" + [margin,margin] + ")");
    
    

//__Initialize layout objects__//
  
// Use the pack layout to initialize node positions:
d3.layout.pack()
    .sort((
        sortOrder?
            ( (sortOrder<0)? 
                function(a,b){return b.size - a.size;} : //descending
                function(a,b){return a.size - b.size;} ) : //ascending
            function(a,b){return 0;} //no sort
        ))
    .size([width/scaleFactor, height/scaleFactor])
    .value(function(d) { return d.size; })
    .nodes({children:data});
    
//Use the force layout to optimize:
var force = d3.layout.force()
    .nodes(data)
    .size([width/scaleFactor, height/scaleFactor])
    .gravity(.5)
    .charge(0) //don't repel
    .on("tick", updateBubbles);
    
    
    //Create circles!//
var bubbles = bubbleGroup.selectAll("circle")
    .data(data)          
    .enter()
        .append("circle");

    //Create text
var text = bubbleGroup.selectAll("text")
        .data(data).enter().append("text")
        .attr("dy", function(d){
            return d.y;
        }) 
        .attr("dx", function(d){
            return d.x;
        }).style("text-anchor", "middle").text("test");
  
    
// Create a function for this tick round,
// with a new quadtree to detect collisions 
// between a given data element and all
// others in the layout, or the walls of the box.
    
    
//keep track of max and min positions from the quadtree
var bubbleExtent;
function collide(alpha) {
  var quadtree = d3.geom.quadtree(data);
  var maxRadius = Math.sqrt(dataMax);
  var scaledPadding = padding/scaleFactor;
  var boxWidth = width/scaleFactor;
  var boxHeight = height/scaleFactor;
    
    //re-set max/min values to min=+infinity, max=-infinity:
  bubbleExtent = [[Infinity, Infinity],[-Infinity, -Infinity]];
    
  return function(d) {
      
      //check if it is pushing out of box:
    var r = Math.sqrt(d.size) + scaledPadding,
        nx1 = d.x - r,
        nx2 = d.x + r,
        ny1 = d.y - r,
        ny2 = d.y + r;
      
      if (nx1 < 0) {
           d.x = r;
      }
      if (nx2 > boxWidth) {
           d.x = boxWidth - r;
      }
      if (ny1 < 0) {
           d.y = r;
      }
      if (ny2 > boxHeight) {
           d.y = boxHeight - r;
      }
      
      
    //check for collisions
    r = r + maxRadius, 
        //radius to center of any possible conflicting nodes
        nx1 = d.x - r,
        nx2 = d.x + r,
        ny1 = d.y - r,
        ny2 = d.y + r;
      
    quadtree.visit(function(quad, x1, y1, x2, y2) {
      if (quad.point && (quad.point !== d)) {
        var x = d.x - quad.point.x,
            y = d.y - quad.point.y,
            l = Math.sqrt(x * x + y * y),
            r = Math.sqrt(d.size) + Math.sqrt(quad.point.size)
                    + scaledPadding;
        if (l < r) {
          l = (l - r) / l * alpha;
          d.x -= x *= l;
          d.y -= y *= l;
          quad.point.x += x;
          quad.point.y += y;
        }
      }
      return x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1;
    });
      
    //update max and min
    r = r-maxRadius; //return to radius for just this node
    bubbleExtent[0][0] = Math.min(bubbleExtent[0][0], 
                                  d.x - r);
    bubbleExtent[0][1] = Math.min(bubbleExtent[0][1], 
                                  d.y - r);
    bubbleExtent[1][0] = Math.max(bubbleExtent[1][0], 
                                  d.x + r);
    bubbleExtent[1][1] = Math.max(bubbleExtent[1][1], 
                                  d.y + r);
      
  };
}  
    
function updateBubbles() {
    
    bubbles
        .each( collide(0.5) ); //check for collisions   
    text.each(collide(0.5));//check for text collisions
    
    //update the scale to squeeze in the box 
    //to match the current extent of the bubbles
    var bubbleWidth = bubbleExtent[1][0] - bubbleExtent[0][0];
    var bubbleHeight = bubbleExtent[1][1] - bubbleExtent[0][1];
    
    scaleFactor = (height/bubbleHeight +
                           width/bubbleWidth)/2; //average
    /*
    console.log("Box dimensions:", [height, width]);
    console.log("Bubble dimensions:", [bubbleHeight, bubbleWidth]);
    console.log("ScaledBubble:", [scaleFactor*bubbleHeight,
                                 scaleFactor*bubbleWidth]);
    //*/
    
    rScale
        .range([0,  Math.sqrt(dataMax)*scaleFactor]);
    
    //shift the bubble cluster to the top left of the box
    bubbles
        .each( function(d){
            d.x -= bubbleExtent[0][0];
            d.y -= bubbleExtent[0][1];
        });
       
    //update positions and size according to current scale:
    bubbles
        .attr("r", function(d){return rScale(d.size);} )
        .attr("cx", function(d){return scaleFactor*d.x;})
        .attr("cy", function(d){return scaleFactor*d.y;});
    
    text
    .attr("dy", function(d){
        return (scaleFactor*d.y)+4;
    })
    .attr("dx", function(d){
        return scaleFactor*d.x*2;
    });
    
}

force.start();
    
})();
&#13;
rect.box {
    fill:none; 
    stroke:royalblue;
    stroke-width:5; 
    shape-rendering: crispEdges;
}
g.bubbles circle {
    fill:rgba(255,0,64,0.5); 
    stroke:rgb(255,0,64);
    stroke-width:3;
}
g.bubbles text {
    fill:royalblue;
    font-family:sans-serif;
    text-anchor:middle;
    alignment-baseline:middle;
    opacity:1;
    pointer-events:all;
    transition:1s;
}
g.bubbles text:hover {
    opacity:1;
}
&#13;
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<svg height=500 width=500></svg>
&#13;
&#13;
&#13;

我已经在圆圈中添加了文字,也包含了碰撞行为。 最初文本是不可见的,因为在他们提到的CSS中如下所示

g.bubbles text {
    fill:royalblue;
    font-family:sans-serif;
    text-anchor:middle;
    alignment-baseline:middle;
    opacity:0;//See this value, this makes text to invisible
    pointer-events:all;
    transition:1s;
}
g.bubbles text:hover {
    opacity:1;
} 

在我的片段中,我通过将其不透明度改为1来将其更改为可见。

并更新了fiddle