我有要添加到SVG的数据对象。请考虑以下伪代码段:
var data = [], counter = 0;
for (var col=1; col<=5; col++)
for (var row=1; row<=3; row++)
data.push({
id: "obj-" + ++counter
,x: col * 120
,y: row * 120
,width: 40
,height: 40
,shape: counter % 2 ? "circle" : "rect"
});
d3.select(".container").selectAll(".obj")
.data(data)
.enter()
.append("g")
.attr("id", function(d){ return d.id; }
/***
now I want to draw here a circle or rect based on the shape key
so if (d.shape == "rect") -- we will use width and height
if (d.shape == "rect" && d.width == d.height) we will set "r" to "width", etc.
***/
理想情况下,我会创建一个Shape类型的对象,例如
function Shape(id, shape, x, y, w, h) {
this.id = id;
this.shape = shape;
this.x = x;
this.y = y;
this.width = w;
this.height = h;
this.render = function(parent) {
var g = parent.append("g")
.attr("id", this.id);
switch (this.shape) {
case "circle":
g.append("circle")
.attr( /* more code here */ )
break;
case "rect":
g.append("rect")
.attr( /* more code here */ )
break;
case "triangle":
g.append("polygon")
.attr( /* more code here */ )
break;
}
}
}
然后我可以做类似的事情:
var data = [], counter = 0;
for (var col=1; col<=5; col++)
for (var row=1; row<=3; row++)
data.push(new Shape({
id: "obj-" + ++counter
,x: col * 120
,y: row * 120
,width: 40
,height: 40
,shape: counter % 2 ? "circle" : "rect"
)});
但是如何从d3调用Shape的render()方法呢?即。
d3.select(".container").selectAll(".obj")
.data(data)
.enter()
/* given a datum named d, call d.render(parent) ? */
我对d3很新,所以数据加入可能是错误的方法吗?是否有不同的方法来呈现更适合此场景的数据项?
答案 0 :(得分:1)
要使数据对象以面向对象的方式呈现自己,您可以使用不太知名的selection.append(name)
。根据文档,您可以向.append()
提供回调,该回调需要返回要追加的DOM元素:
选择。的追加强>(名称)
[...]
可以将名称指定为常量字符串,也可以将其指定为返回要追加的DOM元素的函数。如果name是一个函数,则传递当前数据d和当前索引i,并将此上下文作为当前DOM元素。要根据绑定数据附加任意元素,必须在函数中创建它。例如:
selection.enter().append(function(d) { return document.createElementNS("http://www.w3.org/2000/svg", d.type) })
出于此问题的目的,可以将其修改为不创建元素,而是将创建委托给数据对象的.render()
方法。
d3.select(".container").selectAll("g")
.data(data)
.enter()
.append(function(d) {
return d.render(); // .render() will return the DOM element to append
});
您的.render()
方法可能如下所示:
this.render = function() {
// Create the group.
var g = document.createElementNS(d3.ns.prefix.svg, "g");
g.setAttribute("id", this.id);
// Create and configure the child element based on this.shape.
var child;
switch (this.shape) {
case "circle":
child = document.createElementNS(d3.ns.prefix.svg, "circle");
child.setAttribute("cx", this.x);
child.setAttribute("cy", this.y);
child.setAttribute("r", this.width/2);
break;
case "rect":
child = document.createElementNS(d3.ns.prefix.svg, "rect")
break;
case "triangle":
child = document.createElementNS(d3.ns.prefix.svg, "polygon")
break;
}
// Append the child to the group and return the g DOM element.
g.appendChild(child);
return g;
}
请查看此代码段以获取有效示例:
function Shape(id, shape, x, y, w, h) {
this.id = id;
this.shape = shape;
this.x = x;
this.y = y;
this.width = w;
this.height = h;
this.render = function() {
// Create the group.
var g = document.createElementNS(d3.ns.prefix.svg, "g");
g.setAttribute("id", this.id);
// Create and configure the child element based on this.shape.
var child;
switch (this.shape) {
case "circle":
child = document.createElementNS(d3.ns.prefix.svg, "circle");
child.setAttribute("cx", this.x);
child.setAttribute("cy", this.y);
child.setAttribute("r", this.width/2);
break;
case "rect":
child = document.createElementNS(d3.ns.prefix.svg, "rect")
child.setAttribute("x", this.x);
child.setAttribute("y", this.y);
child.setAttribute("width", this.width);
child.setAttribute("height", this.height);
break;
case "triangle":
child = document.createElementNS(d3.ns.prefix.svg, "polygon")
break;
}
// Append the child to the group and return the g DOM element.
g.appendChild(child);
return g;
}
}
var data = [], counter = 0;
for (var col=1; col<=5; col++)
for (var row=1; row<=3; row++)
data.push(new Shape(
"obj-" + ++counter
,counter % 2 ? "circle" : "rect"
,col * 120
,row * 120
,40
,40
));
console.log(data);
d3.select(".container").selectAll("g")
.data(data)
.enter()
.append(function(d) {
return d.render(); // .render() will return the DOM element to append
});
&#13;
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<svg width="400" height="400">
<g class="container"></g>
</svg>
&#13;
答案 1 :(得分:0)
我也对这个话题感兴趣。这就是我目前正在做的事情。在您的情况下,您可以选择所需的形状,而不是将文本设置为d.text。或许有人可能会以更好的方式启发我们。
// file grid.js
var chipView = Chip().radius(config.chip.radius);
function updateView() {
view.chips = view.renderLayer.selectAll('.draggable-chip')
.data(model.chips /* , id */)
.call(chipView, 'update')
;
view.chips.enter()
.append('g')
.call(chipView)
.classed('chip draggable-chip', true)
.call(d3.behavior.drag()
.on('dragstart', chipDragStarting)
.on('drag', chipDragging)
.on('dragend', chipDragEnding)
)
;
view.chips
.attr('transform', function(d,i){return translateString(d.col*config.grid.size, d.row*config.grid.size);})
;
}
// file chip.js
var Chip = function() {
var config = {
r: 20
};
function chipView(g, updateOnly) {
// g is an array of groups (a d3 selection)
// each g will become a chip
updateOnly = (typeof updateOnly === 'string' || updateOnly === true);
g.each(function() {
var view = d3.select(this); // the svg group in g array
function initView() {
// clear any existing svg nodes inside 'g'
var contents = view.selectAll(this.childNodes);
if (contents)
contents.remove();
// draw the chip
view.append('circle')
.attr('r', config.r)
.attr('cx', 0)
.attr('cy', 0)
;
view.append('text')
.attr('text-anchor', 'middle')
.attr('dy', '0.33em')
.text(function(d,i){return d.text;})
;
}
if (!updateOnly)
initView();
function updateView() {
view.attr('opacity', function(d,i){return 1 - 0.5 * d.ghost;});
view.select('text').attr('opacity', function(d,i){return 1 - d.ghost;});
}
updateView();
});
}
chipView.radius = function(r) {
if (!arguments.length)
return config.r;
r = parseFloat(r);
if (typeof r !== 'number')
throw 'Error: radius must be a number.';
config.r = r;
return this; // for function chaining
};
return chipView;
};