Home > Software engineering >  text align center for tspan text block
text align center for tspan text block

Time:07-14

Below sample generate text block but current test is align left, how to align text by center?

demo()
function demo() {
        var svg = d3.select('body').append('svg')
                .attr('width','100')
                .attr('height','100')
                .attr('transform','translate(50,50)')
        
        var text = ["12","123456789","1234"]

        var fontsize = 40
        var txt = svg.append('text')
                .attr("text-anchor", 'start')
                .attr("dominant-baseline","middle")//text-before-edge
                .attr("font-size",fontsize)
                .attr('alignment-baseline','middle')

        var tspan = txt.selectAll('tspan')
                .data(text)
                .join('tspan')
                .attr('class','tspan')
                .attr("x", 0)
                .attr('y',function(d,i) {
                        return i*fontsize
                })
                .text(d => d)
}
<script src="https://d3js.org/d3.v7.min.js"></script>   

Update

After apply @herrstrietzel's answer, I create below demo to show the usecase to put text box at specific position, currently it always at the center of svg, I wish it deployed at specific location (x,y).

demo()

function demo() {
  var svg = d3.select('body').append('svg')
    .attr('width', 300)
            .attr('height', 200)
            .style('background','#ececec')

        add_grid(svg)

        var data = [
                {
                        text:["B", "BB", "BBB",'BBBB'],
                        x:50,
                        y:50
                },
                {

                        text:["AAAAA", "AAA", "A"],
                        x:150,
                        y:100
                }
                ]
        
        var fontsize = 20
        var txt = svg.selectAll('.box')
                .data(data)
                .join('text')
                .attr("text-anchor", 'middle')
                .attr("x", '50%')
                .attr("y", '0%')
                .attr("dominant-baseline", "central")
                .attr("font-size", fontsize)
                .style('border','1px solid red')
                .attr('x',d => d.x)
                .attr('y',d => d.y)

  var tspan = txt.selectAll('tspan')
    .data(d => d.text)
    .join('tspan')
    .attr('class', 'tspan')
    .attr("x", '50%')
    .attr('dy', function(d) {
      return fontsize * 1.2
    })
            .text(d => d)
            
        function add_grid(svg) {
                var w =  svg.attr('width')
                var step = 10
                var mygrid = function(d) {
                        return `M 0,${d} l ${w},0 M ${d},0 l 0,${w}`
                }

                var grid = []
                for(var i = 0; i < w; i =step) {
                        grid.push(i);
                }

                svg.append('g')
                        .selectAll(null)
                        .data(grid).enter()
                        .append('path')
                        .attr('d',d => mygrid(d))
                        .attr('fill','none')
                        .attr('stroke','green')
                        .attr('stroke-width',.5)
        }       
}
<script src="https://d3js.org/d3.v7.min.js"></script>   

CodePudding user response:

Your text is aligned left since your text-anchor value is "start" instead of "middle".

Working example

demo()

function demo() {
  var svg = d3.select('body').append('svg')
    .attr('width', '100')
    .attr('height', '100')
  //.attr('viewBox','0 0 100 100')
  var text = ["12", "123456789", "1234"]

  var fontsize = 20
  var txt = svg.append('text')
    .attr("text-anchor", 'middle')
    .attr("x", '50%')
    .attr("y", '0%')
    .attr("dominant-baseline", "central")
    .attr("font-size", fontsize)

  var tspan = txt.selectAll('tspan')
    .data(text)
    .join('tspan')
    .attr('class', 'tspan')
    .attr("x", '50%')
    .attr('dy', function(d) {
      return fontsize * 1.2
    })
    .text(d => d)
}
svg {
  border: 1px solid red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.6.1/d3.min.js"></script>

dy can actually be better suited to mimic line breaks.

Worth mentioning:

  • there are no block-like elements in svg:
  • the parent svg won't automatically grow or shrink according to it's content/child node dimensions ( so you need to calculate an appropriate height and with depending on your text length)
  • <text> elements need a x value of "50%" or "viewBox width/2" to be centered

I strongly recommend to test the best <text> properties "statically" (e.g. in a codepen or a static html)

Second example:

You need to set the same x value for both <text> and <tspan> elements to mimic a center aligned text block.
Red circles illustrate the desired horizontal/vertical center points.

demo();

function demo() {
  var svg = d3
    .select("body")
    .append("svg")
    .attr("width", 300)
    .attr("height", 200)
    .style("background", "#ececec");

  add_grid(svg);

  var data = [
    {
      text: ["B", "BB", "BBB", "BBBB"],
      x: 50,
      y: 50
    },
    {
      text: ["AAAAA", "AAA", "A"],
      x: 150,
      y: 100
    }
  ];

  var fontsize = 20;
  var txt = svg
    .selectAll(".box")
    .data(data)
    .join("text")
    .attr("text-anchor", "middle")
    .attr("dominant-baseline", "text-bottom")
    .attr("font-size", fontsize)
    .attr("x", (d) => d.x)
    .attr("y", (d) =>{
      let textL = d.text.length;
      let yOffset =  d.y - (fontsize * 1.2 * textL/2);
      return (yOffset);
    });

  var tspan = txt
    .selectAll("tspan")
    .data((d) => d.text)
    .join("tspan")
    .attr("class", "tspan")
    .attr("x", function (d) {
      let currentX = d3.select(this.parentNode).attr("x");
      return currentX;
    })
    .attr("dy", function (d) {
      return fontsize * 1.2;
    })
    .text((d) => d);
  

  function add_grid(svg) {
    var w =  svg.attr("width");
    var step = 10;
    var mygrid = function (d) {
      return `M 0,${d} l ${w},0 M ${d},0 l 0,${w}`;
    };

    var grid = [];
    for (var i = 0; i < w; i  = step) {
      grid.push(i);
    }

    svg
      .append("g")
      .selectAll(null)
      .data(grid)
      .enter()
      .append("path")
      .attr("d", (d) => mygrid(d))
      .attr("fill", "none")
      .attr("stroke", "green")
      .attr("stroke-width", 0.5);
  }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.6.1/d3.min.js"></script>

<svg width="300" height="200" style="background: rgb(236, 236, 236);">
  <g>
    <path fill="none" stroke="green" stroke-width="0.5" d="M0 0l300 0m-300 0l0 300m0-290l300 0m-290-10l0 300m-10-280l300 0m-280-20l0 300m-20-270l300 0m-270-30l0 300m-30-260l300 0m-260-40l0 300m-40-250l300 0m-250-50l0 300m-50-240l300 0m-240-60l0 300m-60-230l300 0m-230-70l0 300m-70-220l300 0m-220-80l0 300m-80-210l300 0m-210-90l0 300m-90-200l300 0m-200-100l0 300m-100-190l300 0m-190-110l0 300m-110-180l300 0m-180-120l0 300m-120-170l300 0m-170-130l0 300m-130-160l300 0m-160-140l0 300m-140-150l300 0m-150-150l0 300m-150-140l300 0m-140-160l0 300m-160-130l300 0m-130-170l0 300m-170-120l300 0m-120-180l0 300m-180-110l300 0m-110-190l0 300m-190-100l300 0m-100-200l0 300m-200-90l300 0m-90-210l0 300m-210-80l300 0m-80-220l0 300m-220-70l300 0m-70-230l0 300m-230-60l300 0m-60-240l0 300m-240-50l300 0m-50-250l0 300m-250-40l300 0m-40-260l0 300m-260-30l300 0m-30-270l0 300m-270-20l300 0m-20-280l0 300m-280-10l300 0m-10-290l0 300" />
  </g>
   <circle cx="50" cy="50" r="2%" fill="red"/>
  <text text-anchor="middle" dominant-baseline="text-bottom" font-size="20" x="50" y="2" style="border: 1px solid red;">
    <tspan  x="50" dy="24">B</tspan>
    <tspan  x="50" dy="24">BB</tspan>
    <tspan  x="50" dy="24">BBB</tspan>
    <tspan  x="50" dy="24">BBBB</tspan>
  </text>
   <circle cx="150" cy="100" r="2%" fill="red"/>
  <text text-anchor="middle" dominant-baseline="text-bottom" font-size="20" x="150" y="64" style="border: 1px solid red;">
    <tspan  x="150" dy="24">AAAAA</tspan>
    <tspan  x="150" dy="24">AAA</tspan>
    <tspan  x="150" dy="24">A</tspan>
  </text>
</svg>

  • Related