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水平中点。
修正方案
- 删除所有
x: '50%'、y: '0%'这类相对SVG的百分比定位属性,父text直接绑定自定义的x、y坐标 - tspan的x值直接取父text的x属性值,保证每行文本都和父text的锚点对齐
- 计算父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
相关产品推荐
相关产品推荐

