D3垂直条形图为标签文本添加换行符

时间:2016-03-17 16:19:41

标签: javascript d3.js charts

我试图打破长轴的y轴标签。我的条形图如下所示:http://bl.ocks.org/erikvullings/51cc5332439939f1f292但标签较长(通常超过10个单词,带空格)。这是我的代码(它可以工作,我看到图表,唯一的错误'是文本,我加载JSON dinamically,这个数据仅用于SO):

HTML:

<svg id='table1'></svg>

<script>
    var window_width = $(window).width();
    var main_width = (window_width / 100 ) * 80;
    var w = main_width - 400;

    var data = {
      labels: [
        'the text to be splitted 1', 'the text to be splitted 2'
      ],
      series: [
        {
          label: '2012',
          values: [4, 8]
        },
        {
          label: '2013',
          values: [12, 43]
        }
      ]
    };

    var chartWidth   = w,
    barHeight        = 25,
    groupHeight      = barHeight * data.series.length,
    gapBetweenGroups = 10,
    spaceForLabels   = 175,
    spaceForLegend   = 0;

    // Zip the series data together (first values, second values, etc.)
    var zippedData = [];
    for (var i=0; i<data.labels.length; i++) {
      for (var j=0; j<data.series.length; j++) {
        zippedData.push(data.series[j].values[i]);
      }
    }

    var chartHeight = barHeight * zippedData.length + gapBetweenGroups * data.labels.length;
    var x = d3.scale.linear()
        .domain([0, d3.max(zippedData)])
        .range([0, chartWidth]);

    var y = d3.scale.linear()
        .range([chartHeight + gapBetweenGroups, 0]);

    var yAxis = d3.svg.axis()
        .scale(y)
        .tickFormat('')
        .tickSize(0)
        .orient("left");

    // Specify the chart area and dimensions
    var chart = d3.select("#table"+ambit)
        .attr("width", spaceForLabels + chartWidth + spaceForLegend)
        .attr("height", chartHeight);

    // Create bars
    var bar = chart.selectAll("g")
        .data(zippedData)
        .enter().append("g")
        .attr("transform", function(d, i) {
          return "translate(" + spaceForLabels + "," + (i * barHeight + gapBetweenGroups * (0.5 + Math.floor(i/data.series.length))) + ")";
        });

    // Create rectangles of the correct width
    bar.append("rect")
        .attr("fill", function(d,i) { return color(i % data.series.length); })
        .attr("class", "bar")
        .attr("width", x)
        .attr("height", barHeight - 1);

    // Add text label in bar
    bar.append("text")
        .attr("class", "label_txt1")    
        .attr("x", function(d) { return x(d) - 3; })
        .attr("y", barHeight / 2)
        .attr("fill", "red")
        .attr("dy", ".35em")
        .text(function(d) { return d; });

    // Draw labels
    bar.append("text")
        .attr("class", "label_txt")
        .attr("x", function(d) { return - 10; })
        .attr("y", groupHeight / 2)
        .attr("dy", ".35em")
        .text(function(d,i) {
          if (i % data.series.length === 0)
        return data.labels[Math.floor(i/data.series.length)];
          else
        return ""});

    chart.append("g")
          .attr("class", "y axis")
          .attr("transform", "translate(" + spaceForLabels + ", " + -gapBetweenGroups/2 + ")")
          .call(yAxis);

</script>

我按照多个例子来解决这个问题,例如: How do I include newlines in labels in D3 charts?Adding a line break to D3 graph y-axis labelshttps://bl.ocks.org/mbostock/7555321

首次尝试:

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

chart.selectAll('g.y.axis text').each(insertLinebreaks); 

和:

bar.selectAll('.label_txt1').each(insertLinebreaks);

我也试过了:

bar.append("text")
    .attr("class", "label_txt1")    
    .attr("x", function(d) { return x(d) - 3; })
    .attr("y", barHeight / 2)
    .attr("fill", "red")
    .attr("dy", ".35em")
    .text(function(d) { return d; });

chart.selectAll(".label_txt1").call(wrap, 40)

function wrap(text, width) {
    alert(JSON.stringify(text));
    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);
            }
        }
    });
}

我在How do I include newlines in labels in D3 charts?

阅读时将text()更改为html()

在浏览器中,我看到使用类<text>创建的label_txt1。当我尝试用selectAll(".label_txt1")选择文本时,我得到的是行的值,而不是文本。

有人能帮助我吗?谢谢!

1 个答案:

答案 0 :(得分:1)

您引用的此example是在使用svg文本节点时执行文本换行的规范方法。你只是称错了(在错误的班级,你正在包装数字)。将其简化为:

// Draw labels
bar.append("text")
  .attr("class", "label_txt")
  .attr("x", function(d) {
    return -10;
  })
  .attr("y", groupHeight / 2) //<-- not sure you need this
  .attr("dy", ".35em")
  .text(function(d, i) {
    if (i % data.series.length === 0)
      return data.labels[Math.floor(i / data.series.length)];
    else
      return ""
  })
  .call(wrap, 40);

完整代码:

<!DOCTYPE html>
<html>

<head>
  <script data-require="d3@3.5.3" data-semver="3.5.3" src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.3/d3.js"></script>
</head>

<body>
  <svg id="table1"></svg>
  <script>
    var window_width = 1000;
    var main_width = 400;
    var w = 400;

    var data = {
      labels: [
        'the text to be splitted 1', 'the text to be splitted 2'
      ],
      series: [{
        label: '2012',
        values: [4, 8]
      }, {
        label: '2013',
        values: [12, 43]
      }]
    };

    var chartWidth = w,
      barHeight = 25,
      groupHeight = barHeight * data.series.length,
      gapBetweenGroups = 10,
      spaceForLabels = 175,
      spaceForLegend = 0;

    var color = d3.scale.category10();

    // Zip the series data together (first values, second values, etc.)
    var zippedData = [];
    for (var i = 0; i < data.labels.length; i++) {
      for (var j = 0; j < data.series.length; j++) {
        zippedData.push(data.series[j].values[i]);
      }
    }

    var chartHeight = barHeight * zippedData.length + gapBetweenGroups * data.labels.length;
    var x = d3.scale.linear()
      .domain([0, d3.max(zippedData)])
      .range([0, chartWidth]);

    var y = d3.scale.linear()
      .range([chartHeight + gapBetweenGroups, 0]);

    var yAxis = d3.svg.axis()
      .scale(y)
      .tickFormat('')
      .tickSize(0)
      .orient("left");

    // Specify the chart area and dimensions
    var chart = d3.select("#table1")
      .attr("width", spaceForLabels + chartWidth + spaceForLegend)
      .attr("height", chartHeight);

    // Create bars
    var bar = chart.selectAll("g")
      .data(zippedData)
      .enter().append("g")
      .attr("transform", function(d, i) {
        return "translate(" + spaceForLabels + "," + (i * barHeight + gapBetweenGroups * (0.5 + Math.floor(i / data.series.length))) + ")";
      });

    // Create rectangles of the correct width
    bar.append("rect")
      .attr("fill", function(d, i) {
        return color(i % data.series.length);
      })
      .attr("class", "bar")
      .attr("width", x)
      .attr("height", barHeight - 1);

    // Add text label in bar
    bar.append("text")
      .attr("class", "label_txt1")
      .attr("x", function(d) {
        return x(d) - 3;
      })
      .attr("y", barHeight / 2)
      .attr("fill", "red")
      .attr("dy", ".35em")
      .text(function(d) {
        return d;
      });

    // Draw labels
    bar.append("text")
      .attr("class", "label_txt")
      .attr("x", function(d) {
        return -10;
      })
      //.attr("y", groupHeight / 2) //<-- you don't need this...
      .attr("dy", ".35em")
      .text(function(d, i) {
        if (i % data.series.length === 0)
          return data.labels[Math.floor(i / data.series.length)];
        else
          return ""
      })
      .call(wrap, 40);

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

    chart.append("g")
      .attr("class", "y axis")
      .attr("transform", "translate(" + spaceForLabels + ", " + -gapBetweenGroups / 2 + ")")
      .call(yAxis);
  </script>
</body>

</html>