如何使用画笔选择D3.js中多个组元素中包含的圆圈?

时间:2015-10-02 16:04:18

标签: javascript d3.js svg

我正在制作散点图。图中的圆圈包含在多个组元素中。

我正在尝试使用brush来选择落入画笔范围的圆圈。但是brushend函数中的参数为单个组提供数据。

如何更改我的代码,以便brushend函数知道哪些组被刷过,而不必迭代遍历图中的所有圆圈?

<!DOCTYPE html>
<meta charset="utf-8">
<head>
<style>

#plot {
  border-style: solid;
  border-width: 4px;
  border-color: lightgrey;
  display: inline-block;
  line-height: 0;
}

.extent {
  fill: grey;
  fill-opacity: 0.5;
  stroke: black;
  stroke-width: 1px;
}

</style>
<script type="text/javascript" src="d3.js"></script>
</head>

<body>
<div id=plot>  
<script>

(function() {
  var data = [];

  for (i = 0; i < 100; i++) {
    var x = Math.floor(Math.random() * 100) + 1;
    var y = Math.floor(Math.random() * 100) + 1;
    var c = Math.random();

    if (c < 0.33) c = "red";
    else if (c < 0.66) c = "green";
    else c = "blue";

    data.push({x: x, y: y, c: c});
  }

  data = d3.nest()
      .key(function(d) { return d.c })
      .entries(data);

  var svg = d3.select("#plot").append("svg")
      .attr("width", 400)
      .attr("height", 400)
      .selectAll("scatter")
      .data(data)
      .enter().append("g")
      .each(scatter);

  var xScale = d3.scale.linear()
      .domain([0, 100])
      .range([0, 400]);

  var yScale = d3.scale.linear()
      .domain([0, 100])
      .range([400, 0]);

  var brush = d3.svg.brush()
      .x(xScale)
      .y(yScale)
      .on("brushend", brushend);

  svg.call(brush);

  function brushend(d) {
  }

})();

function scatter(d) {
  var g = d3.select(this);

  var xScale = d3.scale.linear()
      .domain([0, 100])
      .range([0, 400]);

  var yScale = d3.scale.linear()
      .domain([0, 100])
      .range([400, 0]);

  g.selectAll("circle")
      .data(d.values)
      .enter().append("circle")
      .attr("r", 4)
      .attr("cx", function(d) { return xScale(d.x) })
      .attr("cy", function(d) { return yScale(d.y) })
      .attr("fill", function(d) { return d.c });
}

</script>
</div>
</body>
</html>

编辑:我意识到画笔不能跨组元素工作,所以我接受了一个不使用传递给brushend函数的参数的答案。

1 个答案:

答案 0 :(得分:1)

您可以通过稍微更改brushend功能来执行此操作。 brush.extent()函数返回画笔选择的障碍,您需要做的就是选择那些障碍之间的所有障碍。

&#13;
&#13;
  function brushend(d) {
      var e = brush.extent();
  svg.selectAll("circle")
  	.classed("hidden", function(d) {
      if(e[0][0] < d.x && d.x < e[1][0]
          && e[0][1] < d.y && d.y < e[1][1]) {
                console.log(d);
      }
      return e[0][0] < d.x && d.x < e[1][0]
          && e[0][1] < d.y && d.y < e[1][1];  	});
  } 
})();
&#13;
&#13;
&#13;

我知道这不是解决这个问题最优雅的方法,但我会寻找一种更好的方法来选择带有该功能的圆圈,然后编辑我的答案。我希望现在这个更改将允许您将选择中的所有圈子记录到您的控制台。

修改 我找到了一个不需要将类应用于所有内容的解决方案,我非常确定它的工作原理应该如此。然而,由于brush.extent()和您的圈子cxcy属性的工作方式不同,因此它并不漂亮。在外面它应该看起来很好。

&#13;
&#13;
  function brushend(d) {
      var e = brush.extent();
  	  var circles = svg.selectAll("circle");
      var ae00 = e[0][0]*4; //The cx and cy values are 4 times as big
      var ae10 = e[1][0]*4;
      var ae01 = 400 - e[0][1]*4; //revert the y values, so 0 is at the top
      var ae11 = 400 - e[1][1]*4;
      for(var j = 0; j < 3; j ++) {
       for (var i = 0; i < circles[j].length; i++) {
      	if(ae00 < circles[j][i].cx.animVal.value && circles[j][i].cx.animVal.value < ae10 
           && ae01 > circles[j][i].cy.animVal.value && circles[j][i].cy.animVal.value > ae11) {
                console.log(circles[j][i]);
        }
      }
      }
  }
&#13;
&#13;
&#13;