在圆形textpath上包装文本

时间:2016-05-15 09:19:39

标签: javascript d3.js svg

我正在使用D3.js构建chart,其中显示了有关员工能力的一些信息。

截图: enter image description here

如您所见,某些文本大于容器元素大小,因此,部分文本被剪切。我想将这些文本包装在容器中。

我找到this example ,但我无法将某些解决方案应用到我的图表中。

帮助将不胜感激......

这是charts codepen url 这是full screen view

P.S。我需要用文字包装文字

1 个答案:

答案 0 :(得分:2)

为了包装标签,您需要调整Mike的解决方案来处理textPath元素。

为此,我们需要几件事:

1。获取标签应包裹的可用宽度

您可以计算弧本身的长度,但我已经通过计算标签所遵循的不可见路径的端点创建的线段来完成此操作。这将为我们提供一点侧边距,因为段的长度短于弧的长度。

两点之间的距离计算如下:

d = sqrt((x2 - x1)^2 + (y2 - y1)^2)

2。当标签具有丰富的可用宽度时将其包裹起来并保持对齐到中心

为了管理这个,我不得不深入研究textPath element上的SVG文档,看看它是如何被包裹并沿y轴移动的。

最初,我尝试在一个textPath标签中设置多个text元素,但我无法设法沿y轴移动它们。事实证明,为此,您需要在tspan元素中添加textPath个元素。但是这里出现了另一个问题 - 我无法将它们集中在一起。

最后,要实现沿y轴和中心对齐的移位,您需要使用一个textPath元素(用于水平对齐),其中一个tspan元素(用于垂直对齐)。

第3。用字母包裹标签,而不是用文字

这就是我认为你需要信件包装的一点(在写作的那一刻,我没有得到OP的答案),因为在图表的小尺寸上,那里是太长的单词,不适合一行。

这是最容易解决的问题。只需调整拆分和连接操作即可从单词切换到字母:

letters = text.text().split('').reverse();  // instead of .split(/\s+/)
...
tspan.text(line.join(""));  // instead of .join(" ")

以下是已更改的整个代码,并附有相关评论:

outerSvg.selectAll(".outerCircleText")
  .data(pie(behaviorsDatasetOuterCircle))
  .enter().append("text")
  .attr("class", "outerCircleText")
  //Move the labels below the arcs for those slices with an end angle greater than 90 degrees
  .attr("dy", function (d, i) {
      d.i = i;
      return (d.startAngle >= 90 * Math.PI / 180 ? 18 : -11);
  })
  .text(function(d) { return d.data.name; })
  .call(wrap);  // Do not add `textPath` elements here. Instead, add them in the `wrap` function

function wrap(text) {
  text.each(function() {
    var text = d3.select(this),
        letters = text.text().split('').reverse(),
        letter,
        line = [],
        lineNumber = 0,
        lineHeight = 1.1, // ems
        y = text.attr("y"),
        dy = parseFloat(text.attr("dy")),
        textPath = text.text(null).append("textPath") // Add a textPath element
            .attr("startOffset", '50%')
            .style("text-anchor", "middle")
            .attr("xlink:href", function(d) { return "#outerArc" + d.i; }),
        tspan = textPath.append('tspan'), // Inslide textPath, add a tspan element, for offset feature later.
        path = d3.select(text.select('textPath').attr('xlink:href')); // Get the path to compute width of text later.

    var startLoc = /M(.*?)A/;
    var newStart = path.attr('d').match(startLoc)[1];
    var newEnd = path.attr('d').indexOf(' 0 0 0 ') > -1 
        ? path.attr('d').split(' 0 0 0 ')[1] 
        : path.attr('d').split(' 0 0 1 ')[1] ;

    // Compute the start/end coordinate points of the arc that the text will follow.
    var x1 = parseFloat(newStart.split(' ')[0]),
        y1 = parseFloat(newStart.split(' ')[1]),
        x2 = parseFloat(newEnd.split(' ')[0]),
        y2 = parseFloat(newEnd.split(' ')[1]);

    // Compute the length of the segment between the arc start/end points. This will be the
    // width which the labels should wrap when reaching it.
    var width = Math.sqrt(Math.pow((x2 - x1), 2) + Math.pow((y2 - y1), 2));

    // And then we go on (with slight changes) with the example from Mike Bostock 
    // from here https://bl.ocks.org/mbostock/7555321
    while (letter = letters.pop()) {
      line.push(letter);
      tspan.text(line.join(""));
      if (tspan.node().getComputedTextLength() > width) {
        line.pop();
        tspan.text(line.join(""));
        line = [letter];

        // Instead of adding only a tspan element, add a new textPath so that the wrapped 
        // letters will be aligned to center. Without it, the letters will start drawing 
        // from right with part of them invisible, like if the labels are not wrapped. 
        textPath = text.append("textPath")
            .attr("startOffset", '50%')
            .style("text-anchor", "middle")
            .attr("xlink:href", function(d) { return "#outerArc" + d.i; }),

        // Add a tspan element to offset the wrapped letters from the previous line
        tspan = textPath.append("tspan")
            .attr('dy', '1em')
            .attr('text-anchor', 'middle')
            .text(letter);
      }
    }
  });
}

最后,这是一个有趣的挑战。 Here是您的codepen的一个分支,带有一个工作示例(更改从第749行开始)。

codepen只包含外部标签。我已经留下了内部标签,以实现此处描述的方法。祝你好运!