D3.js 如何基于数据创建不同类型的SVG元素?
我正在尝试创建一个不同的图例,其中有圆、三角形、矩形、直线等,我想独立创建这些图例,然后使用d3来安排它们的位置和颜色,但我如何直接访问这些数据D3.js 如何基于数据创建不同类型的SVG元素?,d3.js,svg,D3.js,Svg,我正在尝试创建一个不同的图例,其中有圆、三角形、矩形、直线等,我想独立创建这些图例,然后使用d3来安排它们的位置和颜色,但我如何直接访问这些数据 d3.selectAll('g.legend') .data([ // is there a way to have d3 create an element in memory but not append it? { svgFn: function() { this.append('rect') }, ...otherinfo
d3.selectAll('g.legend')
.data([
// is there a way to have d3 create an element in memory but not append it?
{ svgFn: function() { this.append('rect') }, ...otherinfo },
{ svgFn: function() { this.append('circle') }, ...otherinfo },
]).enter()
.append('g')
.append(function(d) { d.svgFn.call(this)})
.attr...
这个问题是如何基于数据创建元素的变体?或者如何动态附加元素?图案在我看来,您的方法将过于复杂和混乱,因为您需要复制函数来在数据中创建元素。这似乎不是一个优雅的解决方案 我更愿意只指定要创建的元素的类型,即数据对象中的
{type:“circle”}
,{type:“rect”}
等,并让该方法完成工作。此方法将接受回调,该回调反过来可能会计算数据中指定的类型,并相应地创建元素:
选择。追加(类型)[…]
否则,类型可能是一个函数,该函数按顺序为每个选定元素求值,并传递当前数据(d)、当前索引(i)和当前组(节点),将其作为当前DOM元素。此函数应返回要追加的元素 这将使您的代码简化为:
d3.selectAll('g.legend')
.data([
{ type: 'rect', other: info },
{ type: 'circle', other: info }
])
.enter().append('g')
.append(function(d) {
return document.createElementNS(d3.namespaces.svg, d.type);
});
补遗 根据user2167582的要求,分配属性的解决方案也可以很容易地合并 对于使用模块的D3V4,您可以使用多值语法传入包含要设置的属性的键值对的对象。假设要创建的元素数组如下所示:
var elementsAndAttributes = [
{ type: 'rect', attrs: { "fill": "blue", "width": "10", "height": "10" } },
{ type: 'circle', attrs: { "fill": "red", "cx": "20", "cy": "20", "r": "10" } }
];
然后,您可以在一次运行中绑定此数据并使用其属性创建元素:
d3.selectAll('g.legend')
.data(elementsAndAttributes)
.enter().append('g')
.append(function(d) { // Create elements from data
return document.createElementNS(d3.namespaces.svg, d.type); // v4 namespace
})
.attrs(function(d) { // Set the attributes object per element
return d.attrs;
});
当仍然使用D3V3时,情况有点不同。尽管v3内置了对多值对象配置的支持,但不允许将对象作为函数的返回值提供(有关原因的讨论,请参阅“多值映射支持”一文)。但是,您可以使用selection.each()
实现相同的功能
d3.selectAll('g.legend')
.data(elementsAndAttributes)
.enter().append('g')
.append(function(d) { // Create elements from data
return document.createElementNS(d3.ns.prefix.svg, d.type); // v3 namespace
})
.each(function(d) { // Iterate over all appended elements
d3.select(this).attr(d.attrs); // Set the attributes object per element
});
忽略D3引用名称空间常量的方式的差异,最后一个版本使用选择。each()
将在D3 v3和v4中实际工作
进一步阅读:我的目标。基于和阅读的答案 相当于:
selection.append(d3.creator("div"));
我找到了一种方法,让d3来做这项工作,而不用担心名称空间
var svg = d3.select("#chart"); // or any other selection to get svg element
d3.selectAll('g.legend')
.data(elementsAndAttributes)
.enter().append('g')
.append(function(d) { // Create elements from data
return d3.creator(d.type).bind(svg.node())();
})
.attrs(function(d) { // Set the attributes object per element
return d.attrs;
});
请注意绑定的creator()
的直接调用,因为回调的结果被用作appendChild(child)
的参数。如果bind
未知,则可以使用
.append(function(d) { // Create elements from data
return d3.creator(d.type).call(svg.node());
})
文档中提到,
this
应该是要追加的父节点,但事实并非如此,this
仅用于获取this.ownerDocument
(==浏览器的文档),creator()
调用的结果被追加到正确的(g
)节点。因此,任何(svg)元素都是正确的。您不能将其分配给var并在以后使用引用吗?var my_data=然后是d3.selectAll('g.legend')。data(my_data)。但是,如何将其他更复杂的属性分配给该元素,例如虚线vs虚线vs直线,圆圈绿色vs圆圈红色vs带有黑色笔划的圆圈红色,更糟糕的是,如果我创建一个类似地图的图形,我需要上述所有内容,我会一组一组地追加吗?当然会。请重新阅读我引用的文档。它准确地解释了这一点。@user2167582请记住,这个函数需要返回一个实际的DOM元素,而不仅仅是类型。您可以使用一个沙盒来完成扩展d3 v3吗?
.append(function(d) { // Create elements from data
return d3.creator(d.type).call(svg.node());
})