关于 d3.js:如何使用 D3 d3js 基于元素的属性分层附加”组合”元素?

How to hierarchically append "composed" elements based on elements' attributes using D3 d3js?

我正在使用 D3.js,并且我有一个元素列表,其中包含一个描述如下类别的属性:

1
2
3
4
var nodes = [
{id:"a", cat:[0,1]},
{id:"b", cat:[0]},
{id:"c", cat:[0,1,2]}];

每个元素都是由强制布局定位的多圆节点。一个元素的类别数定义了代表它的圆圈数。

Nodes

我现在的"解决方案"是为每个类别创建图层。强制布局处理数据列表。这些层处理 svg 元素。
每一层都是一个元素。

  • <g class="layer2"> 中,我为节点 c 画了一个圆,半径 = 9;
  • <g class="layer1"> 中,我为节点 a 和 c 绘制圆,半径 = 6;
  • <g class="layer0"> 中,我为节点 a、b 和 c 绘制圆,半径 = 3;

问题是每个数据元素都由分离层中的分离圆圈表示。

我们如何用一组圆圈来表示每个元素,如下所示:

1
2
3
4
5
6
7
8
9
    <g class="node" id="a">
       <circle>... <circle>...
    </g>
   <g class="node" id="b">
       <circle>...
    </g>
   <g class="node" id="c">
       <circle>... <circle>... <circle>...
    </g>

是否可以在 D3 代码风格中分层使用某些东西,例如:

1
2
3
4
5
6
7
8
svg.selectAll(".node")
    .data(nodes)
    .enter()
    .append("g")
    .attr("id", function(d){return d.id;}
    //for each category on this node              
    .append("circle")
    .attr("r", function(){ return (cat+1)*3;});

这样的解决方案将是最好的选择。

或者我必须单独创建元素<g class="node" id="...">selectAll(".node"),然后将它们链接到数据(节点列表)?


在我看来你只需要嵌套选择:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var groups = svg.selectAll(null)
  .data(nodes)
  .enter()
  .append("g")
  .attr("id", function(d) {
    return d.id
  });

var circles = groups.selectAll(null)
  .data(function(d) {
    return d.cat
  })
  .enter()
  .append("circle")
  //etc...

这是一个演示,其中包含您的 nodes 数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
var nodes = [{
  id:"a",
  cat: [0, 1]
}, {
  id:"b",
  cat: [0]
}, {
  id:"c",
  cat: [0, 1, 2]
}];

var colors = d3.scaleOrdinal(d3.schemeCategory10);

var svg = d3.select("svg");

var groups = svg.selectAll(null)
  .data(nodes)
  .enter()
  .append("g")
  .attr("id", function(d) {
    return d.id
  })
  .attr("transform", function(d, i) {
    return"translate(" + (50 + 100 * i) +",75)";
  });

var circles = groups.selectAll(null)
  .data(function(d) {
    return d.cat
  })
  .enter()
  .append("circle")
  .attr("r", function(d) {
    return 10 + (d * 10)
  })
  .style("fill", function(d, i) {
    return colors(i)
  });

circles.sort(function(a, b) {
  return d3.descending(a, b)
})
1
2
<script src="https://d3js.org/d3.v4.min.js">
<svg></svg>

如果你检查你的 SVG,你会看到:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<svg>
    <g id="a" transform="translate(50,75)">
        <circle r="30" style="fill: rgb(31, 119, 180);"></circle>
        <circle r="20" style="fill: rgb(255, 127, 14);"></circle>
    </g>
    <g id="b" transform="translate(150,75)">
        <circle r="30" style="fill: rgb(31, 119, 180);"></circle>
    </g>
    <g id="c" transform="translate(250,75)">
        <circle r="30" style="fill: rgb(31, 119, 180);"></circle>
        <circle r="20" style="fill: rgb(255, 127, 14);"></circle>
        <circle r="10" style="fill: rgb(44, 160, 44);"></circle>
    </g>
</svg>

PS:我正在对圆圈进行排序(查看代码的底部)以将较小的圆圈置于顶部,使它们可见。


你可以这样做:

1
2
3
4
5
6
7
8
9
10
11
12
svg.selectAll(".node")
  .data(nodes)
  .enter()
  .append("g")
  .attr("id", function(d){return d.id;})  
  .selectAll("circle")
  .data(function(d) {return d.cat}) // <-- bind cat data
  .enter()
  .append("circle")
  .attr("r", function(d) {
        //set radius
  });