javascript 在D3中换行文本

7gcisfzg  于 2023-08-02  发布在  Java
关注(0)|答案(6)|浏览(271)

我想得到的文本 Package 在下面的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; });
}

pgpifvop

pgpifvop1#

您可以修改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

2sbarzqh

2sbarzqh2#

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

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

字符串
我用过了这肯定比自己计算 Package 好。
another d3 plugin可用于文本 Package ,但我从来没有使用过它,所以我不能说它的有用性。

y53ybaqx

y53ybaqx3#

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

import { Text } from '@visx/text';

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

字符串

enyaitl3

enyaitl34#

您还可以通过使用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", `${fontSize}px`)
    .style("overflow-y", "auto")
    .html("The text to display")

字符串
结果:

<foreignObject width="200" height="50">
    <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(...)调用,并通过样式表插入样式。
Source(and more info/options)from this SO answer .

ar7v8xwq

ar7v8xwq5#

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

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

字符串

ikfrs5lh

ikfrs5lh6#

你可以用这个函数来换行

function wrapText(text, width, clientWidth, boundingRect) {
  text.each(function () {
    const textNode = d3.select(this);
    let words = textNode.text().split(/\s+/).reverse();
    words = words.filter((q) => q);
    const lineHeight = 1.1; // Adjust this value to set the line height
    const y = textNode.attr("y");
    const x = textNode.attr("x"); // const dy = parseFloat(textNode.attr("dy"));
    const dy = 0;
    let tspan = textNode
      .text(null)
      .append("tspan")
      .attr("x", x)
      .attr("y", y)
      .attr("dy", dy + "em");
    let word;
    let line = [];
    let lineNumber = 0;
    while ((word = words.pop())) {
      line.push(word);
      tspan.text(line.join(" "));
      // Create a temporary tspan to calculate text length without modifying the content
      const tempTspan = textNode.append("tspan").text(line.join(" "));
      const textLength = tempTspan.node().getComputedTextLength();
      tempTspan.remove();
      if (parseFloat(x) + textLength > clientWidth && textLength < width) {
        tspan.attr("x", boundingRect.width.toString());
      }
      if (parseFloat(x) < boundingRect.left && textLength < width) {
        tspan.attr("x", boundingRect.left.toString());
      }
      if (textLength > width) {
        line.pop();
        tspan.text(line.join(" "));
        line = [word];
        if (parseFloat(x) + textLength > clientWidth) {
          tspan = textNode
            .append("tspan")
            .attr("x", boundingRect.width.toString())
            .attr("y", y)
            .attr("dy", ++lineNumber * lineHeight + dy + "em")
            .text(word);
        } else if (parseFloat(x) < boundingRect.left) {
          tspan = textNode
            .append("tspan")
            .attr("x", boundingRect.left.toString())
            .attr("y", y)
            .attr("dy", ++lineNumber * lineHeight + dy + "em")
            .text(word);
        } else {
          tspan = textNode
            .append("tspan")
            .attr("x", x)
            .attr("y", y)
            .attr("dy", ++lineNumber * lineHeight + dy + "em")
            .text(word);
        }
      }
    }
  });
}

字符串
在函数内部,它使用text.each()方法迭代选择中的每个文本元素,并分别应用 Package 逻辑。
它使用D3的d3.select(this)来选择迭代中的当前文本元素。
它使用.split(/\s+/)方法将文本元素的内容拆分为单词。/\s+/正则表达式用于通过空格(空格和换行符)分割文本。然后调用reverse()方法来颠倒单词的顺序。
它使用.filter((q)=> q)过滤掉任何空单词。此步骤对于文本中存在连续空格或前导/尾随空格的情况非常有用,因为它可以确保删除空单词。
lineHeight变量用于设置换行文本所需的行高。可以调整此值以控制行间距。
分别使用.attr(“y”)和.attr(“x”)检索文本元素的当前y和x属性。这些属性定义文本元素的初始位置。
dy变量设置为0,这意味着文本沿着y轴没有初始偏移。
使用.append(“tspan”)在文本元素中创建一个临时tspan元素。这个tspan元素用于计算文本的长度,而不需要实际修改内容。
使用.text(null)将text元素的文本内容设置为null,从而有效地清除内容。
tspan元素的x、y和dy属性设置为初始位置和偏移值。
此时,换行的设置已经完成,函数已经准备好处理单词并换行文本。
while循环中的其余代码负责实际的文本换行逻辑。它迭代words数组中的每个单词,并将其追加到当前tspan元素,同时测量其长度。
如果当前tspan的长度超过了指定的宽度,则通过创建一个新的tspan元素来开始一个新行。如果文本长度大于容器右侧或左侧的可用空间,则调整tspan元素的x属性以使文本适合可用空间。
在函数结束时,文本内容已经被换行,并且可能已经创建了多个tspan元素来容纳换行的行。

相关问题