javascript - 额外的传奇礼物
问题描述
我在条形图中添加了两个图例,但我不知道为什么会出现额外的图例。我不知道我的代码的哪一部分是错误的,因为我只在我的代码中定义了两个图例。
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]);
});
黑色矩形是额外的一个:
解决方案
对于 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 附加子级相比,嵌套附加的不同方法可以提供好处
推荐阅读
- magento - Magento 2 优惠券添加/删除事件监听器
- javascript - Preact 错误 - 对象不支持属性或方法“平面”
- saml-2.0 - 注销抛出证书无效签名
- javascript - VS Code 代码格式化 JavaScript 不正确
- javascript - 尝试使用枚举属性从 History 对象中提取状态时遇到 Typescript 错误
- azure - 如何将 AddAzureWebAppDiagnostics 与 IFunctionsHostBuilder 一起使用
- php - TCPDF:如何计算并在一页上放置适当的块,然后从表格开始
- selenium - Webdriver 页面源未更改
- javascript - 为什么这段代码同时使用 useMemo 和 createSelector?
- python - 使用命名参数从 Node.js (child_process) 运行 python 脚本