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

D3应用文本换行wrap函数后内容被重置为初始状态的问题咨询

问题原因

  • 核心原因是text()赋值和wrap()调用被放在了transition()链式调用之后。D3过渡队列的操作是异步执行的,transition.text()会在动画执行阶段(也就是你提到的timerFlush触发时)直接覆盖文本元素的内容,把wrap()函数提前生成的多个tspan节点替换为纯文本字符串,导致换行效果消失。
  • 其次你的enter-update-exit执行顺序错误,先声明了enter和update集合再执行exit移除,会导致update集合包含待删除的旧节点,逻辑不符合D3标准更新流程。
  • 额外的问题是你在enter和update的过渡链中重复执行text()赋值和wrap()调用,出现操作冲突。

解决思路

  • 调整调用顺序:所有文本设置、wrap()调用都放在transition()之前执行,transition()仅用来处理需要动画的属性(opacity、x、y坐标等),不要在过渡链中操作文本内容。
  • 修正D3数据绑定流程,严格按照「选择元素→绑定数据→处理exit→处理update→处理enter→合并update+enter做公共操作」的顺序执行。
  • 仅在文本内容、换行宽度发生变化时调用wrap()即可,不需要每次过渡都重复调用。

修正后代码示例

function wrap(text, width) {
    text.each(function () {
        var text = d3.select(this),
            words = text.text().split(/\s+/).reverse(),
            word,
            line = [],
            lineNumber = 0,
            lineHeight = 1.1, // ems
            x = text.attr("x"),
            y = text.attr("y"),
            dy = 0, //parseFloat(text.attr("dy")),
            tspan = text.text(null)
                        .append("tspan")
                        .attr("x", x)
                        .attr("y", y)
                        .attr("dy", dy + "em");
        while (word = words.pop()) {
            line.push(word);
            tspan.text(line.join(" "));
            if (tspan.node().getComputedTextLength() > width) {
                line.pop();
                tspan.text(line.join(" "));
                line = [word];
                tspan = text.append("tspan")
                            .attr("x", x)
                            .attr("y", y)
                            .attr("dy", ++lineNumber * lineHeight + dy + "em")
                            .text(word);
            }
        }
    });
}

const nodes = [1, 2, 3]
// 标准enter-update-exit流程
const nodeTexts = vis.g.selectAll('text.node').data(nodes)
// 先处理删除
nodeTexts.exit().remove()
// 处理更新:先设置文本、调用换行,再做过渡动画
nodeTexts
  .text('This is a long text')
  .call(wrap, 30)
  .transition(transitionDuration)
  .attr('x', (d, i) => i*30)
  .attr('y', (d, i) => i*30)
// 处理新增节点
const nodeEnter = nodeTexts.enter()
  .append('text')
  .attr('class', 'node')
  .attr('text-anchor', 'middle')
  .attr('dominant-baseline', 'middle')
  .attr('font-size', 12)
  .style('cursor', 'pointer')
  .attr('opacity', '0')
  .attr('x', (d, i) => i*30)
  .attr('y', (d, i) => i*30)
  // 先设置文本调用换行,再开过渡
  .text('This is a long text')
  .call(wrap, 30)
  .transition()
  .attr('opacity', '1')
  .attr('x', (d, i) => i*30)
  .attr('y', (d, i) => i*30)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.06 08:57:02