带有文本锚点的 D3 文本换行:中间

D3 text wrap with text-anchor: middle

在 D3 中,我想换行具有中间文本锚点的文本。我查看了 this example from Mike Bostok,但我无法理解这些设置。

我希望看到文本位于红框的中心(水平和垂直)。

var plot = d3.select(container)
  .insert("svg")
  .attr('width', 100)
  .attr('height', 200);

plot.append("text")
  .attr("x", 50)
  .attr("y", 100)
  .attr("text-anchor", "middle")
  .attr("alignment-baseline", "alphabetic")
  .text("The brown fox jumps!")
  .call(wrap, 100);


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
      y = text.attr("y"),
      dy = 1,
      tspan = text.text(null).append("tspan").attr("x", 0).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", 0).attr("y", y).attr("dy", ++lineNumber * lineHeight + dy + "em").text(word);
      }
    }
  });
}

根据 Shashank 的指示,我现在可以使用 translate 方法将文本置于中心位置。只是设置文本的 y 似乎并没有改变任何东西。我还添加了一个 yTrack 变量,以便能够在环绕文本下方继续绘制。

let yTrack = 100,
  plot = d3.select(container)
  .insert("svg")
  .attr('width', 100)
  .attr('height', 200);

plot.append("text")
  .attr("x", 50)
  .attr("y", yTrack)
  .attr("text-anchor", "middle")
  .attr("font-family", "sans-serif")
  .text("The quick brown fox jumps over the lazy dog.")
  .call(wrap, 100);

let height = parseInt(plot.select('text').node().getBoundingClientRect().height);

plot.select("text").attr('transform', 'translate(0, ' + (-height / 2) + ')');

//plot.select("text").attr('y', 0);

yTrack += (parseInt(height / 2) + 10);

plot.append('rect')
  .attr("x", 0)
  .attr("y", yTrack)
  .attr("width", 100)
  .attr("height", 10)
  .style('fill', '#999');

function wrap(text, width) {
  text.each(function() {
    let 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 = 1.1,
      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);
      }
    }
  });
}
#container {
  height: 200px;
  width: 100px;
  border: 1px solid red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div id="container"></div>

docs for text-anchormiddle 将产生以下结果:

The rendered characters are aligned such that the middle of the text string is at the current text position.

在您的情况下,当您将文本分解为 tspan 时,x 等于 0 -- 当前文本位置 将是 svg 的开始(即在点 0)。此外,应用于 text 元素的 x:50 根本无关紧要。

  1. 一种方法是将中心值即 50 应用到 tspans:

    tspan.attr("x", 50)
    

    这是一个片段:

    var plot = d3.select(container)
      .insert("svg")
      .attr('width', 100)
      .attr('height', 200);
    
    plot.append("text")
      .attr("text-anchor", "middle")
      .attr("alignment-baseline", "alphabetic")
      .text("The brown fox jumps!")
      .call(wrap, 100);
    
    var height = plot.select('text').node().getBoundingClientRect().height;
    plot.select('text').attr('y', 100-height/2);
    
    function wrap(text, width) {
      text.each(function() {
        var text = d3.select(this),
          words = text.text().split(/\s+/).reverse(),
          word,
          line = [],
          lineNumber = 0,
          lineHeight = 0.1, // ems
          y = text.attr("y"),
          dy = 1,
          tspan = text.text(null).append("tspan").attr("x", 50).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", 50).attr("y", y).attr("dy", ++lineNumber * lineHeight + dy + "em").text(word);
          }
        }
      });
    }
    
    #container {
      height: 200px;
      width: 100px;
      border: 1px solid red;
    }
    
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
    <div id="container"></div>
    

  2. 另一种方法是按以下方式将 'transform' 属性 应用于文本:

    plot.select('text').attr('transform', 'translate(50, ' + (100-height/2)+')');
    

    这是使用此方法的片段:

    var plot = d3.select(container)
      .insert("svg")
      .attr('width', 100)
      .attr('height', 200);
    
    plot.append("text")
      .attr("text-anchor", "middle")
      .attr("alignment-baseline", "alphabetic")
      .text("The brown fox jumps!")
      .call(wrap, 100);
    
    var height = plot.select('text').node().getBoundingClientRect().height;
    plot.select('text').attr('transform', 'translate(50, ' + (100-height/2)+')');
    
    function wrap(text, width) {
      text.each(function() {
        var text = d3.select(this),
          words = text.text().split(/\s+/).reverse(),
          word,
          line = [],
          lineNumber = 0,
          lineHeight = 0.1, // ems
          y = text.attr("y"),
          dy = 1,
          tspan = text.text(null).append("tspan").attr("x", 0).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", 0).attr("y", y).attr("dy", ++lineNumber * lineHeight + dy + "em").text(word);
          }
        }
      });
    }
    
    #container {
      height: 200px;
      width: 100px;
      border: 1px solid red;
    }
    
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
    <div id="container"></div>
    

在上述两种方法中,我都没有使用 y 中心值,即 100,而是根据文本高度计算中点使用 getBoundingClientRect() 函数。

var height = plot.select('text').node().getBoundingClientRect().height;
plot.select('text').attr('y', 100-height/2);

希望这对您有所帮助。 :)