c3
c3 copied to clipboard
sub-bar label position in stacked horizontal bar chart
hey there,
i'm trying to create a stacked bar chart with labels' positions configured for each segment.
i have a jsbin here.
this is what it looks like now:
my ideal solution would be to have the sub-bar's respective labels aligned:
- on the leftmost boundary of the leftmost sub-bar,
- on the rightmost boundary of the rightmost sub-bar,
- centered for the sub-bars in between
but i'd also be happy just having the labels centered inside each sub-bar.
i've gone through the reference material as well as examples and not found any indication that c3 has specialized API for positioning sub-bar labels.
can this be accomplished with some workaround?
thanks in advance.
for the record, here's a really dirty workaround for this particular situation making use of onrendered
, centering the text per sub-bar. should be easy to extend for the left/right requirement.
onrendered: function () {
var centers = [];
var widths = [];
d3.selectAll(".c3-chart-bars .c3-target")[0].forEach(function(el) {
var bbox = el.getBBox();
centers.push(bbox.width/2 + bbox.x);
});
var i = 0;
d3.selectAll(".c3-chart-text.c3-target")[0].forEach(function(el) {
var bbox = el.getBBox();
var newCenter = centers[i++];
el.querySelector("text").setAttribute("text-anchor", "middle");
el.setAttribute("transform", "translate(-" + (bbox.x - newCenter + bbox.width/2) + ",0)");
});
}
My solution required me going into the C3.js library and making changes What I did was add another config property "labelCentered" and if that is set to true then center the labels. Any horizontal chart will work, stacked or regular.
` ChartInternal.prototype.getXForText = function (points, d, textElement) { var $$ = this, box = getBBox(textElement), xPos, padding; var config = $$.config;
if ($$.config.axis_rotated) {
padding = $$.isBarType(d) ? 4 : 6;
if (config.labelCentered) {
var center = (points[1][1] - points[0][1]) / 2;
var newPadding = (center <= padding ? padding : 0) - box.width / 2;
var totalLeft = center + newPadding + points[0][1] > box.width ? center + newPadding + points[0][1] : padding;
xPos = totalLeft * (d.value < 0 ? -1 : 1)
} else {
xPos = points[2][1] + padding * (d.value < 0 ? -1 : 1);
}
} else {
xPos = $$.hasType('bar') ? (points[2][0] + points[0][0]) / 2 : points[0][0];
} // show labels regardless of the domain if value is null
if (d.value === null) {
if (xPos > $$.width) {
xPos = $$.width - box.width;
} else if (xPos < 0) {
xPos = 4;
}
}
return xPos;
};
`