If you want to set the min
value explicitly, you have to set the options
:
chart.options.scales.y.min = -100;
chart.update('none');
The call to chart.update
is reading the data and options and sets
the dynamic values like chart.scales.y.min
according to these. Thus,
values like chart.scales.y.min
are only useful for reading the state
of the chart objects (in this case 'y'
axis), but can't be used to
change that state.
If you want to leave it to the standard chart.js algorithm to find the nice
limits to the axis, you may try disabling y
panning (by setting
mode: 'x'
, as it's already set for zoom
). A slight vertical change
while panning will interfere with the way the limits are computed, and
might then be amplified by a subsequent zoom operation.
My testing with x-mode panning seemed to always produce good limits, but if
there are still cases when it's not working, please let me know. Here's
the snippet (I also set options.scales.y.ticks.includeBounds = true
):
const PI = Math.PI;
function daysIntoYear(date) {
return (Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()) - Date.UTC(date.getFullYear(), 0, 0)) / 24 / 60 / 60 / 1000;
}
function secondsToDhms(seconds) {
seconds = Number(seconds);
var d = Math.floor(seconds / (3600 * 24));
var h = Math.floor(seconds % (3600 * 24) / 3600);
var m = Math.floor(seconds % 3600 / 60);
var s = Math.floor(seconds % 60);
var dDisplay = d > 0 ? d + (d == 1 ? " day, " : " days, ") : "";
var hDisplay = h > 0 ? h + (h == 1 ? " hour, " : " hours, ") : "";
var mDisplay = m > 0 ? m + (m == 1 ? " minute, " : " minutes, ") : "";
var sDisplay = s > 0 ? s + (s == 1 ? " second" : " seconds") : "";
return dDisplay + hDisplay + mDisplay + sDisplay;
}
function dataSimulation(from, to, grouping) {
const timeDiff = (new Date(to) - new Date(from)) / 1000;
//console.log("fromDate=" + from + " toDate=" + to + " diff=" + secondsToDhms(timeDiff) + " group=" + secondsToDhms(grouping));
datamapped = [];
dataEvery = 60 * 20; // Data get every 20mn
min = 999;
max = -999;
i = 0;
sum = 0;
for (x = new Date(from).getTime(); x <= new Date(to).getTime(); x = x + 1000 * dataEvery) {
date = new Date(x);
H = date.getHours();
M = date.getMinutes();
month = date.getMonth();
day = date.getDate();
nday = daysIntoYear(date);
value = day + (H / 100) + (M / 10000); // simple simulation
value = 20 + (10 * Math.sin(nday * (PI / 180))) + 3 * Math.sin(H * (360 / 24) * (PI / 180)); // more complex
sum = sum + value;
if (value > max)
max = value;
if (value < min)
min = value;
if ((i * dataEvery) > grouping) {
datamapped.push({
x: new Date(x).toISOString(),
min: min,
max: max,
avg: sum / i
});
i = 0;
sum = 0;
min = 999;
max = -999;
}
i = i + 1;
}
return datamapped;
}
async function fetchData(from, to, group) {
/**
const response = await fetch(`data.php?from=${from}&to=${to}&sensor=OWM&grouptime=86400`);
const data = await response.json();
datamapped = data.map(item => ({
x: item[0],
min: item[1],
max: item[2],
avg: item[3]
}));
**/
datamapped = dataSimulation(from, to, group);
return datamapped;
}
var LASTUPDATETIME;
LASTUPDATETIME = new Date();
var LOCK;
LOCK = false;
async function updateData(chart) {
difftime = (new Date().getTime() - LASTUPDATETIME.getTime());
//console.log("LOCK=" + LOCK + " difftime=" + difftime);
if (LOCK == true) {
if (difftime < 1000)
return;
}
LOCK = true;
//if ( difftime < 500)
//{ // debounce
// console.log("too soon");
// return;
//}
const xmin = chart.scales.x.min;
const xmax = chart.scales.x.max;
const fromDate = new Date(xmin).toISOString();
const toDate = new Date(xmax).toISOString();
const timeDiff = (xmax - xmin) / 1000;
group = 31 * 24 * 3600;
if (timeDiff < 1 * 24 * 3600) { // <1 days, display per every minute
group = 60;
} else if (timeDiff < 4 * 24 * 3600) { // <4 days, display per every hours
group = 3600;
} else if (timeDiff < 33 * 24 * 3600) { // <1.1month, display per 4xday
group = 4 * 3600;
} else if (timeDiff < 4 * 31 * 24 * 3600) { // <4month, display per day
group = 24 * 3600;
}
/**
response = await fetch(`data.php?fmt=json&from=${fromDate}&to=${toDate}&sensor=OWM&grouptime=${group}`);
data = await response.json();
datamapped = data.map(item => ({
x: item[0],
min: item[1],
max: item[2],
avg: item[3]
}));
**/
datamapped = dataSimulation(fromDate, toDate, group);
chart.data.datasets[0].data = datamapped;
chart.data.datasets[1].data = datamapped;
chart.data.datasets[2].data = datamapped;
const yDataMax = Math.max(...datamapped.map(({max}) => max)),
yDataMin = Math.min(...datamapped.map(({min}) => min));
//chart.scales.options.y.min = -100; // as a test, the Y axis should be at -100, but not working
chart.update('none'); // with 'none' it's synchronous, so we can get the results immediately after:
console.clear(); // to preserve snippet space
console.log(`after .update, data:, [${yDataMin}, ${yDataMax}], scale: [${chart.scales.y.min}, ${chart.scales.y.max}]`);
LASTUPDATETIME = new Date();
LOCK = false;
}
async function createChart(from, to, group) {
const data = await fetchData(from, to, group);
const ctx = document.getElementById('temperatureChart').getContext('2d');
const temperatureChart = new Chart(ctx, {
type: 'line',
data: {
datasets: [{
data: data, // The three values are on the same data ? strange
parsing: {
yAxisKey: 'min'
},
fill: '+1',
borderWidth: 0
},
{
data: data, // this is strange to have the same data than the previous
parsing: {
yAxisKey: 'max'
},
borderWidth: 0
},
{
data: data,
parsing: {
yAxisKey: 'avg'
},
borderColor: 'green',
fill: false,
borderWidth: 1
}
]
},
options: {
responsive: true,
animation: false,
elements: {
point: {
radius: 1
}
},
scales: {
x: {
type: 'time',
time: {
tooltipFormat: 'yyyy-MM-dd HH:mm'
},
title: {
display: true,
text: 'Date/Time'
},
},
y: {
beginAtZero: false,
ticks: {
includeBounds: false
},
title: {
display: true,
text: 'Temperature (°C)'
},
}
},
plugins: {
legend: {
display: true,
position: 'top'
},
zoom: {
pan: {
// pan options and/or events
enabled: true,
mode: "x",
onPanComplete: function({
chart
}) {
updateData(chart);
}
},
zoom: {
wheel: {
enabled: true,
},
pinch: {
enabled: true
},
mode: 'x',
onZoomComplete: function({
chart
}) {
updateData(chart);
}
}
}
}
}
});
}
// Example usage
createChart('2024-01-01', '2024-12-31', 31 * 24 * 3600);
<div style="width: 100%; margin: auto;">
<canvas id="temperatureChart"></canvas>
</div>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/hammer.js/2.0.8/hammer.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-date-fns"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-zoom"></script>