我有数据和形状定义的结构:
var data = [
{
"id": "first",
"shapes": [
{
"shape": "polygon",
"points": [["8","64"],["8","356"],["98","356"],["98","64"]]
}
]
},
{
"id": "second",
"shapes": [
{
"shape": "ellipse",
"cx": "63", "cy": "306", "rx": "27","ry": "18"
}, {
"shape": "polygon",
"points": [["174","262"],["171","252"],["167","262"]]
}
]
}
]; // in the data may be stored any SVG shape
我想创建SVG:
<svg width="218" height="400">
<g transform="translate(0,400) scale(1,-1)">
<g>
<polygon points="8,64 8,356 98,356 98,64"/>
</g>
<g>
<ellipse cx="63" cy="306" rx="27" ry="18"/>
<polygon points="174,262 171,252 167,262"/>
</g>
</g>
</svg>
对于我追加data
的每个<g>
元素:
var groups = svg.selectAll("g").data(data, function (d) {
return d.id;
});
groups.enter().append("g");
现在我正在绑定组形状的数据:
var shapes = groups.selectAll(".shape").data(function (d) {
return d.shapes;
}, function(d,i){return [d.shape,i].join('-');});
到目前为止,这是预期的。现在我想为每个进入DOM节点的调度绘图功能提供适当的形状,但显然shapes.enter().each()
在这个上下文中没有工作(未定义)。我认为它在每个DOM节点上工作比在每个要绑定的数据上工作。这是有效的:
shapes.enter().append("g").each(function(draw, i) {
var shape = draw.shape;
d3.select(this).call(drawer[shape]);
});
但痛苦的副作用是SVG有两级<g>
:
<svg width="218" height="400">
<g transform="translate(0,400) scale(1,-1)">
<g>
<g>
<polygon points="8,64 8,356 98,356 98,64"/>
</g>
</g>
...
</svg>
如何摆脱它?如何正确构建基于数据的形状?
答案 0 :(得分:4)
根据AmeliaBR提供的数据添加基本形状的两种方法是可以的,但稍微超出了d3.js API。经过长时间的考虑,我决定添加回答我自己的问题。
寻找其他解决方案的灵感是Lars Kotthoff的评论,建议使用路径而不是原始的SVG形状。在此方法中,应添加<g>
:
<path>
shapes.enter().append("path").attr("d", function(d) {
return drawer[d.shape](d);
});
生成基本形状的原始drawer
对象将返回d
的属性<path>
的值。
var drawer = {
polygon: function (d) {
return [
"M", d.points[0].join(','),
"L", d.points
.slice(1, d.points.length)
.map(function (e) {
return e.join(",")
}), "Z"].join(" ");
},
ellipse: function (d) {
return [
'M', [d.cx, d.cy].join(','),
'm', [-d.rx, 0].join(','),
'a', [d.rx, d.ry].join(','),
0, "1,0", [2 * d.rx, 0].join(','),
'a', [d.rx, d.ry].join(','),
0, "1,0", [-2 * d.rx, 0].join(',')].join(' ');
},
circle: function (d) {
return this.ellipse({
cx: d.cx,
cy: d.cy,
rx: d.r,
ry: d.r
});
},
rect: function (d) {
return this.polygon({
points: [
[d.x, d.y],
[d.x + d.width, d.y],
[d.x + d.width, d.y + d.height],
[d.x, d.y + d.height]
]
});
},
path: function (d) {
return d.d;
}
};
查看
答案 1 :(得分:3)
棘手的问题。遗憾的是你不能在enter()选择中调用每个。 你也不能在附加语句中使用(见评论)function(d){}
。
但是我在数据阵列本身上使用Javascript forEach()
调用了它。它使用数组条目,索引和数组本身作为参数调用函数,并且可以指定this
上下文 - 我只是将所需的父元素作为选择传递。
神话般的小提琴:http://fiddle.jshell.net/994XM/1/
(比你的情况简单,但应该能够轻松适应)
由于forEach
中的D3代码全部是每个元素,因此d
和i
值已经可用,所以有点令人困惑,所以D3方法调用中不需要任何内部函数。但是一旦你明白了,一切都会奏效。
答案 2 :(得分:1)
我通过Google发现了这个问题,与上面链接的jsfiddles已停止运作。您现在必须调用document.createElementNS,为其提供SVG命名空间,否则形状不会显示。
我创建了一个更新的fiddle。我还清理了很多不必要的内容,因此应该更容易看到发生了什么。