I am trying to make the points different colors: by default it should be background: linear-gradient(243.46deg, #FFC700 -1%, #F52525 131.66%); If the next value is smaller than the previous, it should show be background: linear-gradient(55.98deg, #E83C3C 11.73%, #9D3C3C 72.51%);
I've found some tutorials about linear-gradient on canvas but still all my points are black
var trendPriceArray = [
0,
109119,
103610,
112561,
0,
0,
0,
101852,
0,
99736,
134382,
110018
];
var trendPercentArray = [-5,
8.6, -9.5, -2.1,
34.7, -18.1
];
var monthLabels = [
'January',
'February',
'March',
'April',
'May',
'June',
'July',
'August',
'September',
'October',
'November',
'December'
];
// Filter out data points with trendPriceArray values of 0
var filteredData = [];
for (var i = 0; i < trendPriceArray.length; i++) {
if (trendPriceArray[i] !== 0) {
filteredData.push({
x: monthLabels[i], // X coordinate as month label
y: trendPriceArray[i], // Y coordinate
label: trendPercentArray[i] + '%', // Display label above the point
});
}
}
// Initialize Chart.js
var ctx = document.getElementById('myChart').getContext('2d');
var myChart = new Chart(ctx, {
type: 'line',
data: {
labels: monthLabels, // Use the month labels
datasets: [{
label: 'Price Trend',
data: filteredData, // Use filtered data
fill: false,
borderColor: '#4e4e4e', // Set the line graph color to #4e4e4e
backgroundColor: 'rgba(75, 192, 192, 0.2)',
borderWidth: 1,
pointBackgroundColor: [], // Empty array to be filled with point background colors
pointRadius: 13, // Set the point radius to 13 pixels
pointHoverRadius: 13, // Set the point hover radius to 13 pixels
}]
},
options: {
scales: {
x: {
type: 'category', // Use category scale with the month labels
title: {
display: true,
text: 'Month',
font: {
family: 'Montserrat', // Set the font family to Montserrat
},
color: '#4e4e4e', // Set the axis label text color to #4e4e4e
},
ticks: {
font: {
family: 'Montserrat', // Set the font family to Montserrat
},
color: '#4e4e4e', // Set the tick text color to #4e4e4e
},
},
y: {
display: true,
title: {
display: true,
text: 'Price',
marginBottom: '10px',
font: {
family: 'Montserrat', // Set the font family to Montserrat
},
color: '#4e4e4e', // Set the axis label text color to #4e4e4e
},
ticks: {
font: {
family: 'Montserrat', // Set the font family to Montserrat
},
color: '#4e4e4e', // Set the tick text color to #4e4e4e
},
}
},
plugins: {
tooltip: {
enabled: true,
mode: 'index',
intersect: false,
},
legend: {
labels: {
font: {
family: 'Montserrat', // Set the font family to Montserrat
},
color: '#4e4e4e', // Set the legend text color to #4e4e4e
},
},
}
}
});
function createDefaultGradient(ctx) {
const gradient = ctx.createLinearGradient(0, 0, 0, 400);
gradient.addColorStop(0, 'rgba(75, 192, 192, 0.2)');
gradient.addColorStop(1, 'rgba(255, 255, 255, 0.2)');
return gradient;
}
function createRedGradient(ctx) {
const gradient = ctx.createLinearGradient(0, 0, 0, 400);
gradient.addColorStop(0, 'rgba(255, 0, 0, 0.2)');
gradient.addColorStop(1, 'rgba(255, 255, 255, 0.2)');
return gradient;
}
function createGradientPointStyle(gradient) {
return function(context) {
const chart = context.chart;
const {
ctx,
borderWidth
} = chart;
const {
x,
y
} = context.p0;
const pointRadius = 6; // You can adjust this value as needed
ctx.save();
ctx.beginPath();
ctx.arc(x, y, pointRadius, 0, Math.PI * 2);
ctx.fillStyle = gradient;
ctx.closePath();
ctx.fill();
ctx.restore();
};
}
// Initialize an empty pointStyle array
myChart.data.datasets[0].pointStyle = [];
for (var i = 0; i < filteredData.length; i++) {
var gradient = createDefaultGradient(ctx); // Default gradient
if (i < filteredData.length - 1 && filteredData[i + 1].y < filteredData[i].y) {
gradient = createRedGradient(ctx); // Red gradient for smaller values
}
myChart.data.datasets[0].pointStyle.push(createGradientPointStyle(gradient));
}
myChart.update(); // Update the chart
<!DOCTYPE html>
<html>
<head>
<!-- Include Chart.js library -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.7.0/chart.min.js"></script>
<!-- Include Montserrat font from Google Fonts -->
<link href="https://fonts.googleapis.com/css2?family=Montserrat&display=swap" rel="stylesheet">
</head>
<body>
<!-- Create a container for the chart -->
<div>
<canvas id="myChart" width="400" height="200"></canvas>
</div>
</body>
</html>
There are theoretically two different avenues to draw custom styled data points, and you combined them the wrong way:
afterDraw
function; otherwise, anything you draw is
covered by the default drawing of the chartpointStyle
, to which you may assign
a newly created (small) canvas or image on which you
draw the (reusable) point symbol.Since the second, standard method is good enough for your requirements, we can forget about the first, which should only be employed in exceptional cases, not covered by the other one.
Also, you should consider the moments/order
when each part of your code gets executed. In
particular, since you are using a
scriptable
pointStyle
(i.e., a function that is called when the point style is needed and returns the canvas), you don't need to set the pointStyle
in a second stage, after the chart was first rendered,
and neither the chart.update
call.
The very point of scriptable options is to enable you to use
just-computed dynamic values (like your filtered
y
values) to avoid redrawing the whole chart.
The fact is that the point values are available before the points
are drawn, so through the arguments provided to the pointStyle
function you'll find the required y
values.
Here's a corrected version of your code, with minimal change:
function createDefaultGradient(ctx, max) {
const gradient = ctx.createLinearGradient(0, 0, 0, max);
gradient.addColorStop(0, 'rgba(75, 192, 192, 0.2)');
gradient.addColorStop(1, 'rgba(255, 255, 255, 0.2)');
return gradient;
}
function createRedGradient(ctx, max) {
const gradient = ctx.createLinearGradient(0, 0, 0, max);
gradient.addColorStop(0, 'rgba(255, 0, 0, 0.2)');
gradient.addColorStop(1, 'rgba(255, 255, 255, 0.2)');
return gradient;
}
function createGradientPointStyle(gradientCreator, pointRadius) {
const canvas = document.createElement('canvas');
canvas.width = 2*pointRadius+2;
canvas.height = 2*pointRadius+2;
const ctx = canvas.getContext("2d");
ctx.beginPath();
ctx.arc(pointRadius+1, pointRadius+1, pointRadius, 0, Math.PI * 2);
ctx.fillStyle = gradientCreator(ctx, 2*pointRadius+2);
ctx.closePath();
ctx.fill();
return canvas;
}
function pointStyle(context, opt){
var i = context.dataIndex,
data = context.dataset.data;
var gradientCreator = createDefaultGradient;
if (i < data.length - 1 && data[i + 1].y < data[i].y) {
gradientCreator = createRedGradient; // Red gradient for smaller values
}
var radius = context.active ? opt.hoverRadius : opt.radius;
return createGradientPointStyle(gradientCreator, radius)
}
var trendPriceArray = [
0,
109119,
103610,
112561,
0,
0,
0,
101852,
0,
99736,
134382,
110018
];
var trendPercentArray = [-5,
8.6, -9.5, -2.1,
34.7, -18.1
];
var monthLabels = [
'January',
'February',
'March',
'April',
'May',
'June',
'July',
'August',
'September',
'October',
'November',
'December'
];
// Filter out data points with trendPriceArray values of 0
var filteredData = [];
for (var i = 0; i < trendPriceArray.length; i++) {
if (trendPriceArray[i] !== 0) {
filteredData.push({
x: monthLabels[i], // X coordinate as month label
y: trendPriceArray[i], // Y coordinate
label: trendPercentArray[i] + '%', // Display label above the point
});
}
}
// Initialize Chart.js
var ctx = document.getElementById('myChart').getContext('2d');
var myChart = new Chart(ctx, {
type: 'line',
data: {
labels: monthLabels, // Use the month labels
datasets: [{
label: 'Price Trend',
data: filteredData, // Use filtered data
// fill: false,
borderColor: '#4e4e4e', // Set the line graph color to #4e4e4e
//backgroundColor: 'rgba(75, 192, 192, 0.2)',
borderWidth: 1,
// pointBackgroundColor: [], // Empty array to be filled with point background colors
pointRadius: 13, // Set the point radius to 13 pixels
pointHoverRadius: 23, // Set the point hover radius to 13 pixels
pointStyle
}]
},
options: {
scales: {
x: {
type: 'category', // Use category scale with the month labels
title: {
display: true,
text: 'Month',
font: {
family: 'Montserrat', // Set the font family to Montserrat
},
color: '#4e4e4e', // Set the axis label text color to #4e4e4e
},
ticks: {
font: {
family: 'Montserrat', // Set the font family to Montserrat
},
color: '#4e4e4e', // Set the tick text color to #4e4e4e
},
},
y: {
display: true,
title: {
display: true,
text: 'Price',
marginBottom: '10px',
font: {
family: 'Montserrat', // Set the font family to Montserrat
},
color: '#4e4e4e', // Set the axis label text color to #4e4e4e
},
ticks: {
font: {
family: 'Montserrat', // Set the font family to Montserrat
},
color: '#4e4e4e', // Set the tick text color to #4e4e4e
},
}
},
plugins: {
tooltip: {
enabled: true,
mode: 'index',
intersect: false,
},
legend: {
labels: {
font: {
family: 'Montserrat', // Set the font family to Montserrat
},
color: '#4e4e4e', // Set the legend text color to #4e4e4e
},
},
}
}
});
<div style="min-height: 60vh">
<canvas id="myChart"></canvas>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.7.0/chart.min.js"></script>