3

HTMLページにいくつかのsvgがあります。それらにいくつかのデータをバインドし、そのデータに基づいて装飾要素を追加したいと考えています。私がしなければならないと思うことは次のとおりです。

// 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 が返されたデータを見つけることができる何かを返さないことです。これがその方法だと思いますが、バインドされたデータを表示するために何をしなければならないかわかりません。

したがって、次のような汚い回避策を実行する必要があります。

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 要素内にデータをバインドしたりせずに、どうすれば同様のことを行うことができますか?

以下はjsFiddle の例です。

または、必要に応じて、コード:

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)"});
4

1 に答える 1

2

each()まず、メソッドがデータの配列を返すことを期待するのは正しくありません。これは、選択を繰り返すための単なる方法です。返されるのは(割り当てられるもの_c)、d3選択オブジェクトです-呼び出しているのと同じ選択オブジェクトですeach()。つまり_c == districts、trueと評価されます。AFAIK、d3選択オブジェクトは、あなたがやりたいことを説明する方法で値を収集するために使用できるものを何も提供しません。

一般に、.map()関数を使用してこれらの値を収集し、に割り当てる必要があります_cが、残念ながら、ここでもそれは不可能なようですdistricts。これも、フラット配列ではなく、d3選択であるためです。そして、map()それを呼び出そうとしても、選択範囲内の各要素を実際に繰り返すことはないと思います。また、thisオブジェクトが必要なSVG要素に割り当てられないため、jsエラーが発生します。呼び出すgetBBox()

結論として、あなたが取ったアプローチは正しいものだと思いますeach()。アレイを繰り返して、押し込んで構築します。


これをより簡潔に行うもう1つの方法を提案できますが、既存のSVGの構造を変更する必要があります。

兄弟になる代わりに、rectsそれぞれをの中に入れ子にし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)");
  });

ポジショニングは各円と長方形に適用されるため、これはまだ完全に優れた形式ではありませんが、理想的には、ポジショニングはグループレベルで適用されます-そしてそれを達成するのはそれほど難しくありません。しかし今、私たちはうるさくなっています。

于 2013-03-08T17:52:55.887 に答える