使用Google Fonts时SVG文本BBox计算异常导致D3.js生成线条定位不准确的问题
解决D3中基于文本BBox生成线条的定位偏移问题
我明白你遇到的困扰了——用文本的getBBox()结果来定位线条时,线条总是和文本错位,这其实是因为你没搞清楚SVG元素的局部坐标系和父坐标系之间的关系。
问题根源
getBBox()返回的是当前元素内容在自身局部坐标系中的边界框,而不是相对于SVG画布全局或者父组的坐标。对于你设置了x、y、text-anchor: middle和dominant-baseline: middle的文本元素来说:
- 文本的锚点(中心)被定位在父坐标系的
(width/2, height/2)(也就是SVG画布中心) - 文本内容的边界框(
getBBox()的结果)是相对于这个锚点的偏移值——比如box.x会是负数(因为文本从锚点向左延伸),box.y也是负数(文本从锚点向上延伸)
你现在直接用box.x和box.y作为线条的坐标,相当于把线条画在了SVG画布的左上角区域,自然会和中心的文本离得很远。
解决方法
要得到线条的正确位置,需要把文本元素自身的定位坐标和BBox的局部偏移结合起来:
- 获取文本元素的
x和y属性值(这是锚点在父坐标系中的位置) - 用
x + box.x得到线条的起始x坐标(文本的左边缘) - 用
y + box.y得到线条的起始y坐标(文本的顶部) - 用
y + box.y + box.height得到线条的结束y坐标(文本的底部)
修改后的代码
下面是调整后的关键代码部分,我标注了修改的核心点:
//create line const border1 = d3.select('g.svgText') .selectAll('line') .data(data) .join('line') .each( function(d, i) { const current = d3.select(this); const target = current.node().parentNode.childNodes[0]; const box = target.getBBox(); // 获取文本元素自身的x和y坐标(转为数字避免字符串拼接问题) const textX = parseFloat(d3.select(target).attr('x')); const textY = parseFloat(d3.select(target).attr('y')); // 结合文本定位和BBox偏移计算线条的正确坐标 const lineX = textX + box.x; const lineYStart = textY + box.y; const lineYEnd = lineYStart + box.height; current .attr('class', (d, i) => { return `textSvgBorder` + `${i}` }) .attr('x1', lineX) .attr('x2', lineX) .attr('y1', lineYStart) .attr('y2', lineYEnd) .attr('stroke', 'black') .attr('stroke-width', 2) // 可选:加粗线条,更直观看到效果 } )
额外小提示
如果你需要获取元素相对于视口的坐标,可以用getBoundingClientRect(),但这个方法会受SVG的viewBox缩放影响;而getBBox()是基于SVG内部的用户坐标系,更适合SVG内的元素定位场景。
内容的提问来源于stack exchange,提问作者smpa01
相关产品推荐
相关产品推荐

