如何在d3气泡图中操纵圆圈?

时间:2015-08-28 09:02:22

标签: javascript d3.js

对不起这个愚蠢的问题,我只是一个可怜的d3新手...

我在JSFiddle上有以下demo bubble diagram:我想要实现的是每当我点击它们时增加圆的半径,然后相应地调整包布局。

为方便起见,这是JSFiddle中的代码:

text {
  font: 20px sans-serif;
}
<!DOCTYPE html>

<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
<script>
        
var root = {
    "name": "Root",
    "children": [
        {
            "name": "Leaf One",
            "children": null,
            "size": 1
        },
        {
            "name": "Leaf Two",
            "children": null,
            "size": 1
        },
        {
            "name": "Leaf Three",
            "children": null,
            "size": 1
        }
    ],
    "size": 1
};

    
var diameter = 400,
    format = d3.format(",d"),
    color = d3.scale.category20c();

var bubble = d3.layout.pack()
    .sort(null)
    .size([diameter, diameter])
    .padding(1.5);

var svg = d3.select("body").append("svg")
    .attr("width", diameter)
    .attr("height", diameter)
    .attr("class", "bubble");

// not needed in JSfiddle, data is hard-coded:
// d3.json("data.json", function(error, root) {
//   if (error) throw error;

  var node = svg.selectAll(".node")
      .data(bubble.nodes(classes(root))
      .filter(function(d) { return !d.children; }))
    .enter().append("g")
      .attr("class", "node")
      .attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });

  node.append("title")
      .text(function(d) { return d.className + ": " + format(d.value); });

  node.append("circle")
      .attr("r", function(d) { return d.r; })
      .style("fill", function(d) { return color(d.packageName); });

  node.append("text")
      .attr("dy", ".3em")
      .style("text-anchor", "middle")
      .text(function(d) { return d.className.substring(0, d.r / 3); });
    
    node.on("click", function(e, i){
  		var circle = svg.select("circle");
  		circle.attr("value", function(d) { 
  			d.value += 1;
  			return d.value; 
  		});
  		circle.attr("r", function(d) { 
  			d.r += 1.0;
  			return d.r; 
  		});
      });
// });

// Returns a flattened hierarchy containing all leaf nodes under the root.
function classes(root) {
  var classes = [];

  function recurse(name, node) {
    if (node.children) node.children.forEach(function(child) { recurse(node.name, child); });
    else classes.push({packageName: name, className: node.name, value: node.size});
  }

  recurse(null, root);
  return {children: classes};
}

d3.select(self.frameElement).style("height", diameter + "px");

</script>

我试图通过node.on("click", ...)方法做到这一点,但我得到了一些卡住,因为修改后的圈子总是第一个:选择我点击的圈子的最佳方式是什么?

此外,在修改圆半径后,如何强制d3包布局刷新?

1 个答案:

答案 0 :(得分:1)

首先,你必须记住,在事件处理程序中,this绑定到当前的DOM元素(在完整的情况下 - <g>)。因此,您可以选择点击<g> d3.select(this),然后选择以下圈子:

var circle = d3.select(this).select("circle");

(只需按svg.select("circle")选择DOM中的第一个圆圈,它总是恰好相同的一个圆圈)

为了刷新布局,您必须更新基础数据,重新计算布局,重新计算数据连接和更新值:

// store the underlying data in a value
var classedRoot = classes(root);

// ...your node creating code here

node.on("click", function(d) {
   // update the data in classedRoot
   d.value += 1;

   // recalculate layout
   var data = bubble.nodes(classedRoot);

   // recompute data join
   node.data(data, function(d) { return d.className; }) // use key function
      // update values
      .attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; })
      .select("circle")
      .attr("r", function(d) { return d.r });
});

请注意,此处使用key function可以将更新的数据与已存在的圈子正确绑定。您使用的值对于所有圈子必须是唯一的。我在这里使用了 className ,但如果它不是唯一的,那么您将不得不使用某种ID。

此处已更新jsFiddle