D3.js将条形图分组渲染x轴不正确

时间:2016-03-01 16:59:35

标签: javascript d3.js bar-chart

我正在尝试实施分组条形图,但无法使其正常工作。我的数据结构如下:

[{
    "Type": "Email",
    "DataPoints": [{
        "xValue": 1,
        "Value": 17
    },
    {
        "xValue": 2,
        "Value": 59
    }]
},
{
    "Type": "Phone",
    "DataPoints": [{
        "xValue": 1,
        "Value": 1
    }]
}]

我的代码如下:

var margin = { top: 20, right: 0, bottom: 40, left: 50 },
    width = 960 - margin.left - margin.right,
    height = 500 - margin.top - margin.bottom,
    tooltipTextColour = "white";

if (data.length > 0) {
    var x0 = d3.scale.ordinal()
                    .rangeRoundBands([0, width], .1);

    var x1 = d3.scale.ordinal();

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

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

    var yAxis = d3.svg.axis()
                        .scale(y)
                        .orient("left")
                        .tickFormat(d3.format(".2s"));

    var svg = placeholder.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 + ')');

    var types = d3.entries(data);

    x0.domain(types.map(function (d) { return d.Type; }));
    x1.domain(types).rangeRoundBands([0, x0.rangeBand()]);
    y.domain([0, d3.max(types, function (d) { return d3.max(d.value.DataPoints, function (d) { return d.Value; }); })]);

    svg.append('g')
        .attr('class', 'x axis')
        .attr('transform', 'translate(0,' + height + ')')
        .call(xAxis);

    svg.append('g')
        .attr('class', 'y axis')
        .call(yAxis)
        .append('text')
          .attr("transform", "rotate(-90)")
          .attr("y", 6)
          .attr("dy", ".71em")
          .style("text-anchor", "end")
          .text("Complaints");

    var type = svg.selectAll(".type")
                    .data(data)
                    .enter()
                    .append('g')
                        .attr('class', 'type')
                        .attr('transform', function (d) { return 'translate(' + x0(d.Type) + ',0)'; });

    type.selectAll("rect")
            .data(function (d) { return d.DataPoints; })
            .enter()
            .append('rect')
            .attr('width', x1.rangeBand())
            .attr('x', function (d) { return x1(d.xValue); })
            .attr('y', function (d) { return y(d.Value); })
            .attr('height', function (d) { return height - y(d.Value); })
            .style('fill', function (d, i) { return color(d.xValue); });
}
else {
    placeholder.append('p').text('No Data to Display').style('font-weight', 'bold');
}

这会产生以下结果:

enter image description here

有人知道为什么会这样吗?我认为这是因为它没有正确渲染x轴,但我无法理解为什么。

非常感谢任何帮助。

1 个答案:

答案 0 :(得分:2)

您没有正确设置x0x1个域名。应该是这样的:

  // x0 is easy it's just the type
  x0.domain(data.map(function(d) {
    return d.Type
  }));
  // x1 is messy
  // this reduces each DataPoints xValue into a single array
  // and then creates a set from it to remove duplicates
  x1.domain(
    d3.set(data.reduce(function(previousValue, currentValue) {
      return previousValue.concat(currentValue.DataPoints.map(function(d) {
        return d.xValue
      }))
    }, [])).values()
  ).rangeRoundBands([0, x0.rangeBand()]);

工作代码:

<!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>
  <div id="placeholder"></div>
  <script>
    var placeholder = d3.select('#placeholder');

    var color = d3.scale.category10();

    var margin = {
        top: 20,
        right: 0,
        bottom: 40,
        left: 50
      },
      width = 960 - margin.left - margin.right,
      height = 500 - margin.top - margin.bottom,
      tooltipTextColour = "white";

    var data = [{
      "Type": "Email",
      "DataPoints": [{
        "xValue": 1,
        "Value": 17
      }, {
        "xValue": 2,
        "Value": 59
      }]
    }, {
      "Type": "Phone",
      "DataPoints": [{
        "xValue": 1,
        "Value": 1
      },{
        "xValue": 3,
        "Value": 30
      }]
    }];

    if (data.length > 0) {
      var x0 = d3.scale.ordinal()
        .rangeRoundBands([0, width], .1);

      var x1 = d3.scale.ordinal();

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

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

      var yAxis = d3.svg.axis()
        .scale(y)
        .orient("left")
        .tickFormat(d3.format(".2s"));

      var svg = placeholder.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 + ')');

      x0.domain(data.map(function(d) {
        return d.Type
      }));
      x1.domain(
        d3.set(data.reduce(function(previousValue, currentValue) {
          return previousValue.concat(currentValue.DataPoints.map(function(d) {
            return d.xValue
          }))
        }, [])).values()
      ).rangeRoundBands([0, x0.rangeBand()]);

      y.domain([0, d3.max(data, function(d) {
        return d3.max(d.DataPoints, function(d) {
          return d.Value;
        });
      })]);

      svg.append('g')
        .attr('class', 'x axis')
        .attr('transform', 'translate(0,' + height + ')')
        .call(xAxis);

      svg.append('g')
        .attr('class', 'y axis')
        .call(yAxis)
        .append('text')
        .attr("transform", "rotate(-90)")
        .attr("y", 6)
        .attr("dy", ".71em")
        .style("text-anchor", "end")
        .text("Complaints");

      var type = svg.selectAll(".type")
        .data(data)
        .enter()
        .append('g')
        .attr('class', 'type')
        .attr('transform', function(d) {
          return 'translate(' + x0(d.Type) + ',0)';
        });

      type.selectAll("rect")
        .data(function(d) {
          return d.DataPoints;
        })
        .enter()
        .append('rect')
        .attr('width', x1.rangeBand())
        .attr('x', function(d) {
          return x1(d.xValue);
        })
        .attr('y', function(d) {
          return y(d.Value);
        })
        .attr('height', function(d) {
          return height - y(d.Value);
        })
        .style('fill', function(d, i) {
          return color(d.xValue);
        });
    } else {
      placeholder.append('p').text('No Data to Display').style('font-weight', 'bold');
    }
  </script>
</body>

</html>