我在html页面上有一些svg。我想在其上绑定(bind)一些数据,并基于该数据添加装饰元素。我想我要做的是:

// pseudo-code
selection = select all existing svg piece i want to decorate

var datas = selection.each( function(d,i) { // each is right? maybe selection.datum() is better?
    var data = this.(do something with the svg piece)
    return data;
});

// add elements with the "default" chain selection.data(datas).enter().append()

我注意到的是selection.each没有返回我可以找到返回数据的地方。我认为这是方法,但是我无法弄清楚要查看绑定(bind)的数据该做什么。

所以我必须做一些肮脏的解决方法,例如:
var datas = [];
selection.each( function(d,i) { // each is right? maybe selection.datum() is better?
    var data = this.(do something with the svg piece)
    datas.push(data);
});

为什么?我如何做类似的事情而无需手动将数据推入数组并在某些现有的svg元素内绑定(bind)数据?

这是一个jsFiddle example

或者,如果您愿意,代码:

的HTML:
<div id="container">
    <svg>
        <rect id="0" x="0" y="50" width="30" height="30"/>
        <rect id="1" x="50" y="50" width="30" height="30"/>
        <rect id="2" x="100" y="50" width="30" height="30"/>
        <rect id="3" x="150" y="50" width="30" height="30"/>
        <rect id="4" x="200" y="50" width="30" height="30"/>
        <rect id="5" x="250" y="50" width="30" height="30"/>
    </svg>
</div>

js:
var svg = d3.select("#container svg");
var districts = svg.selectAll("rect");

var district_data = [];
var _c = districts.each(function(d, i) {
    var bbox = this.getBBox();
    var centroid = [
        bbox.x + bbox.width/2,
        bbox.y + bbox.height/2
    ];
    var ret = {centroid:centroid, position:bbox.x};
    district_data.push( ret );
    return ret;
});

// now, i'm expecting that _c should be something
// similar to district_data
console.log(_c);

svg
  .selectAll("circle")
  .data(district_data) // i think i should use `_c` instead of manually created `district_data` but does not work
  .enter()
  .append("circle")
     .attr("class", "district_circle")
     .attr("cx", function(d){ return d.centroid[0]})
     .attr("cy", function(d){ return d.centroid[1]})
     .attr("r", 10)
     .attr("fill", function(d){ return "rgb("+d.position+",0,0)"});

最佳答案

首先,期望each()方法返回数据数组是不正确的。这只是迭代选择的一种方法。返回的内容(已分配给_c的东西)是d3选择对象-与调用each()相同的选择对象。即_c == districts计算结果为true。 AFAIK,d3选择对象不提供任何可用于以您想要描述的方式收集值的东西。

通常,您希望使用.map()函数来收集这些值并将它们分配给_c,但是不幸的是,在这里似乎不可能,因为districts也是d3选择,而不是平面数组。而且,如果您尝试在其上调用map(),我认为它实际上不会遍历选择中的每个元素,并且您还会收到一个js错误,因为this对象未分配给该对象上的SVG元素您需要调用getBBox()

最重要的是,我认为您采用的方法是正确的:使用each()进行迭代,并通过将其推入来构建数组。

我可以建议一种更简洁的方法,但这需要修改现有SVG的结构:

而不是让rects是 sibling ,而是将每个嵌套在g内。喜欢:

<div id="container">
<svg>
  <g>
    <rect id="0" x="0" y="50" width="30" height="30"/>
  </g>
  <g>
    <rect id="1" x="50" y="50" width="30" height="30"/>
  </g>
  ...

然后在JS中(未经测试):
svg.selectAll('g')
  .each(function(d, i) { // Note, d is actually undefined, bc no data-binding was done
    var rect = d3.select(this).select('rect');
    var bbox = this.getBBox();
    var centroid = [
        bbox.x + bbox.width/2,
        bbox.y + bbox.height/2
    ];

    // Now create the circle, appending it to the group,
    // as a sibling of its corresponding rect
    var circle = d3.select(this).append('circle')
     .attr("class", "district_circle")
     .attr("cx", centroid[0])
     .attr("cy", centroid[1])
     .attr("r", 10)
     .attr("fill", "rgb("+bbox.x+",0,0)");
  });

这仍然不是一个很好的形式,因为定位应用于每个圆和矩形,而理想情况下,定位将应用于组级别–并不太难实现。但是现在我们变得挑剔了。

关于javascript - d3.js:将数据绑定(bind)到现有的svg。每个selector.each做什么?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/15292944/

10-11 11:08
查看更多