66

我正在使用 D3 生成条形图(我改编了此示例中的代码)。我在x轴上使用的标签每个都有几个词长,因为这会使所有标签重叠,我需要跨行打破这些标签。(如果我可以用换行符替换每个标签中的所有空格就可以了。)

我最初通过用文字换行符 ( ) 替换空格&#xA;并设置xml:space="preserve"标签的<text>元素来尝试此操作。不幸的是,事实证明 SVG 不尊重这个属性。接下来,我尝试将每个单词包装成<tspan>我以后可以使用的样式。我通过这个函数传递了每个标签:

function (text) {
    return '<tspan>' + text.replace(/ /g, '</tspan><tspan>') + '</tspan>';
}

但这只是将文字<tspan>s 放入输出中。如何将我的文本标签包装在tspans 中(或做其他事情),以使我的标签不重叠?

4

6 回答 6

89

I ended up using the following code to break each x-axis label across lines:

var insertLinebreaks = function (d) {
    var el = d3.select(this);
    var words = d.split(' ');
    el.text('');

    for (var i = 0; i < words.length; i++) {
        var tspan = el.append('tspan').text(words[i]);
        if (i > 0)
            tspan.attr('x', 0).attr('dy', '15');
    }
};

svg.selectAll('g.x.axis g text').each(insertLinebreaks);

Note that this assumes that the labels have already been created. (If you follow the canonical histogram example then the labels will have been set up in just the way you need.) There also isn't any real line-breaking logic present; the function converts every space into a newline. This fits my purposes fine but you may need to edit the split() line to be smarter about how it partitions the parts of the string into lines.

于 2012-11-07T18:32:33.987 回答
10

SVG 文本元素不支持文本换行,因此有两种选择:

  • 将文本拆分为多个 SVG 文本元素
  • 在 SVG 之上使用覆盖 HTML div

在此处查看 Mike Bostock 对此的评论。

于 2012-11-06T16:04:29.640 回答
6

我发现有用的是使用“foreignObject”标签而不是文本或 tspan 元素。这允许 HTML 的简单嵌入,允许单词自然中断。需要注意的是满足特定需求的对象的整体尺寸:

var myLabel = svg.append('foreignObject')
    .attr({
        height: 50,
        width: 100, // dimensions determined based on need
        transform: 'translate(0,0)' // put it where you want it...
     })
     .html('<div class"style-me"><p>My label or other text</p></div>');

您在此对象中放置的任何元素稍后都可以使用 d3.select/selectAll 获取,以动态更新文本值。

于 2015-04-13T17:05:26.730 回答
5

环顾四周后,我发现 Mike Bostock 提供了一种解决方案,使您能够将文本环绕。

http://bl.ocks.org/mbostock/7555321

在我的代码上实现它(我正在使用折叠树图)。我只是复制了“包装”方法。

然后附加以下内容

    // Standard code for a node    
    nodeEnter.append("text")
        .attr("x", function(d) { return d.children || d._children ? -10 : 10; })
        .attr("dy", ".35em")
        .text(function(d) { return d.text; })
        // New added line to call the function to wrap after a given width
        .call(wrap, 40);

我看不出有任何理由这不适用于强制导向、条形或任何其他模式

修正案 :

对于阅读此内容并使用可折叠图形的任何人,我已将 wrap 函数修改为以下内容。“x”属性中的更改正确设置了对齐方式,递增行号是在单独的行上执行的,因为原始代码中指出了问题,并且“y”已硬设置为零,否则会出现行间距增加的问题每一行。

function wrap(text, width) {
    text.each(function() {
        var text = d3.select(this),
        words = text.text().split(/\s+/).reverse(),
        word,
        line = [],
        lineNumber = 0,
        y = text.attr("y"),
        dy = parseFloat(text.attr("dy")),
        lineHeight = 1.1, // ems
        tspan = text.text(null).append("tspan").attr("x", function(d) { return d.children || d._children ? -10 : 10; }).attr("y", y).attr("dy", dy + "em");     
        while (word = words.pop()) {
            line.push(word);
            tspan.text(line.join(" "));
            var textWidth = tspan.node().getComputedTextLength();
            if (tspan.node().getComputedTextLength() > width) {
                line.pop();
                tspan.text(line.join(" "));
                line = [word];
                ++lineNumber;
                tspan = text.append("tspan").attr("x", function(d) { return d.children || d._children ? -10 : 10; }).attr("y", 0).attr("dy", lineNumber * lineHeight + dy + "em").text(word);
            }
        }
    });
}
于 2016-02-15T09:10:05.530 回答
0

包装长标签也有这个答案。

<!DOCTYPE html>
<meta charset="utf-8">
<style>

.bar {
  fill: steelblue;
}

.bar:hover {
  fill: brown;
}

.title {
  font: bold 14px "Helvetica Neue", Helvetica, Arial, sans-serif;
}

.axis {
  font: 10px sans-serif;
}

.axis path,
.axis line {
  fill: none;
  stroke: #000;
  shape-rendering: crispEdges;
}

.x.axis path {
  display: none;
}

</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>

var margin = {top: 80, right: 180, bottom: 80, left: 180},
    width = 960 - margin.left - margin.right,
    height = 500 - margin.top - margin.bottom;

var x = d3.scale.ordinal()
    .rangeRoundBands([0, width], .1, .3);

var y = d3.scale.linear()
    .range([height, 0]);

var xAxis = d3.svg.axis()
    .scale(x)
    .orient("bottom");

var yAxis = d3.svg.axis()
    .scale(y)
    .orient("left")
    .ticks(8, "%");

var svg = d3.select("body").append("svg")
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)
  .append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

d3.tsv("data.tsv", type, function(error, data) {
  x.domain(data.map(function(d) { return d.name; }));
  y.domain([0, d3.max(data, function(d) { return d.value; })]);

  svg.append("text")
      .attr("class", "title")
      .attr("x", x(data[0].name))
      .attr("y", -26)
      .text("Why Are We Leaving Facebook?");

  svg.append("g")
      .attr("class", "x axis")
      .attr("transform", "translate(0," + height + ")")
      .call(xAxis)
    .selectAll(".tick text")
      .call(wrap, x.rangeBand());

  svg.append("g")
      .attr("class", "y axis")
      .call(yAxis);

  svg.selectAll(".bar")
      .data(data)
    .enter().append("rect")
      .attr("class", "bar")
      .attr("x", function(d) { return x(d.name); })
      .attr("width", x.rangeBand())
      .attr("y", function(d) { return y(d.value); })
      .attr("height", function(d) { return height - y(d.value); });
});

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 = parseFloat(text.attr("dy")),
        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);
      }
    }
  });
}

function type(d) {
  d.value = +d.value;
  return d;
}

</script>

和数据文件“data.tsv”:

name    value
Family in feud with Zuckerbergs .17
Committed 671 birthdays to memory   .19
Ex is doing too well    .10
High school friends all dead now    .15
Discovered how to “like” things mentally    .27
Not enough politics .12
于 2014-11-14T03:38:34.397 回答
-1

利用<tspan>

在 nv.d3 中

nv.models.axis = function() {

...

      .select('text')
            .attr('dy', '0em')
            .attr('y', -axis.tickPadding())
            .attr('text-anchor', 'middle')
            .text(function(d,i) {
              var v = fmt(d);
              return ('' + v).match('NaN') ? '' : v;
            });

将所有出现的 .text( 更改为 .html(

于 2014-03-28T19:12:13.277 回答