Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ChartJS:align zeros on chart with multi axes

Tags:

chart.js

How to align zeros on chart with multi axes, if there are both positive and negative values in dataset?

I want zeroes to be on the same line.

I dont like this: Graph image

link to jsfiddle

new Chart(canvas, {
  type: 'line',
  data: {
    labels: ['1', '2', '3', '4', '5'],
    datasets: [{
      label: 'A',
      yAxisID: 'A',
      data: [-10, 96, 84, 76, 69]
    }, {
      label: 'B',
      yAxisID: 'B',
      data: [-2, 3, 5, 2, 3]
    }]
  },
  options: {
    scales: {
      yAxes: [{
        id: 'A',
        type: 'linear',
        position: 'left',
      }, {
        id: 'B',
        type: 'linear',
        position: 'right',

      }]
    }
  }
});

actualy, The example on the official web page has the same problem. Looks so messy.

like image 355
Meradin Avatar asked Mar 08 '23 12:03

Meradin


1 Answers

The answer above is great if you know the ranges in advance and can specify min and max for each ticks object in advance. If, like me, you're trying to produce the effect with generated data, you'll need something that computes these values for you. The following does this without recourse to hard-coded values. It does generate ugly max / min values - but there's another fix for that: Hide min and max values from y Axis in Chart.js. It may also generate less than ideal spacing in which case a simple fudge factor applied to the min and max values may be used.

function scaleDataAxesToUnifyZeroes (datasets, options) {
  let axes = options.scales.yAxes
  // Determine overall max/min values for each dataset
  datasets.forEach(function (line) {
    let axis = line.yAxisID ? axes.filter(ax => ax.id === line.yAxisID)[0] : axes[0]
    axis.min_value = Math.min(...line.data, axis.min_value || 0)
    axis.max_value = Math.max(...line.data, axis.max_value || 0)
  })
  // Which gives the overall range of each axis
  axes.forEach(axis => {
    axis.range = axis.max_value - axis.min_value
    // Express the min / max values as a fraction of the overall range
    axis.min_ratio = axis.min_value / axis.range
    axis.max_ratio = axis.max_value / axis.range
  })
  // Find the largest of these ratios
  let largest = axes.reduce((a, b) => ({
    min_ratio: Math.min(a.min_ratio, b.min_ratio),
    max_ratio: Math.max(a.max_ratio, b.max_ratio)
  }))
  // Then scale each axis accordingly
  axes.forEach(axis => {
    axis.ticks = axis.ticks || { }
    axis.ticks.min = largest.min_ratio * axis.range
    axis.ticks.max = largest.max_ratio * axis.range
  })
}
like image 155
Richard Wheeldon Avatar answered Apr 30 '23 01:04

Richard Wheeldon