how to set the domain and scale on an axis on a nvd3.js multiBarChart

前端 未结 3 576
情话喂你
情话喂你 2021-02-13 17:04

In d3.js you can set an x axis to use d3.time.scale() then set x.domain([start_date, end_date]) and it will \'fill in\' any missing dates that aren\'t

3条回答
  •  Happy的楠姐
    2021-02-13 17:06

    There is really no need to interpolate your values. You actually can modify the scale of most nvd3 charts, including multiBarCharts, although there is some extra work that needs to be done to make it work.

    The basic thing you need to do is this:

    var xScale = d3.time.scale();
    chart.multibar.xScale(xScale);
    

    Then that should just work! Except it doesn't, because the multiBarChart assumes that the xScale is d3.scale.ordinal(). So you will need to fake being that type by setting xScale.rangeBands and xScale.rangeBand:

    xScale.rangeBands = xScale.range;
    xScale.rangeBand = function() { return (1 - chart.groupSpacing()) * SOME_VALUE };
    

    The problem now is getting SOME_VALUE. This needs to equal the width of an individual bar, which depends on two things: the width of the whole chart and the number of ticks there would be, including the zero values that are missing in the data.

    Here's how nvd3 gets the available width internally:

    var container = d3.select('#chart svg'),
        availableWidth = (chart.width() || parseInt(container.style('width')) || 960) - chart.margin().left - chart.margin().right;
    

    However, if the window resizes, you will need to refresh this value:

    nv.utils.windowResize(function() {
        availableWidth = (chart.width() || parseInt(container.style('width')) || 960) - chart.margin().left - chart.margin().right;
    });
    

    As for getting the number of ticks, this depends solely on your data. In your case, there will be 11 ticks: every year between 2001 and 2011. So we'll go with that. Therefore, the entire scale definition looks like this:

    var container = d3.select('#chart svg'),
        availableWidth,
        numTicks = 11,
        xScale = d3.time.scale();
    
    function updateAvailableWidth() {
        availableWidth = (chart.width() || parseInt(container.style('width')) || 960) - chart.margin().left - chart.margin().right;
    }
    updateAvailableWidth();
    nv.utils.windowResize(updateAvailableWidth);
    
    xScale.rangeBands = xScale.range;
    xScale.rangeBand = function() { return (1 - chart.groupSpacing()) * availableWidth / numTicks; };
    
    chart.multibar.xScale(xScale);
    

    Finally, you need to set your xDomain manually. If you did this with the ordinal scale it had before, it would fail, but with a linear time scale it will work excellently:

    chart.xDomain([new Date().setFullYear('2001'), new Date().setFullYear('2011')]);
    

    Putting it all together, here is your example code (pasteable into http://nvd3.org/livecode/#codemirrorNav):

    nv.addGraph(function() {
        var chart = nv.models.multiBarChart(),
            container = d3.select('#chart svg'),
            availableWidth,
            numTicks = 11,
            xScale = d3.time.scale();
    
        function updateAvailableWidth() {
            availableWidth = (chart.width() || parseInt(container.style('width')) || 960) - chart.margin().left - chart.margin().right;
        }
        updateAvailableWidth();
        nv.utils.windowResize(updateAvailableWidth);
    
        xScale.rangeBands = xScale.range;
        xScale.rangeBand = function() { return (1 - chart.groupSpacing()) * availableWidth / numTicks; };
    
        chart.multibar.xScale(xScale);
        chart.xDomain([new Date().setFullYear('2001'), new Date().setFullYear('2011')]);
    
        chart.xAxis
            .tickFormat(function(d){ return d3.time.format('%y')(new Date(d)); });
    
        chart.yAxis
            .tickFormat(d3.format(',f'));
    
        chart.reduceXTicks(false);
        chart.showControls(false);
    
        var data = [{
          'key': 'GB by year',
          'values': [
            {x: new Date().setFullYear('2001'), y: 0.12},
            {x: new Date().setFullYear('2004'), y: 0.03},
            {x: new Date().setFullYear('2005'), y: 0.53},
            {x: new Date().setFullYear('2006'), y: 0.43},
            {x: new Date().setFullYear('2007'), y: 5.5},
            {x: new Date().setFullYear('2008'), y: 9.9},
            {x: new Date().setFullYear('2009'), y: 26.85},
            {x: new Date().setFullYear('2010'), y: 0.03},
            {x: new Date().setFullYear('2011'), y: 0.12}
          ]
        }];        
    
        container.datum(data).transition().duration(500).call(chart);
    
        nv.utils.windowResize(chart.update);
    
        return chart;
    });
    

提交回复
热议问题