PassionateAbtCoding
PassionateAbtCoding

Reputation: 163

How to give padding on top/bottom/left/right to text that is contained inside a rect

How do I give padding on top/bottom/left/right to text that is contained inside a D3 rect component? I was only able to give it to the left side of text, though I am not sure that is indeed the right way. I have been struggling over this for a couple of days and it would be great if anyone could help me figure out a way to do it.

enter image description here

Below is the example code I have so far ->

https://jsfiddle.net/passionateCoder/4uf3h2L8/

<!DOCTYPE html>
<html>
  <head>
    <!-- <script
      data-require="[email protected]"
      data-semver="3.5.3"
      src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.3/d3.js"
    ></script> -->
    <script src="https://d3js.org/d3.v6.min.js"></script>
  </head>

  <body>
    <script>
      function wrap(text, width) {
        text.each(function () {
          var text = d3.select(this),
            words = text.text().split(/\s+/).reverse(),
            word,
            line = [],
            lineNumber = 0,
            lineHeight = 1.1, // ems
            y = text.attr('y'),
            dy = parseFloat(text.attr('dy')),
            tspan = text
              .text(null)
              .append('tspan')
              .attr('x', 10)
              .attr('y', y)
              .attr('dy', dy + 'em');
          while ((word = words.pop())) {
            line.push(word);
            tspan.text(line.join(' '));
            if (tspan.node().getComputedTextLength() > width) {
              line.pop();
              tspan.text(line.join(' '));
              line = [word];
              tspan = text
                .append('tspan')
                .attr('x', 10)
                .attr('y', y)
                .attr('dy', ++lineNumber * lineHeight + dy + 'em')
                .text(word);
            }
          }
        });
      }

      

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

      var longText =
        'Now is the time for all good men to come to the aid of their countryNow is the time for all good men to come to the aid of their countryNow is the time for all good men to come to the aid of their countryNow is the time for all good men to come to the aid of their country';

      var totHeight = 0;

      drawRect();

      function drawRect() {
        var someWidth = 212;

        var g = svg
          .append('g')
          .attr('transform', 'translate(20,' + totHeight + ')');

        var rect = g
          .append('rect')
          .style('fill', 'steelblue')
          .attr('width', someWidth)
          .attr('height', 1);

        var txt = g
          .append('text')
          .text(longText)
          .attr('x', 4)
          .attr('y', 10)
          .attr('dy', '.71em')
          .style('fill', 'white')
          .call(wrap, someWidth);

        var height = txt.node().getBBox().height + 25;
        totHeight += height + 10;
        rect.attr('height', height);
      }
    </script>
  </body>
</html>

Upvotes: 2

Views: 1443

Answers (1)

Gerardo Furtado
Gerardo Furtado

Reputation: 102174

Firstly, in the wrap function, I'd advise you to set the x attribute according to the attribute you used in the text selection:

x = text.attr('x')

After that, it's simply a matter of subtracting both margins from someWidth. For instance, since you're using 14 (10 + 4):

var txt = g.append('text')
    //etc...
    .attr('x', 14)
    .call(wrap, someWidth - 28);

Here is your code with those changes:

function wrap(text, width) {
  text.each(function() {
    var text = d3.select(this),
      words = text.text().split(/\s+/).reverse(),
      word,
      line = [],
      lineNumber = 0,
      lineHeight = 1.1, // ems
      y = text.attr('y'),
      x = text.attr('x')
    dy = parseFloat(text.attr('dy')),
      tspan = text
      .text(null)
      .append('tspan')
      .attr('x', x)
      .attr('y', y)
      .attr('dy', dy + 'em');
    while ((word = words.pop())) {
      line.push(word);
      tspan.text(line.join(' '));
      if (tspan.node().getComputedTextLength() > width) {
        line.pop();
        tspan.text(line.join(' '));
        line = [word];
        tspan = text
          .append('tspan')
          .attr('x', x)
          .attr('y', y)
          .attr('dy', ++lineNumber * lineHeight + dy + 'em')
          .text(word);
      }
    }
  });
}

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

var longText =
  'Now is the time for all good men to come to the aid of their countryNow is the time for all good men to come to the aid of their countryNow is the time for all good men to come to the aid of their countryNow is the time for all good men to come to the aid of their country';

var totHeight = 0;

drawRect();

function drawRect() {
  //var someWidth = randomIntFromInterval(40, 300);
  var someWidth = 212;

  var g = svg
    .append('g')
    .attr('transform', 'translate(20,' + totHeight + ')');

  var rect = g
    .append('rect')
    .style('fill', 'steelblue')
    .attr('width', someWidth)
    .attr('height', 1);

  var txt = g
    .append('text')
    .text(longText)
    .attr('x', 14)
    .attr('y', 10)
    .attr('dy', '.71em')
    .style('fill', 'white')
    .call(wrap, someWidth - 28);

  var height = txt.node().getBBox().height + 25;
  totHeight += height + 10;
  rect.attr('height', height);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<div id="body"></div>

Upvotes: 1

Related Questions