You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

D3.js实现SVG tspan多行文本居中及指定坐标定位

问题1:tspan多行文本左对齐改居中

初始代码生成的不同长度文本行默认左对齐,核心修改点:

  • 把父<text>元素的text-anchor属性从start改成middle
  • 所有tspan的x属性不要固定为0,要和父text的定位x值保持一致
  • 垂直方向建议用dy控制行间距,替代逐行计算绝对y值的写法,排版更灵活

初始参考代码:

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') // 改为middle即可开启居中锚点
    .attr("dominant-baseline","middle")
    .attr("font-size",fontsize)
    .attr('alignment-baseline','middle')

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

更新

问题2:居中后的文本块无法定位到指定(x,y)坐标

应用居中方案后,尝试给多组文本设置自定义坐标,但文本始终固定在SVG中心,无法移动到目标位置。
问题代码:

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%') // 问题点1:百分比相对SVG计算,不是自定义坐标
    .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%') // 问题点2:tspan的x用百分比,直接跑到SVG中点
    .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>

问题原因

代码里给text和tspan设置的x: '50%'是相对整个SVG视口的宽度计算坐标,不是相对父元素的位置,哪怕后续给父text绑定了自定义x值,tspan上的百分比定位依然会把文本拉到SVG水平中点。

修正方案

  1. 删除所有x: '50%'、y: '0%'这类相对SVG的百分比定位属性,父text直接绑定自定义的x、y坐标
  2. tspan的x值直接取父text的x属性值,保证每行文本都和父text的锚点对齐
  3. 计算父text的y坐标时,上移半个文本块的总高度,让整个多行文本块垂直居中到指定的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 lineHeight = fontsize * 1.2
  var txt = svg.selectAll('.box')
    .data(data)
    .join('text')
    .attr("text-anchor", 'middle')
    .attr('x',d => d.x)
    // 垂直方向偏移半个文本块高度,实现整体居中到指定y坐标
    .attr('y',d => d.y - (d.text.length - 1)*lineHeight/2)
    .attr("dominant-baseline", "central")
    .attr("font-size", fontsize)

  var tspan = txt.selectAll('tspan')
    .data(d => d.text)
    .join('tspan')
    .attr('class', 'tspan')
    // tspan x和父级text保持一致
    .attr("x", function() {
      return this.parentNode.getAttribute('x')
    })
    .attr('dy', lineHeight)
    .text(d => d)
            
  function add_grid(svg) {
    var w = +svg.attr('width')
    var h = +svg.attr('height')
    var step = 10
    var mygrid = function(d) {
      return `M 0,${d} l ${w},0 M ${d},0 l 0,${h}`
    }

    var grid = []
    for(var i = 0; i < Math.max(w,h); 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>

运行后两组文本会分别定位到设置的(50,50)、(150,100)坐标,每组内长度不一的文本行自动水平居中,整个文本块垂直居中对齐到目标点。


内容的提问来源于stack exchange,提问作者lucky1928

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 05:48:42