2015-06-26 162 views
0

給出下面的代碼。我可以創建一個條形圖,由隨機數據填充並使其適當地顯示。不幸的是,對我來說,我無法讓圖表的update適當呈現。d3JS圖表沒有正確渲染

座標軸似乎會相應更新,但圖形的橫條相對於其軸不顯示在正確的位置。下面有一個JSFiddle來說明這個問題。您會注意到沿着x軸在各個位置呈現的條形圖。

var Chart = (function(){ 
    function Chart(options){ 
     this.chart = d3.select(options.el); 
     this.margin = options.dimensions.margin; 
     this.height = options.dimensions.height; 
     this.width = options.dimensions.width; 
     this.data = options.data; 
    } 
    return Chart; 
}()); 
var BarChart = (function(){ 
    function BarChart(options){ 
     Chart.call(this, options); 

     this.xscale; 
     this.vguidescale; 
     this.vaxis; 
     this.haxis; 

     this.height = this.height - this.margin.top - this.margin.bottom; 
     this.width = this.width - this.margin.left - this.margin.right; 
     this.barcolors = createBarColors(options.barcolors); 
     this.verticalTickCount = options.verticalTickCount; 
     this.horizontalTickRangeCount = options.horizontalTickRangeCount || 10; 
     this.chart = this.chart 
      .append('svg') 
       .attr('class', 'metric-chart') 
       .attr('width', this.width + this.margin.left + this.margin.right) 
       .attr('height', this.height + this.margin.top + this.margin.bottom) 
       .append('g') 
        .attr('class', 'metric-chart__chart-bar') 
        .attr('transform', 'translate('+ this.margin.left +', '+ this.margin.top +')'); 
     this.vguide = d3.select('svg').append('g'); 
     this.hguide = d3.select('svg').append('g'); 
    } 

    BarChart.prototype = Chart.prototype; 
    BarChart.prototype.update = function(data) { 
     var that = this; 

     this.xscale = _getAxisScaleX(data, this.width); 
     this.yscale = _getAxisScaleY(data, this.height); 

     this.bars = this.chart.selectAll('rect') 
      .data(d3.values(data)) 
      .order(); 

     this.bars.remove() 
     this.bars.enter() 
      .append('rect') 
       .style('fill', function(d,i) { return that.barcolors[i % that.barcolors.length]; }) 
       .attr('width', that.xscale.rangeBand()) 
       .attr('x', function(d,i) { 
        return that.xscale(i); 
       }) 
       .attr('height', 0) 
       .attr('y', this.height) 
       .transition() 
       .attr('height', function(d) { return that.yscale(d); }) 
       .attr('y', function(d) { 
        return that.height - that.yscale(d); 
       }) 
       .duration(1500) 
       .ease('elastic'); 

     this.bars.exit().transition().attr('height', 0).remove(); 

     this.vguidescale = _getVerticalGuideScale(data, this.height); 
     this.haxis = d3.svg.axis() 
      .scale(this.xscale) 
      .orient('bottom') 
      .tickValues(this.xscale.domain().filter(function(d, i) { 
       var remainder = i % that.horizontalTickRangeCount; 
       return remainder === 0; 
      })); 
     this.vaxis = d3.svg.axis() 
      .scale(this.vguidescale) 
      .orient('left') 
      .ticks(this.verticalTickCount); 

     this.vguide 
      .attr('transform', 'translate(' + this.margin.left + ', ' + this.margin.top + ')') 
      .attr('class', 'metric-chart__vert-guide'); 
     this.hguide 
      .attr('transform', 'translate(' + this.margin.left + ', ' + (this.height + this.margin.top) + ')') 
      .attr('class', 'metric-chart__horz-guide'); 

     this.vaxis(this.vguide); 
     this.haxis(this.hguide); 
    }; 

    function _getAxisScaleX (data, width) { 
     return d3.scale.ordinal() 
      .domain(d3.range(0, data.length)) 
      .rangeBands([0, width], 0.2); 
    } 

    function _getAxisScaleY (data, height) { 
     return d3.scale.linear() 
      .domain([0, d3.max(data)]) 
      .range([0, height]); 
    } 

    function _getVerticalGuideScale (data, height) { 
     return d3.scale.linear() 
      .domain([0, d3.max(data)]) 
      .range([height, 0]); 
    } 

    function createBarColors(colors){ 
     return colors || ['#363636','#565656', '#868686']; 
    } 

    return BarChart; 
}()); 

用法:

var chartDimensions = { 
    margin : { top: 10, right: 10, bottom: 25, left: 25 }, 
    height : 200, 
    width : 500 
}, chartoptions; 

chartoptions = { 
    el : '.graph', 
    data: genRandomArray(0, 50), 
    dimensions : chartDimensions, 
    verticalTickCount : 10, 
    horizontalTickRangeCount : 5 
}; 

/* generate bar chart and update on an interval ... */ 
var chart = new BarChart(chartoptions); 
(function updateChart(){ 
    chart.update(genRandomArray(0, genrandom(10, 90))); 
    setTimeout(updateChart, 4000); 
}()); 

問題演示:

http://jsfiddle.net/h8ztvmq4/

回答

1

我覺得你稍微濫用D3選擇的概念:當你做this.bars = ... .data() ...你最終得到的更新選擇,即自從之前改變其值的所有元素。在那個選擇上,你叫.remove(),暗示更改後的元素應該被刪除。這顯然不是你想要的。之後,您可以正確處理enter()exit()選擇,添加新元素並刪除不再需要的舊元素。如果您在this.bars.enter()調用之前將this.bars.remove()僅撥出第一個電話,它看起來更像您可能想要的:尾巴(更改條數)按預期工作。只是,頭部還沒有正確更新。爲此,您需要使用enter()呼叫之前的更新選項,以修改條的相應屬性(例如,x,y,height),也許使用轉換。

Here's an updated fiddle.它仍然看起來不太完美,因爲每當條數發生變化時都會更改x尺度。因此,變化的酒吧需要水平移動,但是退出的酒吧並不一致,所以這兩個選擇會短暫重疊。您應該考慮在單獨的轉換中更新軸。

+0

你先生/女士,熟悉d3js。你也是非常有幫助的,並且「頭撞到了頭上」。感謝您的解釋,解決方案和細節。我現在可以繼續使用d3js。非常感激! – culturalanomoly