How to do wordwrap for chart labels using d3.js

后端 未结 5 457
灰色年华
灰色年华 2020-12-15 23:41

I am trying to implement the horizontal bar chart using d3.js.Some of the chart labels are too long. How to do word wrap for the chart labels on y aixs?

Source code:

5条回答
  •  天命终不由人
    2020-12-16 00:30

    Here's a function I wrote not only to solve the y-axis word wrap problem, but also wrap word that is more than 1 line in length, and also align the corresponding 'tick' in the center of the label:

    Result is like this:

    See this snippet:

    var tempArray2 = [{date: "2017/3/11", ratio: 1}, {date: "2017/3/12", ratio: 0.5}, {date: "2017/3/13", ratio: 0.3}, {date: "2017/3/14", ratio: 0}, {date: "2017/3/15", ratio: 0.8}];
    
    var margin = {
        top: 20,
        right: 20,
        bottom: 40,
        left: 80
      },
      width = 500 - margin.left - margin.right,
      height = 300 - margin.top - margin.bottom;
    barHeight = 40;
    labelWidth = 0;
    
    tempArray2.sort(function(a, b) {
      return new Date(a.date) - new Date(b.date);
    })
    
    dateRange = Math.round((new Date(tempArray2[tempArray2.length - 1].date) - new Date(tempArray2[0].date)) / 1000 / 3600 / 24);
    
    svg = d3.select('body')
      .append("svg")
      .attr("style", "width: 500px\; height: 300px\;");
    
    var x = d3.scaleUtc().range([0, width])
      .domain([toUTCDate(tempArray2[0].date), calculateDays(toUTCDate(tempArray2[tempArray2.length - 1].date), 1)]);
    
    var y = d3.scaleBand()
      .range([height, 0])
      .padding(0.1)
      .domain(["Domain for testinginginging", "Another domain used for testing", "Horizontal bar"]);
    
    passBar = svg.selectAll(".passBar")
      .data(tempArray2)
      .enter();
    
    passBar.append("rect")
      .attr("class", "passBar")
      .attr("height", barHeight)
      .attr("width", function(d) {
        return x(calculateDays(toUTCDate(d.date), d.ratio)) - x(toUTCDate(d.date));
      })
      .attr("y", y("Horizontal bar") + (y.bandwidth() - barHeight) / 2)
      .attr("transform", function(d) {
        return "translate(" + (margin.left + x(toUTCDate(d.date))) + ", 0)";
      });
    
    failBar = svg.selectAll(".failBar")
      .data(tempArray2)
      .enter();
    
    failBar.append("rect")
      .attr("class", "failBar")
      .attr("height", barHeight)
      .attr("width", function(d) {
        return x(calculateDays(toUTCDate(d.date), 1 - d.ratio)) - x(toUTCDate(d.date));
      })
      .attr("y", y("Horizontal bar") + (y.bandwidth() - barHeight) / 2)
      .attr("transform", function(d) {
        return "translate(" + (margin.left + x(toUTCDate(d.date)) + x(calculateDays(toUTCDate(d.date), d.ratio)) - x(toUTCDate(d.date))) + ", 0)";
      });
    
    //add grid lines
    svg.append("g")
      .attr("class", "grid")
      .attr("transform", "translate(" + margin.left + "," + height + ")")
      .call(make_x_gridlines(dateRange)
        .tickSize(-height)
        .tickFormat("")
      )
    
    // always draw axis at last
    svg.append("g")
      .attr("transform", "translate(" + margin.left + "," + height + ")")
      .attr("class", "xAxis")
      .call(d3.axisBottom(x).ticks(dateRange).tickFormat(d3.utcFormat("%m-%d")))
      .selectAll("text")
      .style("text-anchor", "middle");
    svg.append("g")
      .attr("transform", "translate(" + margin.left + ", 0)")
      .attr("class", "yAxis")
      .call(d3.axisLeft(y))
      .selectAll("text")
      .attr("class", "cateName")
      .style("text-anchor", "start")
      .call(wrapText, margin.left - 13);
    
    function calculateDays(date, number) {
      date.setUTCDate(date.getUTCDate() + number);
      return date;
    }
    
    function make_x_gridlines(tickTime) {
      return d3.axisBottom(x).ticks(tickTime);
    }
    
    function toUTCDate(input) {
      var tempDate = new Date(input);
      return new Date(Date.UTC(tempDate.getFullYear(), tempDate.getMonth(), tempDate.getDate()));
    }
    
    
    
    function wrapText(text, width) {
      text.each(function() {
        var text = d3.select(this),
          textContent = text.text(),
          tempWord = addBreakSpace(textContent).split(/\s+/),
          x = text.attr('x'),
          y = text.attr('y'),
          dy = parseFloat(text.attr('dy') || 0),
          tspan = text.text(null).append('tspan').attr('x', x).attr('y', y).attr('dy', dy + 'em');
        for (var i = 0; i < tempWord.length; i++) {
          tempWord[i] = calHyphen(tempWord[i]);
        }
        textContent = tempWord.join(" ");
        var words = textContent.split(/\s+/).reverse(),
          word,
          line = [],
          lineNumber = 0,
          lineHeight = 1.1, // ems
          spanContent,
          breakChars = ['/', '&', '-'];
        while (word = words.pop()) {
          line.push(word);
          tspan.text(line.join(' '));
          if (tspan.node().getComputedTextLength() > width) {
            line.pop();
            spanContent = line.join(' ');
            breakChars.forEach(char => {
              // Remove spaces trailing breakChars that were added above
              spanContent = spanContent.replace(char + ' ', char);
            });
            tspan.text(spanContent);
            line = [word];
            tspan = text.append('tspan').attr('x', x).attr('y', y).attr('dy', lineHeight+'em').text(word);
          }
        }
        var emToPxRatio = parseInt(window.getComputedStyle(text._groups[0][0]).fontSize.slice(0, -2));
        text.attr("transform", "translate(-" + (margin.left - 13) + ", -" + lineHeight + ")");
    
        function calHyphen(word) {
          tspan.text(word);
          if (tspan.node().getComputedTextLength() > width) {
            var chars = word.split('');
            var asword = "";
            for (var i = 0; i < chars.length; i++) {
              asword += chars[i];
              tspan.text(asword);
              if (tspan.node().getComputedTextLength() > width) {
                if (chars[i - 1] !== "-") {
                  word = word.slice(0, i - 1) + "- " + calHyphen(word.slice(i - 1));
                }
                i = chars.length;
              }
            }
          }
          return word;
        }
      });
    
      function addBreakSpace(inputString) {
        var breakChars = ['/', '&', '-']
        breakChars.forEach(char => {
          // Add a space after each break char for the function to use to determine line breaks
          inputString = inputString.replace(char, char + ' ');
        });
        return inputString;
      }
    }
    svg {
        width: 100%;
        height: 100%;
        position: center;
    }
    .passBar {
        fill: #a6f3a6;
    }
    .failBar {
        fill: #f8cbcb;
    }
    .grid line {
        stroke: white;
        stroke-width: 2px;
    }
    .grid path {
        stroke-width: 0;
    }
    .xAxis {
        font-size: 15px;
        shape-rendering: crispEdges;
    }
    .yAxis {
        font-size: 15px;
        shape-rendering: crispEdges;
    }
    
    

    Main function about word wrap is this one: wrapText. Modified from https://bl.ocks.org/ericsoco/647db6ebadd4f4756cae and https://bl.ocks.org/mbostock/7555321

    Hope it can help.

提交回复
热议问题