Wrap text within circle

2019-01-23 17:40发布

I'm using d3 to draw a UML diagram and would like to wrap text within the shapes drawn with d3. I've gotten as far as the code below and can't find a solution to make the text 'fit' within my shape (see image below).

var svg =  d3.select('#svg')
    .append('svg')
        .attr('width', 500)
        .attr('height', 200);

var global = svg.append('g');

global.append('circle')
      .attr('cx', 150)
      .attr('cy', 100)
      .attr('r', 50);

global.append('text')
  .attr('x', 150)
  .attr('y', 100)
  .attr('height', 'auto')
  .attr('text-anchor', 'middle')
  .text('Text meant to fit within circle')
  .attr('fill', 'red');

result

4条回答
趁早两清
2楼-- · 2019-01-23 17:46

Here is the best I could do.

enter image description here

I want to center and wrap a text inside a circle or rect in SVG. The text should remain centered (horizontal/vertical) whatever the text length.

svg {
    width: 600px;
    height: 200px;
    background-color: yellow;
}
.circle {
    background-color: blue;
    height: 100%;
    border-radius: 100%;
    text-align: center;
    line-height: 200px;
    font-size: 30px;
}
.circle span {
    line-height: normal;
    display:inline-block;
    vertical-align: middle;
    color: white;
    text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.8);
}
<svg>
    <foreignObject width="200" height="200" x="100" y="100" transform="translate(-100,-100)">
        <div class="circle">
            <span>Here is a</span>
        </div>
    </foreignObject>

    <foreignObject width="200" height="200" x="300" y="100" transform="translate(-100,-100)">
        <div class="circle">
            <span>Here is a paragraph</span>
        </div>
    </foreignObject>

    <foreignObject width="200" height="200" x="500" y="100" transform="translate(-100,-100)">
        <div class="circle">
            <span>Here is a paragraph that requires word wrap</span>
        </div>
    </foreignObject>
</svg>

The transform attribute is not mandatory, I'm using a translate(-r, -r) so that the (x,y) of the foreignObject is like the (cx, cy) of the SVG circle, and width, height = 2*r with r the radius.

I did this to use as nodes within a D3 force layout. I leave as an exercise to translate this snippet into javascript D3's style.

查看更多
放我归山
3楼-- · 2019-01-23 17:46

It's not ideal, but @Pablo.Navarro's answer led me to the following.

var svg =  d3.select('#svg')
  .append('svg')
    .attr('width', 500)
    .attr('height', 200);

var radius = 60,
    x      = 150,
    y      = 100,
    side   = 2 * radius * Math.cos(Math.PI / 4),
    dx     = radius - side / 2;

var global = svg.append('g')
  .attr('transform', 'translate(' + [ dx, dx ] + ')');

global.append('circle')
  .attr('cx', x)
  .attr('cy', y)
  .attr('r', radius);

global.append('foreignObject')
  .attr('x', x - (side/2))
  .attr('y', y - (side/2))
  .attr('width', side)
  .attr('height', side)
  .attr('color', 'red')
  .append('xhtml:p')
    .text('Text meant to fit within circle')
    .attr('style', 'text-align:center;padding:2px;margin:2px;');

Result

result

查看更多
对你真心纯属浪费
4楼-- · 2019-01-23 17:58

If you add your content inside a <text> element immediately below the SVG shape, then you can use D3plus' .textwrap() function to do exactly this. I quote from the documentation:

Using d3plus.textwrap, SVG <text> elements can be broken into separate <tspan> lines, as HTML does with <div> elements.... D3plus automatically detects if there is a <rect> or <circle> element placed directly before the <text> container element in DOM, and uses that element's shape and dimensions to wrap the text. If it can't find one, or that behavior needs to be overridden, they can manually be specified using .shape( ), .width( ), and .height( ).

I've created a codepen to better illustrate this since the examples in the documentation can be a little confusing: http://codepen.io/thdoan/pen/rOPYxE

查看更多
男人必须洒脱
5楼-- · 2019-01-23 18:08

SVG doesn't provide text wrapping, but using foreignObject you can achieve a similar effect. Assuming that radius is the radius of the circle, we can compute the dimensions of a box that will fit inside the circle:

var side = 2 * radius * Math.cos(Math.PI / 4),
    dx = radius - side / 2;

var g = svg.append('g')
    .attr('transform', 'translate(' + [dx, dx] + ')');

g.append("foreignObject")
    .attr("width", side)
    .attr("height", side)
    .append("xhtml:body")
    .html("Lorem ipsum dolor sit amet, ...");

The group should be displaced a small amount to have the text centered. I know that this is not exactly what is asked, but it can be helpful. I wrote a small fiddle. The result will look like this:

enter image description here

查看更多
登录 后发表回答