D3.js: calculate width of bars in time scale with changing range?

后端 未结 4 1569
猫巷女王i
猫巷女王i 2020-12-29 03:37

I\'m building a D3 bar chart with a time scale on the x-axis. The range of the x-axis can vary.

How can I specify the correct width for the bars on the bar chart? I

4条回答
  •  慢半拍i
    慢半拍i (楼主)
    2020-12-29 04:26

    Even though I agree with Scott that a bar chart is meant to be used with ordinal or categorical data on x axis, I guess the question is more about a convenience of drawing a time axis. As d3.time.scale does a really good job with help of d3.time.format.multi of drawing time axis of various duration (hours, days, months, etc.) and its ticks, it could be a good idea to combine d3.time.scale for an axis, and d3.scale.ordinal for a band width calculation.

    The snippet below is inspired by the discussion in D3 Google Group about the topic. The unit for ordinal scale is a day.

    function prepare(data)
    {
      var dateParse = d3.time.format('%Y-%m-%d');
      data.forEach(function(v)
      {
        v.date = dateParse.parse(v.date);
      });
    
      var dateValueMap = data.reduce(function(r, v)
      {
        r[v.date.toISOString()] = v.value;
        return r;
      }, {});
    
      var dateExtent = d3.extent(data.map(function(v)
      {
        return v.date;
      }));
    
      // make data have each date within the extent
      var fullDayRange = d3.time.day.range(
        dateExtent[0], 
        d3.time.day.offset(dateExtent[1], 1)
      );
      fullDayRange.forEach(function(date)
      {
        if(!(date.toISOString() in dateValueMap))
        {
          data.push({
            'date'  : date,
            'value' : 0
          });
        }
      });
    
      data = data.sort(function(a, b)
      {
        return a.date - b.date;
      });
    
      return data;
    }
    
    function draw(data)
    {
      var margin = {
        'top'    : 10, 
        'right'  : 20, 
        'bottom' : 20, 
        'left'   : 60
      };
      var size = {
        'width'  : 600 - margin.left - margin.right,
        'height' : 180 - margin.top - margin.bottom
      };
    
      var svg = d3.select('#chart').append('svg')
        .attr('width',  '100%')
        .attr('height', '100%')
        .append('g')
        .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
    
      var dates = data.map(function(v)
      {
        return v.date;
      });
      var x = d3.time.scale()
        .range([0, size.width])
        .domain(d3.extent(dates));
    
      var y = d3.scale.linear()
        .range([size.height, 0])
        .domain([0, d3.max(data.map(function(v)
        {
          return v.value;
        }))]);
    
      var xAxis = d3.svg.axis()
      .scale(x)
      .orient('bottom');
    
      var yAxis = d3.svg.axis()
        .scale(y)
        .orient('left');
    
      var barWidth = d3.scale.ordinal()
        .domain(dates)
        .rangeRoundBands(x.range(), 0.1)
        .rangeBand(); 
    
      svg.append('g')
        .attr('class', 'x axis')
        .attr('transform', 'translate(' + barWidth / 2 + ',' + size.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('Amount');
    
      svg.selectAll('.bar')
        .data(data)
        .enter()
        .append('rect')
        .attr('class', 'bar')
        .attr('x', function(d) 
        { 
          return x(d.date); 
        })
        .attr('width', barWidth)
        .attr('y', function(d) 
        { 
          return y(d.value); 
        })
        .attr('height', function(d) 
        { 
          return size.height - y(d.value); 
        });
    }
    
    function getData()
    {
      return [
        {'date': '2014-01-31', 'value': 5261.38}, 
        {'date': '2014-02-02', 'value': 7460.23}, 
        {'date': '2014-02-03', 'value': 8553.39}, 
        {'date': '2014-02-04', 'value': 3897.18}, 
        {'date': '2014-02-05', 'value': 2822.22}, 
        {'date': '2014-02-06', 'value': 6762.49}, 
        {'date': '2014-02-07', 'value': 8624.56}, 
        {'date': '2014-02-08', 'value': 7870.35}, 
        {'date': '2014-02-09', 'value': 7991.43}, 
        {'date': '2014-02-10', 'value': 9947.14}, 
        {'date': '2014-02-11', 'value': 6539.75}, 
        {'date': '2014-02-12', 'value': 2487.3}, 
        {'date': '2014-02-15', 'value': 3517.38}, 
        {'date': '2014-02-16', 'value': 1919.08}, 
        {'date': '2014-02-19', 'value': 1764.8}, 
        {'date': '2014-02-20', 'value': 5607.57}, 
        {'date': '2014-02-21', 'value': 7148.87}, 
        {'date': '2014-02-22', 'value': 5496.45}, 
        {'date': '2014-02-23', 'value': 296.89}, 
        {'date': '2014-02-24', 'value': 1578.59}, 
        {'date': '2014-02-26', 'value': 1763.16}, 
        {'date': '2014-02-27', 'value': 8622.26},
        {'date': '2014-02-28', 'value': 7298.99}, 
        {'date': '2014-03-01', 'value': 3014.06}, 
        {'date': '2014-03-05', 'value': 6971.12}, 
        {'date': '2014-03-06', 'value': 2949.03}, 
        {'date': '2014-03-07', 'value': 8512.96}, 
        {'date': '2014-03-09', 'value': 7734.72}, 
        {'date': '2014-03-10', 'value': 6703.21}, 
        {'date': '2014-03-11', 'value': 9798.07}, 
        {'date': '2014-03-12', 'value': 6541.8}, 
        {'date': '2014-03-13', 'value': 915.44}, 
        {'date': '2014-03-14', 'value': 9570.82}, 
        {'date': '2014-03-16', 'value': 6459.17}, 
        {'date': '2014-03-17', 'value': 9389.62},
        {'date': '2014-03-18', 'value': 6216.9}, 
        {'date': '2014-03-19', 'value': 4433.5}, 
        {'date': '2014-03-20', 'value': 9017.23},
        {'date': '2014-03-23', 'value': 2828.45},
        {'date': '2014-03-24', 'value': 63.29}, 
        {'date': '2014-03-25', 'value': 3855.02},
        {'date': '2014-03-26', 'value': 4203.06},
        {'date': '2014-03-27', 'value': 3132.32}
      ];
    }
    
    draw(prepare(getData()));
    #chart {
      width  : 600px;
      height : 180px;
    }
    .bar {
      fill : steelblue;
    }
    
    .axis {
      font : 10px sans-serif;
    }
    
    .axis path,
    .axis line {
      fill            : none;
      stroke          : #000;
      shape-rendering : crispEdges;
    }
    
    .x.axis path {
      display : none;
    }
    
    

提交回复
热议问题