in chart js (4.4.8)
i want to give discrete line colors based on a baseline and threshold value of the data point.
when i give
ctx.createLinearGradient(0, yBaselinePixel, 0, yThresholdPixel);
example example reference link
i'm using chart.js 4.4.8 in vue.js 4.2.5
const getGradient = (ctx, chartArea, chart, yBaselinePixel, yThresholdPixel) => {
const normalize = (pixel) => (pixel - chartArea.bottom) / (chartArea.top - chartArea.bottom);
const stopBaseline = normalize(yBaselinePixel);
const stopThreshold = normalize(yThresholdPixel);
const gradient = ctx.createLinearGradient(0, yBaselinePixel, 0, yThresholdPixel);
gradient.addColorStop(0, "#22c55e");
gradient.addColorStop(stopBaseline, "#f9ba4f");
gradient.addColorStop(stopThreshold, "#f59e0b");
gradient.addColorStop(1, "#ef4444");
return gradient;
}
let flushRateGradient;
const flushRateChartData = {
labels: res.data.map(d => d.build_no),
datasets: [
{
label: 'flush rate',
data: new Array(res.data.length).fill(0),
pointRadius: 2,
// borderColor: '#bd7ebe99',
legendColor: '#bd7ebe99',
tension: 0.2,
isShown: true,
borderColor: (context) => { // the gradient approach
const chart = context.chart;
const { ctx, chartArea } = chart;
if (!chartArea) { // on initial chart load
return;
}
if (flushRateGradient) {
return flushRateGradient;
}
const yBaselinePixel = chart.scales.y.getPixelForValue(flushRateBaselineValue);
const yThresholdPixel = chart.scales.y.getPixelForValue(flushRateThresholdValue);
flushRateGradient = getGradient(ctx, chartArea, chart, yBaselinePixel, yThresholdPixel);
return flushRateGradient;
},
/* ?????
HOW TO ADD MULTIPLE COLOR STOP HERE IN SEGMENT APPROACH WITHOUT APPLYING GRADIENT.
AS THIS IS COLORING THE WHOLE LINE SEGMENT WITH ONE COLOR
????? */
// segment: { // the segment approach
// borderColor: ctx => (ctx.p1.parsed.y > flushRateThresholdValue) ? 'red' : '#bd7ebe99',
// }
},
{
label: '', // actual threshold drawn on chart
type: 'line',
data: new Array(res.data.length).fill(flushRateBaselineValue),
pointRadius: 0,
borderColor: '#ea6e6e',
borderDash: [4, 7],
borderWidth: 2,
tension: 0.2,
isShown: true,
},
{
label: '', // actual baseline drawn on chart
type: 'line',
data: new Array(res.data.length).fill(flushRateBaselineValue),
pointRadius: 0,
borderColor: '#f9ba4f',
borderDash: [4, 7],
borderWidth: 2,
tension: 0.2,
isShown: true,
},
],
}
I can't fully verify your code, as you haven't included data
and other parameters like flushRateBaselineValue
and flushRateBaselineValue
.
But if the stopBaseLine
and stopThreshold
values are computed correctly, and you want to use color "#22c55e"
from 0
to stopBaseLine
, color "#f9ba4f"
from stopBaseLine
to stopThereshold
and color #ef4444
from stopThereshold
to 1
, you have to set the gradient this way:
const gradient = ctx.createLinearGradient(0, chartArea.bottom, 0, chartArea.top);
gradient.addColorStop(0, "#22c55e");
gradient.addColorStop(stopBaseline, "#22c55e");
gradient.addColorStop(stopBaseline, "#f9ba4f");
gradient.addColorStop(stopThreshold, "#f9ba4f");
gradient.addColorStop(stopThreshold, "#ef4444");
gradient.addColorStop(1, "#ef4444");
The arguments of the function ctx.createLinearGradient
are pixel positions of the full extent of the gradient.
And, if the color is to be the same on a fragment of the gradient - the fractional 0 to 1 positions of the color stops, should repeat that color for the start and end of that interval (as the linear interpolation between two equal values is constant -- the same value on the whole interval).
Here are those changes applied to a (debuggable) snippet, based on a standard example from chart.js docs:
const getGradient = (ctx, chartArea, chart, yBaselinePixel, yThresholdPixel) => {
const normalize = (pixel) => (pixel - chartArea.bottom) / (chartArea.top - chartArea.bottom);
const stopBaseline = normalize(yBaselinePixel);
const stopThreshold = normalize(yThresholdPixel);
const gradient = ctx.createLinearGradient(0, chartArea.bottom, 0, chartArea.top);
gradient.addColorStop(0, "#22c55e");
gradient.addColorStop(stopBaseline, "#22c55e");
gradient.addColorStop(stopBaseline, "#f9ba4f");
gradient.addColorStop(stopThreshold, "#f9ba4f");
gradient.addColorStop(stopThreshold, "#ef4444");
gradient.addColorStop(1, "#ef4444");
return gradient;
}
let flushRateGradient;
const flushRateBaselineValue = 5;
const flushRateThresholdValue = 7;
const config = {
type: 'line',
data: {
labels: ["January", "February", "March", "April", "May", "June", "July"],
datasets: [{
label: "Dataset 1",
data: [
10, 5, 2, 10, 4, 1, 10
],
tension: 0.2,
borderColor: (context) => { // the gradient approach
const chart = context.chart;
const { ctx, chartArea } = chart;
if (!chartArea) { // on initial chart load
return;
}
if (flushRateGradient) {
return flushRateGradient;
}
const yBaselinePixel = chart.scales.y.getPixelForValue(flushRateBaselineValue);
const yThresholdPixel = chart.scales.y.getPixelForValue(flushRateThresholdValue);
flushRateGradient = getGradient(ctx, chartArea, chart, yBaselinePixel, yThresholdPixel);
return flushRateGradient;
},
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
title:{
display:true,
text:'Chart.js Line Chart'
},
tooltips: {
mode: 'index',
intersect: false,
},
hover: {
mode: 'nearest',
intersect: true
}
}
};
const chart = new Chart("canvas", config);
<div id="container" style="height: 300px;">
<canvas id="canvas"></canvas>
</div>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>