首页 > 解决方案 > 额外的传奇礼物

问题描述

我在条形图中添加了两个图例,但我不知道为什么会出现额外的图例。我不知道我的代码的哪一部分是错误的,因为我只在我的代码中定义了两个图例。

var color_hash = {  0 : ["Male", "blue"],
                    1 : ["Female", "pink"]}

var legend = svg.append("g")
            .attr("class", "legend")
            .attr("x", width - 65)
            .attr("y", 25)
            .attr("height", 100)
            .attr("width", 100);

legend.selectAll('g').data(data)
      .enter()
      .append('g')
      .each(function(d, i) {
        var g = d3.select(this);
        g.append("rect")
         .attr("x", width - 65)
         .attr("y", i*25)
         .attr("width", 10)
         .attr("height", 10)
         .style("fill", color_hash[String(i)][1]);
        g.append("text")
         .attr("x", width - 50)
         .attr("y", i * 25 + 8)
         .attr("height", 30)
         .attr("width", 100)
         .style("fill", color_hash[String(i)[1]])
         .text(color_hash[String(i)][0]);

      });

黑色矩形是额外的一个:

在此处输入图像描述

标签: javascriptd3.jsvisualization

解决方案


对于 D3 中的进入/更新/退出循环,您通常希望拥有一个数据数组,其中包含您要绘制的每个元素的一项。你有:

  • 颜色有对象color_hash这是你真正想用来绘制图例的,并且
  • 一些数据数组data,虽然我们不知道里面是什么。

我们data用来可视化color_hash,这并不理想。

一方面,您只想绘制 2 个元素,我可以告诉您,长度data至少为 3:

你创建一个空g的:

var legend = svg.append("g")

然后你选择g它的子元素:

legend.selectAll('g')

由于没有,因此这是一个空选择。然后将数据分配给该选择并输入新的 HTML/SVG 元素:

legend.selectAll('g')
  .data(data)
  .enter()
  .append('g')

由于legend是一个空选择,因此输入选择将为数据数组中的每个项目创建一个 HTML/SVG 元素。进入(和/或退出)后,HTML/SVG 元素的数量应等于数据数组中的项目数量。因此,data其中必须至少有 3 个项目(如果创建了其他元素,它可能会有更多,但它们超出了 SVG/容器边界。这也解释了为什么第三个框没有颜色或文本:颜色哈希没有键为 2 或更大的值)。

D3 是从数据中创建元素,通常在元素和项目之间存在一对一的关系。要创建我们的图例,数据数组应该是我们想要绘制的。因此,我们需要将颜色哈希转换为数组:

var legendData = [
  {name: "A", color:"crimson"},
  {name: "B", color:"steelblue"}
];

现在我们只需将其提供给selection.data()

而且,由于我们现在将要绘制的数据绑定到图例条目,我们还可以简化代码,而不是:

 .style("fill", color_hash[String(i)][1]);

 .text(color_hash[String(i)][0]);

我们可以使用:

  .style("fill",d.color);

  .text(d.name);

这给了我们:

var color_hash = {  0 : ["Male", "blue"],
                    1 : ["Female", "pink"]}

var width = 300;
var height = 200;

var svg = d3.select("svg")
  .attr("width",width)
  .attr("height",height);

var legendData = [
  {name:"A",color:"crimson"},
  {name:"B",color:"steelblue"}
]

var legend = svg.append("g")

legend.selectAll('g')
  .data(legendData)
  .enter()
  .append('g')
  .each(function(d, i) {
        var g = d3.select(this);
        g.append("rect")
         .attr("x", width - 65)
         .attr("y", i*25+25)
         .attr("width", 10)
         .attr("height", 10)
         .style("fill", d.color);
        g.append("text")
         .attr("x", width - 50)
         .attr("y", i * 25 + 33)
         .attr("height", 30)
         .attr("width", 100)
         .style("fill", d.color)
         .text(d.name);

      });
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg></svg>

我在这里只关注输入循环:可能会对放置进行进一步修改,并且与使用 .each 附加子级相比,嵌套附加的不同方法可以提供好处


推荐阅读