D3:如何有条件地将 SVG 对象绑定到数据

D3: How to conditionally bind SVG objects to data?

本文关键字:对象 SVG 绑定 数据 有条件 D3      更新时间:2023-09-26

我这里有一个对象数组,我正在使用D3进行可视化。我将每个对象绑定到一个组元素,并向其附加一个依赖于某些对象属性的 SVG 图形,大致如下所示:

var iconGroups = zoomArea.selectAll("g.icons")
        .data(resources)
        .enter()
        .append("g")
var icons = iconGroups.append(function(d){
         if(d.type == "Apple"){
             return appleIcon;
         }else if(d.type == "Orange"){
             return orangeIcon;
         })

等。现在我想用一行来扩展其中一些图标。我可以为每个数据点添加一个行元素,并仅在适用的情况下设置它们可见,但由于我只想为一百个数据点中的一个添加它们,这似乎效率低下。有没有办法将SVG行仅绑定到d.type == "Apple"的对象?

我会为图标和线条创建单独的选择,这样:

var iconGroups = zoomArea.selectAll('g.icons')
  .data(resources);
iconGroups
  .enter()
  .append('g')
  .classed('icons', true);
iconGroups.exit().remove();
var icons = iconGroups.selectAll('.icon').data(function(d) {return [d];});
icons
  .enter()
  .append(function(d) {
    if(d.type === 'Apple'){
      return appleIcon;
    }else if(d.type === 'Orange'){
      return orangeIcon;
    }        
  }).classed('icon', true);
icons.exit().remove();
var lines = iconGroups.selectAll('.line').data(function(d) {
  return d.type === 'Apple' ? [d] : [];
});
lines
  .enter()
  .append('line')
  .classed('line', true);
lines.exit().remove();

添加 .exit().remove() 只是因为我总是添加它以确保更新更好地工作。 :)

也许代码比 .filter() 长,但我一直使用以下结构,并且更容易扩展它。

编辑:apropos 注释 - 如果需要传递索引,则应在绑定数据中传递它们:

var iconGroups = zoomArea.selectAll('g.icons')
  .data(resources.map(function(resource, index) {
    return Object.create(resource, {index: index})
  }));

(Object.create() 只是用来不改变数据,你可以使用 _.clone、Object.assign() 或者如果它不打扰你,就改变它)

然后,您可以像以下方式访问它:

lines.attr("x1", function(d){ console.log(d.index);})

您可以将一个类添加到要选择的图标(例如 appleIcon),并在选择器中使用该类来添加行。

使用 d3 过滤器。

selection.filter(selector)

筛选所选内容,返回仅包含指定选择器为 true 的元素的新选择。

参考: https://github.com/mbostock/d3/wiki/Selections#filter

演示:http://bl.ocks.org/d3noob/8dc93bce7e7200ab487d