Reputation: 2626
I am attempting to create a line chart where the color of the line (and the points) is dependant upon the value being plotted. For example if the value is above the following thresholds [0, 115, 125]
then the color would be either ['green', 'yellow', 'red']
respectively.
The requirement is nearly identical to that which is achieved in this example: https://jsfiddle.net/egamegadrive16/zjdwr4fh/
The difference is that I am using react-chart-js-2
and as a result, the draw()
method is not accessible in the same way. Instead, it is suggested to create a plugin to manipulate the chart.
This is the plugin code at present:
import { Chart } from "react-chartjs-2";
class variableLineColorUtils {
selectColor(value, thresholds, colors) {
let color = colors[0];
thresholds.every((limit, index) => {
if (value < limit) return false;
else color = colors[index];
return true;
});
return color;
}
}
const variableLineColor = {
id: "variableLineColor",
afterDraw: (chart, easing) => {
const options = chart.options.variableLineColor;
if (options) {
const utils = new variableLineColorUtils();
const datasets = chart.config.data.datasets;
datasets.forEach((set, i) => {
const points = chart.getDatasetMeta(i).data;
points.forEach((point, index) => {
const color = utils.selectColor(
datasets[i].data[point._index],
options.thresholds,
options.colors
);
point.custom = { borderColor: color, backgroundColor: color };
});
chart.update();
});
}
}
};
Chart.pluginService.register(variableLineColor);
export default variableLineColor;
And these are the options
used for the plugin:
variableLineColor: {
thresholds: [0, 115, 125],
colors: ["green", "yellow", "red"]
}
This approach only amends the color of the points themselves, not the line between the points. The line remains in the chart's default backgroundColor
.
How can I amend the color of the line itself?
Upvotes: 11
Views: 16658
Reputation: 53
In addition to @blacktide answer here is a snippet of how to do it with typescript and some tailwind classes:
Chart.register({
id: "customLineColorPlugin",
beforeRender: chart => {
const ctx = chart.ctx;
const yAxis = chart.scales["y"];
const yPos = yAxis.getPixelForValue(0);
const gradientFill = ctx.createLinearGradient(0, 0, 0, chart.height);
gradientFill.addColorStop(0, "rgb(86,188,77)");
gradientFill.addColorStop(yPos / chart.height, "rgb(86,188,77)");
gradientFill.addColorStop(yPos / chart.height, "rgb(229,66,66)");
gradientFill.addColorStop(1, "rgb(229,66,66)");
const datasets = chart.data.datasets;
datasets.forEach(dataset => {
dataset.borderColor = gradientFill;
});
},
});
const LineChart: React.FC = () => {
const data = {
labels: ["Enero", "Febrero", "Marzo", "Abril", "Mayo"],
datasets: [
{
label: "Datos",
data: [5, -2, 3, -4, 7],
borderColor: "red",
},
],
};
const options: ChartOptions<"line"> = {
scales: {
y: {
beginAtZero: false,
max: 10,
min: -10,
},
},
};
useEffect(() => {
const chart = new Chart(document.getElementById("myChart") as HTMLCanvasElement, {
type: "line",
data: data,
options: options,
});
return () => {
chart.destroy();
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<div>
<canvas id="myChart" width={250} height={80} className="rounded-lg border bg-card p-5"></canvas>
</div>
);
};
export default LineChart;
This is the result:
Upvotes: 0
Reputation: 31
My solution uses ChartJS version 3's new feature , being able to edit the line in segments you can write a function like this.
segment:{
borderColor:function(context){
const yval = context.p1.raw.y
if (yval>=5){
return "green"
} else if(yval<=-5){
return "red"
} else{
return "gray"
}
}
},
This code will return green if the value is above 5 and red if below, and gray if it's in between. I believe this is the most simplistic solution since it doesn't require making your own plugin or re-rendering the chart.
Upvotes: 2
Reputation: 12196
You can use the plugins
array to create a new beforeRender
plugin that will accomplish this.
plugins: [{
beforeRender: (x, options) => {
const c = x.chart;
const dataset = x.data.datasets[0];
const yScale = x.scales['y-axis-0'];
const yPos = yScale.getPixelForValue(0);
const gradientFill = c.ctx.createLinearGradient(0, 0, 0, c.height);
gradientFill.addColorStop(0, 'rgb(86,188,77)');
gradientFill.addColorStop(yPos / c.height, 'rgb(86,188,77)');
gradientFill.addColorStop(yPos / c.height, 'rgb(229,66,66)');
gradientFill.addColorStop(1, 'rgb(229,66,66)');
const model = x.data.datasets[0]._meta[Object.keys(dataset._meta)[0]].dataset._model;
model.borderColor = gradientFill;
},
}];
The result will look something like this:
This will also work for the background color just by changing the model.borderColor
line to model.backgroundColor
. For example:
Upvotes: 6
Reputation: 1915
For that, you will have to manipulate your data something as I did check out the below code. I doubt if you can do it with the plugin.
import React from 'react';
import { Line } from 'react-chartjs-2';
const data = {
labels: [1, 2, 3, 4, 5, 6, 7, 8, 9],
datasets: [
{
label: 'First one',
fill: false,
borderColor: 'gray',
data: [null, null, 2, 0, 3],
},
{
label: 'Second one',
fill: false,
borderColor: 'blue',
data: [2, 4, 2, null, null, null, null, null, null],
},
{
label: 'Third one',
fill: false,
borderColor: 'cyan',
data: [null, null, null, null, 3, 4, 1, null, null],
},
],
}
function App() {
return (
<div>
<Line
data={data}
/>
</div>
);
}
export default App
Upvotes: 2
Reputation: 659
Try to use borderColor instead of backgroundColor and Please refer ChartJs StackOverflow. It may solve your problem.
Upvotes: 0