How to match text width to circle size in D3 circle pack
Asked Answered
S

2

8

Using D3 I display a bunch of circles in different sizes, each filled with text. I'm stuck with finding the correct font size so that the text fits correct in the circle, depending of it's size and the length of the text. Long text should possibly be broken up in more lines. Here is my code:

var data = {
    "name": "",
    "children": [
        { "name": "This is a tag", "value": 242 },
        { "name": "Circle", "value": 162 },
        { "name": "Tree", "value": 80 },
        { "name": "My sentence is very long and needs breaks", "value": 80 },
    ]
}

var diameter = 300,
    format = d3.format(",d");

var bubble = d3.layout.pack()
    .sort(null)
    .size([diameter, diameter])
    .padding(1.5);

var svg = d3.select("body").append("svg")
    .attr("width", diameter)
    .attr("height", diameter)
    .attr("class", "bubble");

d3.json(data, function(error, root) {
  var node = svg.selectAll(".node")
      .data(bubble.nodes(data)
      .filter(function(d) { return !d.children; }))
    .enter().append("g")
      .attr("class", "node")
      .attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });

  node.append("circle")
      .attr("r", function(d) { return d.r; })
      .style("fill", function(d) { return '#f88' });

  // text part
  node.append("text")
      .attr("dy", ".3em")
      .style("text-anchor", "middle")
      .style("font-size", function(d) { return Math.round(d.r/3)+'px'; })
      .text(function(d) { return d.name.substring(0, d.r / 3); });
});

d3.select(self.frameElement).style("height", diameter + "px");

I have created a fiddle as well on http://jsfiddle.net/L4nMx/ I think I should calculate the width of the text and modify the font size until it matches the circle's size or something like that. Or is there any "strech" function to do this the easy way?

Sporadic answered 20/5, 2014 at 12:10 Comment(4)
This question is about a similar problem, it can be helpful.Tabulator
Hi @PabloNavarro, I looked at the answers and a link in a comment by @VividD, but none talk about the text wrap method in this popular gist from Mike. I know it does not really leverage the circle but has some flexibility on splitting the text and does not use foreignObject which is not supported by IE...what do you think?Piatt
It's a bit more involved, but a good solution anyway. A definitive solution to this issue should be implemented in SVG. There are some steps in that direction though.Tabulator
Thanks, these solutions are helpful even though there are only about wrapping text.Sporadic
S
9

This solution is fine for me for now. It's not accurate maths but fits anyway.

See it in action on http://jsfiddle.net/L4nMx/3/

  .style("font-size", function(d) {
      var len = d.name.substring(0, d.r / 3).length;
      var size = d.r/3;
      size *= 10 / len;
      size += 1;
      return Math.round(size)+'px';
  })
  .text(function(d) {
      var text = d.name.substring(0, d.r / 3);
      return text;
  });

Next step would be to break long text into multiple lines so you could enlarge font sizes in such cases but I didn't manage to solve this. It's not easy in SVG because simple line breaks are not possible. Maybe the wrapping solutions from the comments in the question can be added here - somehow...

Sporadic answered 21/5, 2014 at 8:41 Comment(0)
L
0

There is this bl.ocks https://bl.ocks.org/mbostock/1846692

which is basically

 node.append("circle")
      .attr("r", function(d) { return d.r; });

  node.append("text")
      .text(function(d) { return d.name; })
      .style("font-size", function(d) { return Math.min(2 * d.r, (2 * d.r - 8) / this.getComputedTextLength() * 24) + "px"; })
      .attr("dy", ".35em");
Lagas answered 30/9, 2017 at 3:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.