如何在拥有单个图例的同时将标题添加到多个饼图?

时间:2017-02-21 16:33:14

标签: javascript d3.js svg

我有这些数据:

var data = [
  [
    {"piece_value":76.34,name:"AG",group:"G1"},
    {"piece_value":69.05,name:"A3",group:"G1"},
    {"piece_value":275.19,name:"A4",group:"G1"}
  ],
  [
    {"piece_value":69.93,name:"AG",group:"G2"},
    {"piece_value":61.50,name:"A3",group:"G2"},
    {"piece_value":153.31,name:"A4",group:"G2"}
  ],
  [
    {"piece_value":67.48,name:"AG",group:"G3"},
    {"piece_value":58.03,name:"A3",group:"G3"},
    {"piece_value":145.93,name:"A4",group:"G3"}
  ]
];

我有3个饼图(请参阅代码段)。我想在每个饼图的顶部放置相应的group值,并将图例移到右侧(垂直图例)。问题是,现在我有3个传说(每个饼图一个),但实际上它总是相同的传说。因此,我想在右侧放置一个图例,而不是复制它3次,而将group值作为标题。 怎么做?



var data = [
  [
    {"piece_value":76.34,name:"AG",group:"G1"},
    {"piece_value":69.05,name:"A3",group:"G1"},
    {"piece_value":275.19,name:"A4",group:"G1"}
  ],
  [
    {"piece_value":69.93,name:"AG",group:"G2"},
    {"piece_value":61.50,name:"A3",group:"G2"},
    {"piece_value":153.31,name:"A4",group:"G2"}
  ],
  [
    {"piece_value":67.48,name:"AG",group:"G3"},
    {"piece_value":58.03,name:"A3",group:"G3"},
    {"piece_value":145.93,name:"A4",group:"G3"}
  ]
];

var m = 10,
    r = 90,
    z = d3.scale.category20c();

var svg = d3.select("body").selectAll("svg")
    .data(data)
    .enter().append("svg")
    .attr("width", (r + m) * 2)
    .attr("height", (r + m) * 2)
    .append("g")
    .attr("transform", "translate(" + (r + m) + "," + (r + m) + ")");

var pie = d3.layout.pie()
    .sort(null)
    .value(function(d) {
        return d.piece_value;
    });

var arc = d3.svg.arc()
    .innerRadius(r / 2.2)
    .outerRadius(r/1.2)

svg.selectAll("path")
    .data(pie)
    .enter().append("path")
    .attr("d", arc)
    .style("fill", function(d, i) {
        return z(i);
    });

svg.selectAll("foo")
    .data(pie)
    .enter()
    .append("text")
    .attr("text-anchor", "middle")
    .attr("transform", d => "translate(" + arc.centroid(d) + ")")
    .text(d => d.data.piece_value);
		
svg.selectAll("foo")
    .data(d=>d)
    .enter()
    .append("text")
		.attr("transform", (d,i)=>"translate(" + (-r + 2.5*m + (i * 70)) + "," +  (-r + m) + ")")
		.text(d=>d.name);
		
svg.selectAll("foo")
    .data(d=>d)
    .enter()
    .append("rect")
		.attr("transform", (d,i)=>"translate(" + (-r + m + (i * 70)) + "," +  (-r) + ")")
		.attr("width", 10)
		.attr("height", 10)
		.style("fill", (d, i) => z(i));

<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
&#13;
&#13;
&#13;

2 个答案:

答案 0 :(得分:1)

为每个甜甜圈设置一个图例背后的想法是它允许具有不同的数据(关于分类变量)。但是,由于您说您只想要一个图例,我假设每个图表的分类变量都相同。既然如此,我们可以创建一个新的数据阵列......

var legendData = data[0].map(d=>d.name);

...使用任何索引(因为,再次,数组与分类变量相同),而不是绘制另一个SVG,我们可以简单地用HTML创建图例:

var legendDiv = d3.select("#legend");

var legendRow = legendDiv.selectAll("foo")
    .data(legendData)
    .enter()
    .append("div")
    .style("margin-bottom", "2px");

legendRow.append("div")
    .html("&nbsp")
    .attr("class", "rect")
    .style("background-color", (d, i) => z(i));

legendRow.append("div")
    .html(d => d);

这是一个带有垂直图例的演示:

&#13;
&#13;
var data = [
  [
    {"piece_value":76.34,name:"AG",group:"G1"},
    {"piece_value":69.05,name:"A3",group:"G1"},
    {"piece_value":275.19,name:"A4",group:"G1"}
  ],
  [
    {"piece_value":69.93,name:"AG",group:"G2"},
    {"piece_value":61.50,name:"A3",group:"G2"},
    {"piece_value":153.31,name:"A4",group:"G2"}
  ],
  [
    {"piece_value":67.48,name:"AG",group:"G3"},
    {"piece_value":58.03,name:"A3",group:"G3"},
    {"piece_value":145.93,name:"A4",group:"G3"}
  ]
];

var m = 10,
    r = 90,
    z = d3.scale.category20c();

var svg = d3.select("body").selectAll("svg")
    .data(data)
    .enter().append("svg")
    .attr("width", (r + m) * 2)
    .attr("height", (r + m) * 2)
    .append("g")
    .attr("transform", "translate(" + (r + m) + "," + (r + m) + ")");

var pie = d3.layout.pie()
    .sort(null)
    .value(function(d) {
        return d.piece_value;
    });

var arc = d3.svg.arc()
    .innerRadius(r / 2)
    .outerRadius(r/1)

svg.selectAll("path")
    .data(pie)
    .enter().append("path")
    .attr("d", arc)
    .style("fill", function(d, i) {
        return z(i);
    });

svg.selectAll("foo")
    .data(pie)
    .enter()
    .append("text")
    .attr("text-anchor", "middle")
    .attr("transform", d => "translate(" + arc.centroid(d) + ")")
    .text(d => d.data.piece_value);
		
var legendData = data[0].map(d=>d.name);

var legendDiv = d3.select("#legend");

var legendRow = legendDiv.selectAll("foo")
	  .data(legendData)
    .enter()
    .append("div")
		.style("margin-bottom", "2px");

legendRow.append("div")
		.html("&nbsp")
		.attr("class", "rect")
		.style("background-color", (d, i) => z(i));
		
legendRow.append("div")
		.html(d=>d);
		
&#13;
.rect {
	width: 20px;
	float: left;
	margin-right: 10px;
}
&#13;
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div id="legend">Legend:</div>
&#13;
&#13;
&#13;

PS :你说你想要右边的传奇。要做到这一点,只需并排创建2个div:在左边一个你附加SVG,在右边一个你附加图例。我会把这作为你的家庭作业。

PS2 :请注意scale.category20以先到先得的方式运作的事实。因此,为了使图例准确,内部数组中的分类变量的顺序必须相同。

答案 1 :(得分:0)

我只想为传奇添加一个完全独立的svg。此外,除非您需要不同的svg,否则您可以考虑为每个饼添加一个组,并为图例添加一个组(但是在翻译方面的工作量更多)。 为了表演,我还会坚持使用单个绑定进入,并将其存储在局部变量中。 最后,我会为每个图例项创建一个组,以避免重复翻译代码:

&#13;
&#13;
var data = [
  [
    {"piece_value":76.34,name:"AG",group:"G1"},
    {"piece_value":69.05,name:"A3",group:"G1"},
    {"piece_value":275.19,name:"A4",group:"G1"}
  ],
  [
    {"piece_value":69.93,name:"AG",group:"G2"},
    {"piece_value":61.50,name:"A3",group:"G2"},
    {"piece_value":153.31,name:"A4",group:"G2"}
  ],
  [
    {"piece_value":67.48,name:"AG",group:"G3"},
    {"piece_value":58.03,name:"A3",group:"G3"},
    {"piece_value":145.93,name:"A4",group:"G3"}
  ]
];

var m = 10,
    r = 90,
    z = d3.scale.category20c();

var svg = d3.select("body")
    .selectAll("svg")
    .data(data)
    .enter()
    .append("svg")
    .attr("width", (r + m) * 2)
    .attr("height", (r + m) * 2)
    .append("g")
    .attr("transform", "translate(" + (r + m) + "," + (r + m) + ")");

var pie = d3.layout.pie()
    .sort(null)
    .value(function(d) {
        return d.piece_value;
    });

var arc = d3.svg.arc()
    .innerRadius(r / 2.2)
    .outerRadius(r/1.2)

svg.selectAll("path")
    .data(pie)
    .enter()
    .append("path")
    .attr("d", arc)
    .style("fill", function(d, i) {
        return z(i);
    });

 var foo = svg.selectAll("foo")
    .data(pie)
    .enter();
 foo.append("text")
    .attr("text-anchor", "middle")
    .attr("transform", d => "translate(" + arc.centroid(d) + ")")
    .text(d => d.data.piece_value);
	//the legend
 var legendGroup =    d3.select("body")
      .append('svg')
      .append('g')
      .classed('legend',true);
 var enterLegend = legendGroup
    .selectAll('.legend-item')
    .data(pie)
    .enter();
 var legendItem = enterLegend
    .append('g')
    .classed('legend-item',true)
    .attr("transform", (d,i)=>"translate(" + (-r + (i * 70)) + "," +  (-r + m) + ")");
 legendItem.append("text")
         .text(d=>d.name);
		
 legendItem.append("rect")
		.attr('x',10)
		.attr("width", 10)
		.attr("height", 10)
		.style("fill", (d, i) => z(i));
&#13;
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
&#13;
&#13;
&#13;

我没有运行代码,但我希望你能理解。