d3.js:圆环图中文本的垂直对齐
我在d3.js中有一个简单的甜甜圈图表,它只用于比较2到3个项目 我想将图例和值合并为图表中心的文本 在当前的实现中,对于3个项目,文本对齐是可以的,但是对于2个项目,它不会调整。路线有点硬编码:d3.js:圆环图中文本的垂直对齐,d3.js,vertical-alignment,D3.js,Vertical Alignment,我在d3.js中有一个简单的甜甜圈图表,它只用于比较2到3个项目 我想将图例和值合并为图表中心的文本 在当前的实现中,对于3个项目,文本对齐是可以的,但是对于2个项目,它不会调整。路线有点硬编码: var l = svg.selectAll('.legend') .data(data) .enter().append('g') .attr('class', 'legend'); l.append('text')
var l = svg.selectAll('.legend')
.data(data)
.enter().append('g')
.attr('class', 'legend');
l.append('text')
.attr('x', 0)
.attr('y', function(d, i) { return i * 40 - radius / 2 + 10; })
.attr('class', function(d, i){return 'legend-label data-label value-' + (i+1)})
.text(function(d, i) { return d + '%'; });
l.append('text')
.attr('x', 0)
.attr('y', function(d, i) { return i * 40 - radius / 2 + 22; })
.attr('class', function(d, i){return 'legend-label units-label value-' + (i+1)})
.text(function(d, i) { return legend[i]; });
如何使文本对齐方式更灵活,使其在2项和3项中均匀分布?有什么我可以用的吗?我可以用吗
这是完整的脚本
/**
* Donut chart for d3.js
*/
function donutChart() {
var width = 420,
height = 420,
radius = 0,
factor = 0.7;
var legend = ['Low', 'Medium', 'High'];
function chart(selection) {
selection.each(function(data) {
if (radius == 0) {
radius = Math.min(width, height) / 2 - 10;
}
var arc = d3.svg.arc()
.innerRadius(radius * factor)
.outerRadius(radius);
var pie = d3.layout.pie()
.sort(null)
.value(function(d) { return d; });
var svg = d3.select(this).append('svg')
.attr('width', width)
.attr('height', height)
.attr('class', 'donut')
.append('g')
.attr('transform', 'translate(' + width / 2 + ',' + height / 2 + ')');
var g = svg.selectAll('.arc')
.data(pie(data))
.enter().append('g')
.attr('class', 'arc');
g.append('path')
.attr('d', arc)
.attr('class', function(d, i){return 'value-' + (i+1)})
.style('stroke', '#fff');
var l = svg.selectAll('.legend')
.data(data)
.enter().append('g')
.attr('class', 'legend');
l.append('text')
.attr('x', 0)
.attr('y', function(d, i) { return i * 40 - radius / 2 + 10; })
.attr('class', function(d, i){return 'legend-label data-label value-' + (i+1)})
.text(function(d, i) { return d + '%'; });
l.append('text')
.attr('x', 0)
.attr('y', function(d, i) { return i * 40 - radius / 2 + 22; })
.attr('class', function(d, i){return 'legend-label units-label value-' + (i+1)})
.text(function(d, i) { return legend[i]; });
});
}
chart.width = function(_) {
if (!arguments.length) return width;
width = _;
return chart;
};
chart.height = function(_) {
if (!arguments.length) return height;
height = _;
return chart;
};
chart.radius = function(_) {
if (!arguments.length) return radius;
radius = _;
return chart;
};
chart.factor = function(_) {
if (!arguments.length) return factor;
factor = _;
return chart;
};
chart.legend = function(_) {
if (!arguments.length) return legend;
legend = _;
return chart;
};
return chart;
}
d3.select('#chart')
.datum([78, 20])
.call(donutChart()
.width(220)
.height(220)
.legend(['This Thing', 'That Thing'])
);
d3.select('#chart2')
.datum([63, 20, 15])
.call(donutChart()
.width(220)
.height(220)
.legend(['This Thing', 'That Thing', 'Another Thing'])
);
我会将所有图例文本元素放在一个公共的
元素中,然后可以将该元素垂直转换为中心。在SVG中确定实际的文本大小总是一件痛苦的事情,您可以查看getBBox()
,了解更多关于这方面的信息。但是使用预先确定的文本高度可以很好地工作。下面是一些计算(为了清晰起见,分为许多步骤):
修改的JSFIDLE:谢谢@redmallard!我欣赏这种方法的简单性。事实上,我不久前用一种我不完全理解的更复杂的方法解决了这个问题(一个晚上的修补):
// hardcoding 36 here, you could use getBBox to get the actual SVG text height using sample text if you really wanted.
var legendItemHeight = 36;
// calculated height of all legend items together
var actualLegendHeight = data.length * legendItemHeight;
// inner diameter
var availableLegendHeight = radius * factor * 2;
// y-coordinate of first legend item (relative to the center b/c the main svg <g> element is translated
var legendOffset = (availableLegendHeight - actualLegendHeight) / 2 - (radius*factor);
// append all the legend items to a common group which is translated
var l = svg.selectAll('.legend')
.data(data)
.enter().append('g')
.attr('class', 'legend')
.attr('transform', function(d, i) {
return 'translate(0, '+ (legendOffset + i * legendHeight)+')';
});