Fit Text into SVG Element (Using D3/JS)
Asked Answered
A

4

13

I want to write text inside a rectangle I create as follows:

body = d3.select('body')

svg = body.append('svg').attr('height', 600).attr('width', 200)

        rect = svg.append('rect').transition().duration(500).attr('width', 150)
                        .attr('height', 100)
                        .attr('x', 40)
                        .attr('y', 100)
                        .style('fill', 'white')
                        .attr('stroke', 'black')

        text = svg.append('text').text('This is some information about whatever')
                        .attr('x', 50)
                        .attr('y', 150)
                        .attr('fill', 'black')​

However, as you can see (http://jsfiddle.net/Tmj7g/3/) the text gets cut off. Any nifty ways to write a paragraph inside of the svg rectangle created? Thanks,

Anlace answered 13/6, 2012 at 2:56 Comment(1)
This technique might come in handy. It works with the current svg spec and without foreignObject element bl.ocks.org/mbostock/7555321Cryoscopy
D
16

The answer to this question might be relevant. SVG provides no way of wrapping text automatically, but you can embed HTML within SVGs and then use a div for example.

I've updated the jsfiddle here, but it doesn't work that well together with the animation. If you want to make it work properly and behave like any other SVG element, you'll have to pre-compute the line breaks and insert them manually.

Disoperation answered 13/6, 2012 at 8:58 Comment(3)
Does this work on IE9? If so what are the alternatives? I know this was answered long ago.Bobsled
Yes, it doesnt work at all in IE9 because it doesnt support foreign Objects. Are there any alternatives to wrapping text in D3.js?Bobsled
This is not so much a D3 but an SVG issue. If you don't need fancy graphics, you could simply generate HTML. If you need SVG, your can use its tspan elements to wrap text for example, but this is much less convenient.Disoperation
C
14

To make it work with the animations just enclose in a group element and animate that one instead. http://jsfiddle.net/MJJEc/

body = d3.select('body')
svg = body.append('svg')
                    .attr('height', 600)
                    .attr('width', 200);
    var g = svg.append('g').attr("transform" ,"scale(0)");
    rect = g.append('rect')
                    .attr('width', 150)
                    .attr('height', 100)
                    .attr('x', 40)
                    .attr('y', 100)
                    .style('fill', 'none')
                    .attr('stroke', 'black')
    text = g.append('foreignObject')
                    .attr('x', 50)
                    .attr('y', 130)
                    .attr('width', 150)
                    .attr('height', 100)
                    .append("xhtml:body")
                    .html('<div style="width: 150px;">This is some information about whatever</div>')

    g.transition().duration(500).attr("transform" ,"scale(1)");
Company answered 12/3, 2013 at 15:2 Comment(0)
I
2

I had a similar issue and found a reasonable solution by calculating the width of my box. Secondly, I figured out that on average the character width for my current font is about 8. Next I simply do a substring on the text to be displayed. That seems to work perfectly in most cases.

var rectText = rectangles.append("text")
    .text(function(d) {

        TextBoxLength = timeScale(dateFormat.parse(d.endTime)) - timeScale(dateFormat.parse(d.startTime));
        return d.task.substring(0, Math.floor(TextBoxLength / 8));
    })
    .attr("x", function(d) {
        return (timeScale(dateFormat.parse(d.endTime)) - timeScale(dateFormat.parse(d.startTime))) / 2 + timeScale(dateFormat.parse(d.startTime)) + theSidePad;
    })
    .attr("y", function(d, i) {
        return d.position * theGap + 14 + theTopPad;
    })
    .attr("font-size", 12)
    .attr("text-anchor", "middle")
    .attr("text-height", theBarHeight)
    .attr("fill", "#000000");
Interlocution answered 17/2, 2015 at 19:2 Comment(0)
S
2

Another approach, when trying to fit a straight line of text into an svg element, could use the strategy found in http://bl.ocks.org/mbostock/1846692:

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");
Stucker answered 6/5, 2016 at 19:30 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.