问题描述
我有要添加到SVG的数据对象。考虑下面的伪代码片段:
var data = [],counter = 0;
for(var col = 1; 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;}
/ ***
现在我要在这里绘制一个圆形或rect形状键
所以如果(d.shape ==rect) - 我们将使用宽度和高度
if(d.shape ==rect&& d.width == d .height),我们将r设置为width等。
*** /
理想情况下,我将创建一个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){
casecircle:
.append(circle)
.attr(/ *这里更多的代码* /)
break;
caserect:
g.append(rect)
.attr(/ * more code here * /)
break;
casetriangle:
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 $ b 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 )
.enter()
/ *给定一个名为d的数据,调用d.render(parent)? * /
我是d3的新手,所以也许数据连接是错误的方式去?是否有一种不同的方式来呈现对于这种情况更好的数据项?
面向对象的方式,您可以诉诸不太知名的。根据文档,您可以提供回调 .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()将返回DOM元素追加
}
您的 .render()
像这样:
this.render = function(){
//创建组。
var g = document.createElementNS(d3.ns.prefix.svg,g);
g.setAttribute(id,this.id);
//基于this.shape创建和配置子元素。
var child;
switch(this.shape){
casecircle:
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;
caserect:
child = document.createElementNS(d3.ns.prefix.svg,rect)
break;
casetriangle:
child = document.createElementNS(d3.ns.prefix.svg,polygon)
break;
}
//将子元素附加到组并返回g DOM元素。
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(){//创建组。 var g = document.createElementNS(d3.ns.prefix.svg,g); g.setAttribute(id,this.id); //基于this.shape创建和配置子元素。 var child; switch(this.shape){casecircle:child = document.createElementNS(d3.ns.prefix.svg,circle); child.setAttribute(cx,this.x); child.setAttribute(cy,this.y); child.setAttribute(r,this.width / 2);打破; caserect: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);打破; casetriangle:child = document.createElementNS(d3.ns.prefix.svg,polygon)break; } //将该子元素附加到组并返回g DOM元素。 g.appendChild(child); return g; }} var data = [],counter = 0; for(var col = 1; col
< script src =https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js>< / script>< svg width =400height = 400> < g class =container>< / g>< / svg>
I have data objects that I want to add to an SVG. Consider the following pseudo-snippet:
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.
***/
Ideally, I would create an object of type Shape, e.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(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;
}
}
}
Then I'd be able to do something like:
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"
)});
But how can I call the Shape's render() method from d3? i.e.
d3.select(".container").selectAll(".obj")
.data(data)
.enter()
/* given a datum named d, call d.render(parent) ? */
I'm rather new to d3, so maybe data joins is the wrong way to go? Is there a different way to render data items that would be better for this scenario?
解决方案 To have the data objects render themselves in an object-oriented manner you can resort to a less known use of selection.append(name)
. According to the docs, you can provide a callback to .append()
which needs to return a DOM element to append:
For the purpose of this question this may be modified to not create the element in place, but to delegate the creation to the .render()
method of your data objects.
d3.select(".container").selectAll("g")
.data(data)
.enter()
.append(function(d) {
return d.render(); // .render() will return the DOM element to append
});
Your .render()
method might look something like this:
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;
}
Have a look at this snippet for a working example:
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
});
<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>
这篇关于面向对象d3的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!