在 D3 中环绕文本

Posted

技术标签:

【中文标题】在 D3 中环绕文本【英文标题】:Wrapping Text in D3 【发布时间】:2014-09-07 04:32:28 【问题描述】:

我想让文本在下面的 D3 树上换行,而不是

Foo is not a long word

每一行都换成

Foo is
not a
long word

我尝试将文本设置为“foreignObject”而不是文本对象,文本确实换行了,但它不会在树动画上移动,并且全部分组在左上角。

代码位于

http://jsfiddle.net/mikeyai/X43X5/1/

javascript

var width = 960,
    height = 500;

var tree = d3.layout.tree()
    .size([width - 20, height - 20]);

var root = ,
    nodes = tree(root);

root.parent = root;
root.px = root.x;
root.py = root.y;

var diagonal = d3.svg.diagonal();

var svg = d3.select("body").append("svg")
    .attr("width", width)
    .attr("height", height)
  .append("g")
    .attr("transform", "translate(10,10)");

var node = svg.selectAll(".node"),
    link = svg.selectAll(".link");

var duration = 750,
    timer = setInterval(update, duration);

function update() 
  if (nodes.length >= 500) return clearInterval(timer);

  // Add a new node to a random parent.
  var n = id: nodes.length,
      p = nodes[Math.random() * nodes.length | 0];
  if (p.children) p.children.push(n); else p.children = [n];
  nodes.push(n);

  // Recompute the layout and data join.
  node = node.data(tree.nodes(root), function(d)  return d.id; );
  link = link.data(tree.links(nodes), function(d)  return d.source.id + "-" + d.target.id; );

  // Add entering nodes in the parent’s old position.
  node.enter().append("text")
      .attr("class", "node")
      .attr("x", function(d)  return d.parent.px; )
      .attr("y", function(d)  return d.parent.py; )
        .text('Foo is not a long word');

  // Add entering links in the parent’s old position.
  link.enter().insert("path", ".node")
      .attr("class", "link")
      .attr("d", function(d) 
        var o = x: d.source.px, y: d.source.py;
        return diagonal(source: o, target: o);
      );

  // Transition nodes and links to their new positions.
  var t = svg.transition()
      .duration(duration);

  t.selectAll(".link")
      .attr("d", diagonal);

  t.selectAll(".node")
      .attr("x", function(d)  return d.px = d.x; )
      .attr("y", function(d)  return d.py = d.y; );

【问题讨论】:

我昨天回答了非常相似的问题***.com/questions/24755321/… 我实际上已经看到了您在答案中发布的两种资源,不幸的是,没有人给我太多的见解 - 我不明白 tspan 元素如何与 D3 结合使用。 mbostock 示例可能有一些用处,但我不知道 x.rangeBand() 是什么以及如何用简单的文本调用实现的“换行”函数。 我以前曾与这样的事情作斗争,发现最简单的方法就是翻转我的树jsfiddle.net/robschmuecker/5nHGz x.rangeBand() 只是一个宽度。因此,您可以将其替换为另一个整数。 我修改了我的示例以显示我的树可以多么容易地更新jsfiddle.net/robschmuecker/5nHGz/1 只是在第 1222 行添加了“wrap”函数和对其的调用,并添加了一个很长的第一个节点名称用于测试。看过你的,但无法立即使用。 【参考方案1】:

您可以修改Mike Bostock's "Wrapping Long Labels" example 以将<tspan> 元素添加到您的<text> 节点。将包装文本添加到节点需要进行两项主要更改。我没有深入研究让文本在过渡期间更新其位置,但添加起来应该不会太难。

首先是添加一个函数wrap,基于上面示例中的函数。 wrap 将负责添加 <tspan> 元素以使您的文本适合特定宽度:

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);
            
        
    );

第二个变化是不再设置每个节点的文本,而是需要为每个节点调用wrap

// Add entering nodes in the parent’s old position.
node.enter().append("text")
    .attr("class", "node")
    .attr("x", function (d)  return d.parent.px; )
    .attr("y", function (d)  return d.parent.py; )
    .text("Foo is not a long word")
    .call(wrap, 30); // wrap the text in <= 30 pixels

【讨论】:

不幸的是,我无法让我的示例使用它来处理转换,但谢谢你,因为这是朝着正确方向迈出的一步 我想通了,我必须在转换之后添加 t.selectAll("tspan")...。这与您的有用帖子一起回答了我的问题! @user235236:太好了,很高兴为您提供帮助! 答案很有帮助。谢谢@mdml 我唯一不知道的是如何将动画应用于包装的文本。在.call(wrap, 30) 之前设置的.transition() 将不起作用。在将文本设置为tspan 之前,在 wrap 函数中插入一个转换似乎也不起作用。 @user235236 你是怎么解决的? 这很有帮助,谢谢 :) 我查看了 blocks.org 示例,但不知道他在哪里附加了文本元素。 d3 blocks.org 示例应该更简单【参考方案2】:

如果您使用的是 React,那么您可以使用的库是 @visx/text。它公开了一个更强大的 SVG 文本元素,支持 width 参数。

import  Text  from '@visx/text';

const App = () => (
  <svg>
    <Text width=20>Foo is not a long word</Text>
  </svg>
);

【讨论】:

【参考方案3】:

如果您愿意添加另一个 JS 库,另一种选择是使用 D3 插件 D3plus。它具有内置的text wrapping 功能。它甚至支持填充和调整文本大小以填充可用空间。

d3plus.textwrap()
  .container(d3.select("#rectWrap"))
  .draw();

我用过。这肯定比自己计算包装要好。

有 another d3 plugin 可用于文本换行,但我从未使用过它,所以我无法说出它的用处。

【讨论】:

【参考方案4】:

这是一种使用 d3 plus 进行文本换行的方法。这对我来说真的很容易,并且现在可以在所有浏览器中使用

d3plus.textwrap()
    .container(d3.select("#intellectual"))
    .shape('square')
    .width(370)
    .height(55)
    .resize(true)
    .draw();      

【讨论】:

【参考方案5】:

您还可以通过 foreignObject 在 SVG 中使用纯 html 元素。

例如,我使用以下内容将 HTML div 附加到我的 svg 对象。

svg.append("foreignObject")
    .attr("width", blockWidth)
    .attr("height", blockHeight)
    .append("xhtml:div")
    .style("color", "#000")
    .style("text-align", "center")
    .style("width", "100%")
    .style("height", "100%")
    .style("padding", "5px")
    .style("font-size", `$fontSizepx`)
    .style("overflow-y", "auto")
    .html("The text to display")

结果:

<foreignObject  >
    <div style="color: rgb(0, 0, 0); text-align: center; width: 100%; height: 100%; padding: 5px; font-size: 12px; overflow-y: auto;">
        The text to display
    </div>
</foreignObject>

如果您的样式是静态的,您也可以使用.attr('class', 'classname') 来代替所有.style(...) 调用并通过样式表插入样式。

来源(以及更多信息/选项)from this SO answer。

【讨论】:

以上是关于在 D3 中环绕文本的主要内容,如果未能解决你的问题,请参考以下文章

d3.geo.path 矩形包裹错误的方式

多行文本文本输入框 textarea 可点击任意地方编辑

使用 D3 和 Leaflet 重复 SVG

基于WPS的Word最佳实践系列(图文环绕方式)

QT中怎样读取中文文本文件!

信创办公--基于WPS的Word最佳实践系列(图文环绕方式)