How can I have D3.js automatically adjust font-size for each node based on their individual radius/diameter?
I use a style that allows automatic inc
To offset text within a circle, rather than running along the diameter, I implement this differently:
dy
shifts a text node up or down within the circle and is used to calculate the width or the chord to size the text.
The scale is then stored in a data attribute on the text element, rather than modifying the source data.
jsfiddle
function appendScaledText(parentGroup, textVal, dyShift) {
parentGroup
.append("text")
.attr("dy", dyShift)
.attr("text-anchor", "middle")
.attr("dominant-baseline", "central")
.attr("font-family", "sans-serif")
.attr("fill", "white")
.text(textVal)
.style("font-size", "1px")
.each(getSize)
.style("font-size", function() {
return d3.select(this).attr("data-scale") + "px";
});
}
function getSize() {
var d3text = d3.select(this);
// in other cases could be parentElement or nextElementSibling
var circ = d3.select(this.previousElementSibling);
var radius = Number(circ.attr("r"));
var offset = Number(d3text.attr("dy"));
// TODO: this could be bounding box instead
var textWidth = this.getComputedTextLength();
// TODO: could adjust based on ratio of dy to radius
var availWidth = chordWidth(Math.abs(offset), radius);
// fixed 15% 'padding', could be more dynamic/precise based on above TODOs
availWidth = availWidth * 0.85;
d3text.attr("data-scale", availWidth / textWidth);
}
function chordWidth(dFromCenter, radius) {
if (dFromCenter > radius) return Number.NaN;
if (dFromCenter === radius) return 0;
if (dFromCenter === 0) return radius * 2;
// a^2 + b^2 = c^2
var a = dFromCenter;
var c = radius;
var b = Math.sqrt(Math.pow(c, 2) - Math.pow(a, 2)); // 1/2 of chord length
return b * 2;
}