javascriptchart.js

Charts.js line chart: Show the most recent date on x-axis when setting ticks.maxTicksLimit


I'm currently using making a line chart with Charts.js over the past 30 years.

Although I have a data point for every year, I can't put a label for each year on the x-axis. 30 years won't fit there. So, I've chosen to only list some years, by setting

ticks: {
    maxTicksLimit: SOME_LIMIT_I_MADE
}

Here is a photo of what my chart looks like

Chart Image

The most recent data point is in 2020 and the oldest is in 1991.

It seems that setting maxTicksLimit starts the scale at the earliest year. Then the scale skips forwards in regular intervals.

What I really want is for the most recent year 2020 to be displayed on the scale (so that users can see that the data is new). Is there a way to make the scale start at the max year, and then the skip backwards from there (the opposite of how my graph currently is)?

Thanks!


Solution

  • This could be solved by somehow implement your own ticks.maxTicksLimit on the xAxis. You would have to proceed as follows.

    1. Define the xAxis as a time cartesian axis that accepts the data as an array of data points using an object containing x and y properties.
    2. Generate a labels array out of the years contained in your data. This array should contain the starting year and end year together with a number of equally spread years between both (see function createLabels in the code snippet below).
    3. Tell Chart.js to generate ticks on the xAxis from given labels by defining tick.sources: 'labels'.

    const data = [];
    for (let year = new Date().getFullYear() - 29; year <= new Date().getFullYear(); year++) {
      data.push({
        x: year.toString(),
        y: Math.floor((Math.random() * 6) + 1)
      })
    }
    
    const maxTicksLimit = 6;
    function createLabels() {
      const years = data.map(o => Number(o.x));
      const startTime = years[0];
      const endTime = years[years.length - 1];
      const tickGap = (endTime - startTime) / (maxTicksLimit - 1);
      const labels = [startTime];
      for (let i = 1; i < maxTicksLimit - 1; i++) {
        labels.push(Math.round(startTime + i * tickGap));
      }
      labels.push(endTime);
      return labels.map(l => l.toString());
    }
    
    new Chart('myChart', {
      type: 'line',
      data: {
        labels: createLabels(),
        datasets: [{
          label: 'Demo',
          fill: false,
          data: data,
          borderColor: 'blue'
        }]
      },
      options: {
        scales: {
          xAxes: [{
            type: 'time',
            time: {
              parser: 'YYYY',
              unit: 'year'
            },
            ticks: {
              source: 'labels'
            }      
          }],
          yAxes: [{
            ticks: {
              beginAtZero: true
            }
          }]
        }
      }
    });
    <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.bundle.min.js"></script>
    <canvas id="myChart" height="80"></canvas>