D3 管理多个图表标签的多个类



在一个页面上编译多个圆环图时,我注意到在附加数据标签时我得到了一些"伪影"。以下代码段:

var margins = {
top: 20,
left: 50,
bottom: 20,
right: 20
};
var width = 300;
var arcSize = (6 * width / 100);
var innerRadius = arcSize * 3;
var extent = 1800;
var data2 = [
[{
value: (231 / extent * 100),
marker: 231,
label: "Collective",
color: '#b8cce4',
neg: false
},
{
value: (233 / extent * 100),
marker: 233,
label: "Targeted",
color: '#95b3d7',
neg: false
},
{
value: (45 / extent * 100),
marker: 45,
label: "Specific",
color: '#4f81b9',
neg: false
},
],
[{
value: (171 / extent * 100),
marker: 171,
label: "Collective",
color: '#b8cce4',
neg: false
},
{
value: (1712 / extent * 100),
marker: 1712,
label: "Targeted",
color: '#95b3d7',
neg: false
},
{
value: (1 / extent * 100),
marker: 1,
label: "Specific",
color: '#4f81b9',
neg: false
},
],
[{
value: (207 / extent * 100),
marker: 207,
label: "Collective",
color: '#b8cce4',
neg: false
},
{
value: (975 / extent * 100),
marker: 975,
label: "Targeted",
color: '#95b3d7',
neg: false
},
{
value: (153 / extent * 100),
marker: 153,
label: "Specific",
color: '#4f81b9',
neg: false
},
]
];
var svg = d3.select('body').append('svg').attr('width', 1100 + 100).attr('height', 1100 + 100);
var graphGroup = svg.append("g")
.attr("transform", "translate(" + margins.left + "," + margins.top + ")");
for (var j = 0; j < (data2.length); j++) {
var data = data2[j];
var arcs = data.map(function(obj, i) {
return d3.svg.arc().innerRadius(i * arcSize + innerRadius).outerRadius((i + 1) * arcSize - (width / 100) + innerRadius);
});
var arcsGrey = data.map(function(obj, i) {
return d3.svg.arc().innerRadius(i * arcSize + (innerRadius + ((arcSize / 2) - 2))).outerRadius((i + 1) * arcSize - ((arcSize / 2)) + (innerRadius));
});
var pieData = data.map(function(obj, i) {
return [{
value: obj.value * 0.75,
arc: arcs[i],
object: obj
},
{
value: (100 - obj.value) * 0.75,
arc: arcsGrey[i],
object: obj
},
{
value: 100 * 0.25,
arc: arcs[i],
object: obj
}
];
});
var pie = d3.layout.pie().sort(null).value(function(d) {
return d.value;
});
var g = graphGroup.selectAll(null).data(pieData).enter()
.append('g')
.attr('transform', 'translate(' + (width / 2 + (j * 300)) + ',' + width / 2  + ') rotate(180)');
var thisClass = "g"+String(j);
var gText = graphGroup.selectAll('.'+thisClass).data([{}]).enter()
.append('g')
.classed('textClass', true)
.attr('transform', 'translate(' + (width / 2 + (j * 300)) + ',' + width / 2 + ') rotate(180)');

g.selectAll('path').data(function(d) {
return pie(d);
}).enter().append('path')
.attr('id', function(d, i) {
if (i == 1) {
return "Text" + d.data.object.label
}
})
.attr('d', function(d) {
return d.data.arc(d);
}).attr('fill', function(d, i) {
if (d.data.object.neg == false) {
return i == 0 ? d.data.object.color : i == 1 ? '#D3D3D3' : 'none';
} else {
return i == 0 ? 'red' : i == 1 ? '#D3D3D3' : 'none';
}
}).attr('class', 'segments');
/*
g.selectAll('.segments').attr('fill', function(d,i) {
return d.data.object.neg==true ? 'red' : 'none';
});
*/
graphGroup.selectAll('g').each(function(d, index) {
var el = d3.select(this);
var path = el.selectAll('path').each(function(r, i) {
if (i === 1) {
var centroid = r.data.arc.centroid({
startAngle: r.startAngle + 0.05,
endAngle: r.startAngle + 0.001 + 0.05
});
var lableObj = r.data.object;
var thisLength = this.getTotalLength();
g.append('text')
.attr('font-size', ((5 * width) / 100))
.attr('dominant-baseline', 'central')
/*.attr('transform', "translate(" + centroid[0] + "," + (centroid[1] + 10) + ") rotate(" + (180 / Math.PI * r.startAngle + 7) + ")")
.attr('alignment-baseline', 'middle')*/
.append("textPath")
.attr("textLength", function(d, i) {
return 0;
})
.attr("xlink:href", "#Text" + r.data.object.label)
.attr("startOffset", function() {
return thisLength-22;
//return index === 2 || index === 5 ? thisLength - 22 : 5
})
.attr('font-weight', 'normal')
.attr("dy", '-3em')
.text(function(d) {
if (lableObj.neg == true) {
return '-' + lableObj.marker;
} else {
return lableObj.marker;
}
});
}
if (i === 0) {
var centroidText = r.data.arc.centroid({
startAngle: r.startAngle,
endAngle: r.startAngle
});
var lableObj = r.data.object;
gText.append('text')
.attr('font-size', ((5 * width) / 100))
.text(lableObj.label)
.attr('transform', "translate(" + (centroidText[0] - ((1.5 * width) / 100)) + "," + (centroidText[1] + ") rotate(" + (180) + ")"))
.attr('dominant-baseline', 'central');
}
});
});
}
<script src="https://d3js.org/d3.v3.min.js"></script>

在使用.selectAll(null)显示多个圆环图方面得到一些帮助后,我一直在徒劳地从第 2 和第 3 个图表中删除文本"工件"。数据标签出于某种原因而重复,从代码段中可见。

我尝试的是创建一个特定于当前迭代的单独类:

var thisClass = "g"+String(j);
var gText = graphGroup.selectAll('.'+thisClass).data([{}]).enter()
.append('g')
.classed('textClass', true)
.attr('transform', 'translate(' + (width / 2 + (j * 300)) + ',' + width / 2 + ') rotate(180)');

问题

为什么我的基于类的解决方案无法按预期工作?我愿意接受其他建议,只显示正确的数据标签(旋转似乎没有保留,不知道为什么(。

您应该使用 d3 内部数据迭代,而不是自己迭代选择。

问题的解决方案是为每个图表创建一个组元素,您可以对其进行迭代:

const arcGroup = g
.selectAll(".group-arc")
.data(d => pie(d))
.enter()
.append("g")
.attr("class", "group-arc");

这将允许您根据pie(d)数组中的数据数量将元素附加到您的组中,在您的情况下: 3. 之后,对于数据组中的每个元素,您可以附加路径(这将向组添加一个path子元素。pie(d( 的结果将是每个图表的 3 个数据点 [开始、值、结束] 的数组 - 这将导致在g元素上迭代 3 次,对每个.group-arc迭代 3 倍,将 [开始、值、结束] 作为数据点。

arcGroup
.append("path")
.attr("id", function(d, i) {
if (i == 1) {
return "Text" + d.data.object.label;
}
})
.attr("d", function(d) {
return d.data.arc(d);
})
.attr("fill", function(d, i) {
if (d.data.object.neg == false) {
return i == 0 ? d.data.object.color : i == 1 ? "#D3D3D3" : "none";
} else {
return i == 0 ? "red" : i == 1 ? "#D3D3D3" : "none";
}
})

下一步是添加文本属性。每个数据对象d都是一个包含 3 个饼图元素的数组:开始、值、结束。您可以使用i索引来了解当前正在呈现的标签(0 - 饼图标签,1 - 值(。相同的指数可用于计算旋转。

arcGroup
.append("text")
.text(function(d, i) {
var lableObj = d.data.object;
if (i === 0) {
return lableObj.label;
} else if (i === 1) {
if (lableObj.neg === true) {
return "-" + lableObj.marker;
} else {
return lableObj.marker;
}
}
})
.attr("transform", (d, i) => {
var centroidText = d.data.arc.centroid({
startAngle: d.startAngle,
endAngle: d.startAngle
});
return (
"translate(" +
(centroidText[0] - (1.5 * width) / 100) +
"," +
(centroidText[1] + ") rotate(" + 180 + ")")
);
});

或者,您可以使用.call方法在单个路径组上执行代码:

arcGroup.call(elem => {
elem.forEach(arcGr => {
const { parentNode } = arcGr;
const groupAdd = d3.select(parentNode);
const arcData = d3.select(arcGr[0]).data();
groupAdd
.append("text")
.attr("alignment-baseline", "middle")
.text(d => d[0].object.label)
.attr("transform", d => {
var centroidText = arcData[0].data.arc.centroid({
startAngle: arcData[0].startAngle,
endAngle: arcData[0].startAngle
});
console.log(centroidText);
return `translate(${centroidText.join(",")})rotate(180)`;
});
groupAdd
.append("text")
.text(d => d[1].object.marker)
.attr("alignment-baseline", "hanging")
.attr("text-anchor", "end")
.attr("dx", "-2")
.attr("transform", d => {
var path = arcData[0].data.arc(arcData[0]);
var coords = path.split("L")[1].split("A")[0];
return `translate(${coords})rotate(180)rotate(${(arcData[0].endAngle *
180) /
Math.PI})`;
});
});
});

上面的实现可能不是确切的所需输出,但我希望您知道如何简化实现。我还注意到您正在使用 d3 v3 - 如果可能的话,您应该切换到 v5。

以下是完整的代码:


var margins = {
top: 20,
left: 50,
bottom: 20,
right: 20
};
var width = 300;
var arcSize = (6 * width) / 100;
var innerRadius = arcSize * 3;
var extent = 1800;
var data2 = [
[
{
value: (231 / extent) * 100,
marker: 231,
label: "Collective",
color: "#b8cce4",
neg: false
},
{
value: (233 / extent) * 100,
marker: 233,
label: "Targeted",
color: "#95b3d7",
neg: false
},
{
value: (45 / extent) * 100,
marker: 45,
label: "Specific",
color: "#4f81b9",
neg: false
}
],
[
{
value: (171 / extent) * 100,
marker: 171,
label: "Collective",
color: "#b8cce4",
neg: false
},
{
value: (1712 / extent) * 100,
marker: 1712,
label: "Targeted",
color: "#95b3d7",
neg: false
},
{
value: (1 / extent) * 100,
marker: 1,
label: "Specific",
color: "#4f81b9",
neg: false
}
],
[
{
value: (207 / extent) * 100,
marker: 207,
label: "Collective",
color: "#b8cce4",
neg: false
},
{
value: (975 / extent) * 100,
marker: 975,
label: "Targeted",
color: "#95b3d7",
neg: false
},
{
value: (153 / extent) * 100,
marker: 153,
label: "Specific",
color: "#4f81b9",
neg: false
}
]
];
var svg = d3
.select("body")
.append("svg")
.attr("width", 1100 + 100)
.attr("height", 1100 + 100);
var graphGroup = svg
.append("g")
.attr("transform", "translate(" + margins.left + "," + margins.top + ")");
for (var j = 0; j < data2.length; j++) {
var data = data2[j];
var arcs = data.map(function(obj, i) {
return d3.svg
.arc()
.innerRadius(i * arcSize + innerRadius)
.outerRadius((i + 1) * arcSize - width / 100 + innerRadius);
});
var arcsGrey = data.map(function(obj, i) {
return d3.svg
.arc()
.innerRadius(i * arcSize + (innerRadius + (arcSize / 2 - 2)))
.outerRadius((i + 1) * arcSize - arcSize / 2 + innerRadius);
});
var pieData = data.map(function(obj, i) {
return [
{
value: obj.value * 0.75,
arc: arcs[i],
object: obj
},
{
value: (100 - obj.value) * 0.75,
arc: arcsGrey[i],
object: obj
},
{
value: 100 * 0.25,
arc: arcs[i],
object: obj
}
];
});
var pie = d3.layout
.pie()
.sort(null)
.value(function(d) {
return d.value;
});
var g = graphGroup
.selectAll(null)
.data(pieData)
.enter()
.append("g")
.attr(
"transform",
"translate(" + (width / 2 + j * 300) + "," + width / 2 + ") rotate(180)"
);
const arcGroup = g
.selectAll(".group-arc")
.data(d => pie(d))
.enter()
.append("g")
.attr("class", "group-arc");
arcGroup
.append("path")
.attr("id", function(d, i) {
if (i == 1) {
return "Text" + d.data.object.label;
}
})
.attr("d", function(d) {
return d.data.arc(d);
})
.attr("fill", function(d, i) {
if (d.data.object.neg == false) {
return i == 0 ? d.data.object.color : i == 1 ? "#D3D3D3" : "none";
} else {
return i == 0 ? "red" : i == 1 ? "#D3D3D3" : "none";
}
});
arcGroup.call(elem => {
elem.forEach(arcGr => {
const { parentNode } = arcGr;
const groupAdd = d3.select(parentNode);
const arcData = d3.select(arcGr[0]).data();
groupAdd
.append("text")
.attr("alignment-baseline", "middle")
.text(d => d[0].object.label)
.attr("transform", d => {
var centroidText = arcData[0].data.arc.centroid({
startAngle: arcData[0].startAngle,
endAngle: arcData[0].startAngle
});
console.log(centroidText);
return `translate(${centroidText.join(",")})rotate(180)`;
});
groupAdd
.append("text")
.text(d => d[1].object.marker)
.attr("alignment-baseline", "hanging")
.attr("text-anchor", "end")
.attr("dx", "-2")
.attr("transform", d => {
var path = arcData[0].data.arc(arcData[0]);
var coords = path.split("L")[1].split("A")[0];
return `translate(${coords})rotate(180)rotate(${(arcData[0].endAngle *
180) /
Math.PI})`;
});
});
});

/** 
//Previous version
arcGroup
.append("text")
.text(function(d, i) {
var lableObj = d.data.object;
if (i === 0) {
return lableObj.label;
} else if (i === 1) {
if (lableObj.neg === true) {
return "-" + lableObj.marker;
} else {
return lableObj.marker;
}
}
})
.attr("transform", (d, i) => {
var centroidText = d.data.arc.centroid({
startAngle: d.startAngle,
endAngle: d.startAngle
});
return (
"translate(" +
(centroidText[0] - (1.5 * width) / 100) +
"," +
(centroidText[1] + ") rotate(" + 180 + ")")
);
});
*/
}

最新更新