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

如何使用D3.js在IE11中对齐tspan的X轴位置?

D3.js力导向图IE11标签渲染问题

初始问题

使用D3.js构建力导向图时,遇到标签渲染兼容性问题:除IE11外,其他浏览器标签显示正常,但IE11中第一个<tspan>无法居中对齐。尝试过添加/移除x属性、调整dx值等方法,但因每组标签长度不同,dx的计算逻辑无法通用。

当前SVG结构

<text class="edgelabel" id="edgelabel0" font-size="10" style="text-anchor: middle;">
   <textPath xlink:href="#edgepath0" dominant-baseline="middle" startOffset="50%" style="cursor: pointer;">
      <tspan class="edgelabels" dx="0">label one</tspan>
      <tspan class="edgelabels" x="0" dy="22" dx="0">label two</tspan>
      <tspan class="edgelabels" x="0" dy="22" dx="0">label three</tspan>
   </textPath>
</text>

对应渲染代码

const edgelabels = zoomContainer
  .selectAll('.edgelabel')
  .data(links)
  .enter()
  .append('text')
  .attr('class', 'edgelabel')
  .style('text-anchor', 'middle')
  .attr('id', function (d, i) {
    return 'edgelabel' + i;
  })
  .attr('font-size', 10);

edgelabels
  .append('textPath')
  .attr('xlink:href', function (d, i) {
    return '#edgepath' + i;
  })
  .style('cursor', 'pointer')
  .attr('dominant-baseline', 'middle')
  .attr('startOffset', '50%')
  .selectAll('div.textPath')
  .data(function (d, i) {
    return d.label;
  })
  .enter()
  .append('tspan')
  .attr('class', 'edgelabels')
  .text(function (d, i) {
    return console.log(d), d;
  })
  .attr('x', function (d, i) {
    if (i > 0) {
      return 0;
    }
  })
  .attr('dy', function (d, i) {
    if (i > 0) {
      return 22;
    }
  })
  .attr('dx', 0);

想了解:

  • 是否有其他人遇到过类似问题?
  • 代码中是否存在错误?
  • 还有哪些方法可以实现每组标签的dx属性统一?
  • 是否需要重构代码?

更新后的问题

发现IE11不支持<textPath>内嵌套<tspan>,于是将<tspan>直接放在<text>内,通过动态计算x/y坐标让文本定位到路径上,重构了ticked()方法以获取最新坐标,但出现新问题:所有文本元素使用相同的坐标,p1和p2始终获取最后一条边的坐标,导致所有文本都在同一条路径上。

相关代码

// 获取edgepath坐标用于<text>定位
edgepaths.each(function (d) {
  source_x = d.source.x;
  source_y = d.source.y;
  target_x = d.target.x;
  target_y = d.target.y;
});

let p1 = { x: source_x, y: source_y };
let p2 = { x: target_x, y: target_y };

// 将<text>居中到路径上
let textAnchor = this.interpolatedPoint(p1, p2, 0.5);

// 给<text>元素添加x/y属性
edgelabels.attr('x', textAnchor.x).attr('y', textAnchor.y)

问题解答

关于初始IE11 <tspan> 居中问题

这是IE11对SVG <textPath> 内 <tspan> 的兼容性bug——IE11不会继承<textPath>的startOffset和text-anchor设置给子<tspan>,导致第一个<tspan>无法居中。你的代码逻辑本身没问题,核心是浏览器兼容性限制。

关于更新后坐标复用的问题

问题出在edgepaths.each()的循环逻辑:你在循环里直接覆盖全局的source_x、source_y等变量,循环结束后这些变量只会保留最后一条边的坐标,所以所有文本都用了同一个位置。

修复方法很简单,不要用全局变量存坐标,而是直接在edgelabels.attr()的回调里针对每条边单独计算坐标:

// 直接在edgelabels的attr回调中处理每条边的坐标
edgelabels.attr('x', function(d) {
  const midX = (d.source.x + d.target.x) / 2;
  return midX;
}).attr('y', function(d) {
  const midY = (d.source.y + d.target.y) / 2;
  return midY;
});

如果你的interpolatedPoint是自定义方法(比如处理曲线中点),也可以在回调里调用:

edgelabels.attr('x', function(d) {
  const p1 = {x: d.source.x, y: d.source.y};
  const p2 = {x: d.target.x, y: d.target.y};
  return interpolatedPoint(p1, p2, 0.5).x;
}).attr('y', function(d) {
  const p1 = {x: d.source.x, y: d.source.y};
  const p2 = {x: d.target.x, y: d.target.y};
  return interpolatedPoint(p1, p2, 0.5).y;
});

这样每条边的文本都会基于自身的source和target坐标计算中点,不会出现复用最后一条边坐标的问题。

其他优化建议

  1. 不需要给非第一个<tspan>设置x="0",因为<tspan>默认会继承父元素的位置,只需要设置dy来换行即可。
  2. 若要兼容IE11的多行标签,除了直接用<text>+坐标定位,还可以考虑给每个标签组创建独立的<text>元素,而不是用<tspan>,这样更易控制位置。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 14:01:17