Chartjs-Chart.js 4 linear gradient based on the lowest values

0👍

With your scriptable borderColor: function(context, opts)
you extracted, chart, the chart instance, and the chartArea and
the canvas 2d context as:

const chart = context.chart;
const {ctx, chartArea} = chart;

You can also find the following:

  • the id of the y axis used by this dataset:
    const yScaleId = opts._proxy.scales.y.id; 
    

    This is the rigorous approach, for a chart with multiple y axes; in practice you probably know the id, and in this case, where there;s just the default axis, the id is y

  • the actual y scale object
       chart.scales[yScaleId]
    

    and its min and max values, scaleMin and scaleMax

  • the data array of this dataset:
    context.dataset.data
    

    and from it the minimum and maximum values of the data, dataMin and dataMax.

  • With the extreme values for the axis and for the data, it is a simple
    linear relation that would give:

    const fracStart = (dataMin - scaleMin)/(scaleMax - scaleMin),
           fracEnd = (dataMax - scaleMin)/(scaleMax - scaleMin);
    

    These are the numbers that should replace 0 and 1 in the definition of the linear vertical gradient, so that the first color corresponds to the minimum value and the final color corresponds to the maximum value.

Full code from your codepen:

const ctx = document.getElementById('myChart');

let width, height, gradient;
function getGradient(ctx, chartArea, fracStart = 0, fracEnd = 1) {
  const chartWidth = chartArea.right - chartArea.left;
  const chartHeight = chartArea.bottom - chartArea.top;
  if (!gradient || width !== chartWidth || height !== chartHeight) {
    // Create the gradient because this is either the first render
    // or the size of the chart has changed
    width = chartWidth;
    height = chartHeight;
    gradient = ctx.createLinearGradient(0, chartArea.bottom, 0, chartArea.top);
    const fracMid = (fracStart + fracEnd) / 2;
    gradient.addColorStop(fracStart, 'green');
    gradient.addColorStop(fracMid, 'yellow');
    gradient.addColorStop(fracEnd, 'red');
  }

  return gradient;
}

new Chart(ctx, {
  type: 'line',
  data: {
    labels: ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'],
    datasets: [{
      label: '# of Votes',
      data: [31, 27, 30, 51, 28, 32],
      stepped: true,
      borderWidth: 4,
      borderColor: function(context, opts) {
        const chart = context.chart;
        const {ctx, chartArea} = chart;
        
         // the id of the y scale of this dataset:
        const yScaleId = opts._proxy.scales.y.id; 
        const scaleMin = chart.scales[yScaleId].min,
              scaleMax = chart.scales[yScaleId].max;
        const dataMin = Math.min(...context.dataset?.data ?? [scaleMin]),
              dataMax = Math.max(...context.dataset?.data ?? [scaleMax]);
        const fracStart = (dataMin - scaleMin)/(scaleMax - scaleMin),
              fracEnd = (dataMax - scaleMin)/(scaleMax - scaleMin);

        if (!chartArea) {
          // This case happens on initial chart load
          return;
        }
        return getGradient(ctx, chartArea, fracStart, fracEnd);
      },
    }]
  },
  options: {
    scales: {
      y: {
        beginAtZero: true
      }
    }
  }
});
<div>
  <canvas id="myChart"></canvas>
</div>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>

and codepen fork.

Leave a comment