以下玩具问题说明了我的问题。一张藏宝图说,我有一系列的“位置”。数组中的每个项目(例如怪物或宝藏)都可以存在于地图上的多个位置。例如

locations = [
  {name:'treasure', color: 'blue', coords:[[100,100], [200,300]]},
  {name:'monsters', color: 'red', coords:[[100,150], [220,420], [50,50]]}
]


现在,我想使用D3绘制这些图形。不良/幼稚的方法(有效-see here for fiddle)如下所示:

for location in locations
  for coords in location.coords
    svg.append('circle')
      .attr('cx', coords[0])
      .attr('cy', coords[1])
      .attr('r', 8)
      .style('fill', location.color)
      .datum(location)


但是,当我修改数据的内容时,我不想每次都运行此幼稚的代码。看来使用data()和enter()是做到这一点的“正确”方法,但我无法弄清楚它如何与子坐标一起工作。例如

svg.selectAll('circle').data(locations).enter().append('circle')
  .attr('cx', (d) -> d.coords[0][0])
  .attr('cy', (d) -> d.coords[0][1])
  .attr('r', 8)
  .style('fill', (d) -> d.color)


这很好用,但是如您所见,我只在每个位置都打印所有位置的第一坐标。我怀疑这样做的唯一方法是展平我的数据数组,因此总共有5个条目-3个怪物和2个宝藏物品。

只是想知道是否有一种方法可以使用D3更好地处理此问题。

最佳答案

为此,您需要nested selections。这个想法是,您可以附加多个元素,而不是每个数据项附加单个元素。在代码中,它看起来像这样:

// append a `g` element for each data item to hold the circles
var groups = svg.selectAll("g.circle").data(locations)
   .enter().append("g").attr("class", "circle");

// now select all the circles in each group and append the new ones
groups.selectAll("circle")
   // the d in this function references a single data item in locations
   .data(function(d) { return d.coords; })
   .enter().append("circle")
   .attr("cx", function(d) { return d[0]; })
   .attr("cy", function(d) { return d[1]; });


它对更新和退出选择的作用相同。

08-25 19:53