feat: tijdlabels op hele uren in plaats van de ruwe meettijden

De labels toonden de werkelijke meettijdstippen van Netatmo (11:41,
17:41, 23:41), wat onrustig leest. Nu markeringen op hele uren die op de
klok zijn uitgelijnd: elke 6 uur vanaf middernacht, dus 00:00, 06:00,
12:00, 18:00, elk op zijn echte tijdspositie in het venster. "nu" blijft
op de rechterrand; een markering die daar te dicht bij komt valt weg.

axisMarks() is vervangen door hourTicks(), die uur voor uur door het
venster loopt en telkens de lokale klok raadpleegt. Daardoor blijven de
labels ook op de zomertijdwissel hele lokale uren, waar optellen in vaste
stappen er een uur naast zou zitten; daar is een test voor. Een label
vlak op de linkerrand wordt niet gecentreerd, zodat het binnen de
grafiek blijft. Servicewerker naar v9.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-13 11:33:14 +02:00
co-authored by Claude Opus 5
parent e2db301aef
commit 5d61b6ec90
5 changed files with 74 additions and 39 deletions
+13 -9
View File
@@ -2,7 +2,7 @@ import {
f1, fInt, fmm, markerPos, barHeights, wmoIcon, trendLabel,
formatDate, formatTime, dayAbbr, monthName, isOlderThan, msUntil,
co2Level, fPct, fHpa, seriesRange, sparklinePath,
windArrowDeg, fKmh, formatHour, formatFullDate, pinRoom, axisMarks,
windArrowDeg, fKmh, formatHour, formatFullDate, pinRoom, hourTicks,
} from './format.js';
import { icon, windArrow } from './icons.js';
@@ -111,14 +111,18 @@ function renderPressure(p) {
setText('press-min', range ? String(Math.round(range.min)) : '–');
$('press-path').setAttribute('d', sparklinePath(values, 560, 56));
// Tijdlabels op dezelfde posities als de punten waar ze bij horen; de laatste is "nu".
const marks = axisMarks(p.history.length);
$('press-hours').innerHTML = marks.map((m, i) => {
const last = i === marks.length - 1;
const label = last ? 'nu' : formatTime(p.history[m.index].at, state.tz);
const style = i === 0 ? 'left:0' : last ? 'right:0' : `left:${m.percent}%;transform:translateX(-50%)`;
return `<span style="${style}">${label}</span>`;
}).join('');
// Hele uren op hun echte tijdspositie, plus "nu" op de rechterrand.
const first = p.history[0];
const last = p.history[p.history.length - 1];
const ticks = first && last ? hourTicks(first.at, last.at, state.tz) : [];
$('press-hours').innerHTML = ticks
.map((t) => {
// Een markering vlak op de linkerrand niet centreren, anders steekt het label buiten de grafiek.
const style = t.percent < 3 ? 'left:0' : `left:${t.percent.toFixed(1)}%;transform:translateX(-50%)`;
return `<span style="${style}">${formatTime(t.at, state.tz)}</span>`;
})
.concat(last ? ['<span style="right:0">nu</span>'] : [])
.join('');
}
function renderForecast(days) {
+23 -6
View File
@@ -221,11 +221,28 @@ export function pinRoom(rooms, pinnedId) {
}
/**
* Posities voor labels langs een as. Geeft per fractie de bijbehorende index in een reeks van
* `length` punten, plus het percentage waarop dat label hoort te staan. Onder de 2 punten valt er
* niets zinnigs te labelen.
* Markeringen op hele uren binnen een tijdvenster, uitgelijnd op de klok: elke `stepHours` uur
* gerekend vanaf middernacht in de gegeven tijdzone, dus 00:00, 06:00, 12:00, 18:00 bij een stap van 6.
* Per markering het tijdstip en de positie in procenten van het venster.
*
* Er wordt uur voor uur door het venster gelopen en telkens de lokale klok geraadpleegd, zodat de
* zomertijdwissel vanzelf goed gaat: een dag van 23 of 25 uur levert gewoon de juiste hele uren op.
* Markeringen binnen `minGapPercent` van het einde vallen weg; daar staat het label "nu".
*/
export function axisMarks(length, fractions = [0, 0.25, 0.5, 0.75, 1]) {
if (length < 2) return [];
return fractions.map((f) => ({ index: Math.round(f * (length - 1)), percent: f * 100 }));
export function hourTicks(startIso, endIso, tz, stepHours = 6, minGapPercent = 9) {
const t0 = new Date(startIso).getTime();
const t1 = new Date(endIso).getTime();
if (Number.isNaN(t0) || Number.isNaN(t1) || t1 <= t0) return [];
const hourMs = 3600000;
const span = t1 - t0;
const ticks = [];
for (let t = Math.ceil(t0 / hourMs) * hourMs; t <= t1; t += hourMs) {
const at = new Date(t).toISOString();
if (Number(formatHour(at, tz)) % stepHours !== 0) continue;
const percent = ((t - t0) / span) * 100;
if (percent > 100 - minGapPercent) break;
ticks.push({ at, percent });
}
return ticks;
}
+1 -1
View File
@@ -1,6 +1,6 @@
/* Service worker: app-shell cache-first, API network-first met timeout en fallback op de laatste response. */
const VERSION = 'weerscherm-v8';
const VERSION = 'weerscherm-v9';
const SHELL = [
'./',
'./index.html',