【发布时间】:2012-10-25 20:15:57
【问题描述】:
我有一个这样的数据结构(假设该数据结构是不可协商的):
data = {
segments : [
{x : 20, size : 10, colors : ['#ff0000','#00ff00']},
{x : 40, size : 20, colors : ['#0000ff','#000000']}
]};
使用d3.js javascript 库,我想绘制四个矩形,一个用于colors 数组中的每种颜色。来自segments 数组中每个条目的信息用于绘制对应于其color 数组中每种颜色的矩形。例如,红色和绿色矩形的宽度和高度均为 10。生成的 html 应如下所示:
<div id="container">
<svg width="200" height="200">
<g>
<rect x="20" y="20" width="10" height="10" fill="#ff0000"></rect>
<rect x="30" y="30" width="10" height="10" fill="#00ff00"></rect>
</g>
<g>
<rect x="40" y="40" width="20" height="20" fill="#0000ff"></rect>
<rect x="60" y="60" width="20" height="20" fill="#000000"></rect>
</g>
</svg>
</div>
我已经想出了一些代码来实现这一点,但我发现在data 中使用来自两个不同嵌套级别的数据的部分令人困惑,我觉得可能有一种更惯用的方式来完成d3.js 也是如此。这是代码(完整示例在http://jsbin.com/welcome/39650/edit):
function pos(d,i) { return d.x + (i * d.size); } // rect position
function size(d,i) { return d.size; } // rect size
function f(d,i) { return d.color; } // rect color
// add the top-level svg element and size it
vis = d3
.select('#container')
.append('svg')
.attr('width',200)
.attr('height',200);
// add the nested svg elements
var nested = vis
.selectAll('g')
.data(data.segments)
.enter()
.append('g');
// Add a rectangle for each color
nested
.selectAll('rect')
.data(function(d) {
// **** ATTENTION ****
// Is there a more idiomatic, d3-ish way to approach this?
var expanded = [];
for(var i = 0; i < d.colors.length; i++) {
expanded.push({
color : d.colors[i],
x : d.x
size : d.size });
}
return expanded;
})
.enter()
.append('rect')
.attr('x',pos)
.attr('y',pos)
.attr('width',size)
.attr('height',size)
.attr('fill',f);
有没有更好的和/或更惯用的方法来使用 d3.js 从数据结构中的两个不同嵌套级别访问数据?
编辑
这是我想出的解决方案,感谢 meetamit's answer 的闭包想法,感谢 nautat's answer 使用更多惯用的 d3.js 缩进:
$(function() {
var
vis = null,
width = 200,
height = 200,
data = {
segments : [
{x : 20, y : 0, size : 10, colors : ['#ff0000','#00ff00']},
{x : 40, y : 0, size : 20, colors : ['#0000ff','#000000']}
]
};
// set the color
function f(d,i) {return d;}
// set the position
function pos(segment) {
return function(d,i) {
return segment.x + (i * segment.size);
};
}
// set the size
function size(segment) {
return function() {
return segment.size;
};
}
// add the top-level svg element and size it
vis = d3.select('#container').append('svg')
.attr('width',width)
.attr('height',height);
// add the nested svg elements
var nested = vis
.selectAll('g')
.data(data.segments)
.enter().append('g');
// Add a rectangle for each color. Size of rectangles is determined
// by the "parent" data object.
nested
.each(function(segment, i) {
var
ps = pos(segment),
sz = size(segment);
var colors = d3.select(this)
.selectAll('rect')
.data(segment.colors)
.enter().append('rect')
.attr('x', ps)
.attr('y',ps)
.attr('width', sz)
.attr('height',sz)
.attr('fill', f);
});
});
这是完整的工作示例:http://jsbin.com/welcome/42885/edit
【问题讨论】:
-
为什么在你的 html 代码中需要嵌套 svg 标签?
-
我希望将与
segment对应的所有矩形组合在一起。如果我理解正确,我可以通过更改父svg元素的x和y属性来移动第一个segment中的所有矩形。如果有更好的方法,我很想知道。 -
带有变换属性的组元素怎么样:
<g transform='translate(10,20)'>...</g>:w3.org/TR/SVG/coords.html#TransformAttribute。如果我理解正确,您应该为每幅图只使用一个 svg。 -
是的,你是对的,
<g>元素看起来确实是我应该使用的。我已经编辑了问题和 JSBin 代码以使用<g>而不是<svg>元素。感谢您指出这一点!
标签: javascript d3.js