Time scale bar charts overlap

I’m pretty sure this is not default behaviour as when using a time axis scale with a bar chart, the bars overlap. If you compare this to a standard bar chart the bars would shrink in size. The screen shots are taken from the samples.

Resized
resized

Overlapping
overlapped

As far as I can work out I believe it could be to do with the calculation of the width based on the number of ticks. As the ticks are reduced on the time version to fit the label in the calculation should really still use the number of points. But so far I have not manage to find the exact location in the source.

John

Author: Fantashit

2 thoughts on “Time scale bar charts overlap

  1. In my case, this fix seems to introduce the same problem it tried to fix (im using v2.1.6 where this fix is already in the code). Let me show the output before removing the fix piece of code from chart js code:
    beforecomment

    Then after commenting this code:
    if (xScale.ticks.length !== me.chart.data.labels.length) { var perc = xScale.ticks.length / me.chart.data.labels.length; fullBarWidth = (fullBarWidth * perc); }
    I get the following output, which seems to fix my issue:
    aftercomment

    Im not sure about the “me.chart.data.labels.length” variable, where and how is its value defined? I think this problem has something to do with the fact im using two datasets, but im not sure, it might be that im doing something wrong.

  2. A colleague and I have been trying to fix the overlapping issue and have come up with the following naive approach that temporarily fixes the issue for our needs. Since we are new to the ChartJS library and are not able to spend the time to re-work the bar chart so that our modifications are properly optimized we would like to share our changes with the community.

    In Summary

    We have modified the getRuler function so that it can find the shortest distance between two points in a data series and uses that distance for the bar width. As mentioned above, this approach is not fully optimized as it will require a little rework so that getRuler is not recalculating the minimum distance every single time it is called. Instead it will need to integrate with responsive and somehow cache this calculation.

    Whats Fixed

    • Bar Widths on Time Scale Axes (getRuler)
    • Positioning of Stacked Bars on Time Scale Axes (getRuler)
    • Category Widths on A Bar Chart’s Time Scale Axes (getRuler)
    • Alignment of Bars over Time Scale Labels (calculateBarX)

    First Change made

    getRuler: function(index) {
      var me = this;
      var meta = me.getMeta();
      var xScale = me.getScaleForId(meta.xAxisID);
      var datasetCount = me.getBarCount();
    
      var tickWidth;
    
      if (xScale.options.type === 'category') {
        tickWidth = xScale.getPixelForTick(index + 1) - xScale.getPixelForTick(index);
      } else {
        // Average width
        tickWidth = xScale.width / xScale.ticks.length;
      }
      var categoryWidth = tickWidth * xScale.options.categoryPercentage;
      var categorySpacing = (tickWidth - (tickWidth * xScale.options.categoryPercentage)) / 2;
      var fullBarWidth = categoryWidth / datasetCount;
    
      /*** START MODIFICATIONS ***/
    
      // correct the bar width in case of time series modifying ticks
      if (xScale.ticks.length !== me.chart.data.labels.length) {
        var dataPos = [];
        for (var i = 0; i < meta.data.length; i++) {
          dataPos.push(xScale.getPixelForValue(null, meta.data[i]._index, meta.data[i]._datasetIndex, me.chart.isCombo));
        }
    
        dataPos.sort(function(a,b){
          return a-b;
        })
    
        var minDist;
        var tmpDist;
        for (var i = 1; i < dataPos.length; i++) {
          tmpDist = dataPos[i] - dataPos[i-1];
          if(!minDist || tmpDist < minDist) { minDist = tmpDist; }
        }
    
        if(fullBarWidth > minDist) { fullBarWidth = minDist; }
    
        categoryWidth = fullBarWidth;
        fullBarWidth = fullBarWidth / datasetCount;
      }
    
      /*** END MODIFICATIONS ***/
    
      var barWidth = fullBarWidth * xScale.options.barPercentage;
      var barSpacing = fullBarWidth - (fullBarWidth * xScale.options.barPercentage);
    
      return {
        datasetCount: datasetCount,
        tickWidth: tickWidth,
        categoryWidth: categoryWidth,
        categorySpacing: categorySpacing,
        fullBarWidth: fullBarWidth,
        barWidth: barWidth,
        barSpacing: barSpacing
      };
    }
    
    

    Second Change Made

    calculateBarX: function(index, datasetIndex) {
      var me = this;
      var meta = me.getMeta();
      var xScale = me.getScaleForId(meta.xAxisID);
      var barIndex = me.getBarIndex(datasetIndex);
    
      var ruler = me.getRuler(index);
      var leftTick = xScale.getPixelForValue(null, index, datasetIndex, me.chart.isCombo);
      leftTick -= me.chart.isCombo ? (ruler.tickWidth / 2) : 0;
    
      /*** START MODIFICATIONS ***/
      if (xScale.options.type == 'time') { leftTick -= ruler.barWidth * 1.38; }
      /*** END MODIFICATIONS ***/
    
      if (xScale.options.stacked) {
        return leftTick + (ruler.categoryWidth / 2) + ruler.categorySpacing;
      }
    
      return leftTick +
        (ruler.barWidth / 2) +
        ruler.categorySpacing +
        (ruler.barWidth * barIndex) +
        (ruler.barSpacing / 2) +
        (ruler.barSpacing * barIndex);
    }
    

    Before the code change: https://jsfiddle.net/a1x7p17h/2/

    After the code change: https://jsfiddle.net/fmbycrLx/2/

Comments are closed.