D3.js Automatic font size based on individual radius / diameter nodes

How can I set D3.js to automatically adjust the font size for each node based on their individual radius / diameter?

I use a style that allows me to automatically increase the size

node.append("text") .attr("dy", ".3em") .style("text-anchor", "middle") .text(function(d) { return d.className.substring(0, dr / 3); }) .style("font-size", "10px") // initial guess //This is what gives it increased size... .style("font-size", function(d) { return (2 * dr - 10) / this.getComputedTextLength() * 10 + "px"; }) 

; * 10 + "px"; })

This effect removes text from smaller nodes. I also have a zoom function that I can zoom in on a point that initially covers 12 pixels to cover the entire screen.

 .call(d3.behavior.zoom().scaleExtent([1, 200]).on("zoom", zoom)) 

Is it possible to automatically format node -font individually for recording with appropriate sizes, so when the image scale of the called node -font will seem proportional to node - is the size compared to one font suitable for everyone?

enter image description here

Right Lists: NAME (SIZE)
I would like to learn with examples. Thus, at the size of the image, the little green dot north of the moving circle next to P will have black unreadable words until we zoom in to see what is written on the circle. The goal is to have a proportional readable font when zoomed in ..?

+8
javascript
source share
3 answers

You can do this by dynamically setting the size of the text depending on the size of the container. To do this, you need to add text, get its bounding box, get the bounding box of the container element and get the correct font size based on the current font size and these bounding boxes.

The code will look something like this.

 // ... .append("text") .text("text") .style("font-size", "1px") .each(getSize) .style("font-size", function(d) { return d.scale + "px"; }); function getSize(d) { var bbox = this.getBBox(), cbbox = this.parentNode.getBBox(), scale = Math.min(cbbox.width/bbox.width, cbbox.height/bbox.height); d.scale = scale; } 
+13
source share

Thanks to the OP and the accepted answer (both highlighted); I ended it a little differently because my text was shifted inside the circle, and did not work directly in its diameter. The text node has a dy value to move it up or down in a circle, and I use this to figure out which chord I need to measure in a circle so that my text still automatically sets with the desired height offset.This also helped me better keep The calculated size in the data attribute in the text element, and not change the original data. I thought it could be useful for anyone who stumbles upon this in the future.

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); var circ = d3.select(this.previousElementSibling); // in other cases could be parentElement or nextElementSibling var radius = Number(circ.attr("r")); var offset = Number(d3text.attr("dy")); var textWidth = this.getComputedTextLength(); // TODO: this could be bounding box instead var availWidth = chordWidth(Math.abs(offset), radius); // TODO: could adjust based on ratio of dy to radius availWidth = availWidth * 0.85; // fixed 15% 'padding' for now, could be more dynamic/precise based on above TODOs d3text.attr("data-scale", availWidth / textWidth); // sets the data attribute, which is read in the next step } 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; } 
+1
source share

In addition, you can create a text label embedded in each node as follows:

 this.g.append("g") .attr("class", "labels") .selectAll(".mytext") .data(NODE_DATA) .enter() .append("text") .text(function (d) { return d.LabelText; // Here label text is the text that you want to show in the node }) .style("font-size", "1px") .attr("dy", ".35em") // You can adjust it .each(function (d) { var r = Number(d.Size), a = this.getComputedTextLength(), c=0.35, // Same as dy attribute value b = 2*Math.sqrt(r*rc*c), s = Math.min(r, b/a); d.fs = s; }) .style("font-size", function (d) { return d.fs + "px"; }) 
0
source share

All Articles