From c0715b7cc97a8bff909e897fbbcf87376eff3991 Mon Sep 17 00:00:00 2001 From: Theo Weijmer Date: Sun, 13 Sep 2026 11:30:11 +0200 Subject: [PATCH] feat: assen bij de luchtdrukgrafiek MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit De lijn had geen schaal, dus je kon niet zien hoeveel hPa de uitslag was of wanneer een dip viel. Nu links de hoogste en laagste waarde van het venster in hele hPa, uitgelijnd op boven- en onderkant van de lijn, en eronder vijf tijdlabels (0/25/50/75/100% van het venster) met "nu" rechts, ondersteund door drie lichte verticale hulplijnen. Posities komen uit de pure functie axisMarks(), met tests. De losse tekst "1009 – 1015 hPa in 24 u" in de kopregel vervalt: de y-as zegt hetzelfde en de x-as maakt het venster duidelijk. De extra labelrij past door de marge onder de grote temperatuur 8px te krappen; er blijft 5px speling boven en onder de hero. Servicewerker naar v8. Co-Authored-By: Claude Opus 5 --- docs/weerscherm-spec.md | 5 +++++ public/app.js | 15 +++++++++++++-- public/format.js | 10 ++++++++++ public/index.html | 18 +++++++++++++----- public/styles.css | 14 +++++++++----- public/sw.js | 2 +- tests/js/format.test.js | 27 ++++++++++++++++++++++++++- 7 files changed, 77 insertions(+), 14 deletions(-) diff --git a/docs/weerscherm-spec.md b/docs/weerscherm-spec.md index 6596e45..d781375 100644 --- a/docs/weerscherm-spec.md +++ b/docs/weerscherm-spec.md @@ -251,6 +251,11 @@ De iconen `sun`, `partly`, `rain` en `showers` staan in de mockup. Maak `cloud`, - De temperatuur staat groot, met 1 decimaal ("16,4°"). Het label heeft een trend-aanduiding: `up` → "↗ stijgend", `down` → "↘ dalend", `stable` → "→ stabiel". - Daaronder de gradiëntbalk van `--cold` naar `--warm`. De marker staat op `(temp − min) / (max − min)`, begrensd op 0–100%. Bij `max == min` staat hij op 50%. - Onder de balk: "9,2° laagst om 06:10" en "19,8° hoogst om 13:05". + - Daaronder de luchtdruk: "Luchtdruk 1014 hPa ↗ stijgend" met een grafiek van de afgelopen 24 uur. + _Assen toegevoegd 2026-09-13: links de hoogste en laagste waarde van het venster in hele hPa, onder de + lijn vijf tijdlabels op 0/25/50/75/100% met "nu" rechts, plus drie lichte verticale hulplijnen. De losse + tekst "1009 – 1015 hPa in 24 u" in de kopregel is daarmee vervallen. Zie `axisMarks()` in + `public/format.js`._ - **Binnen (rechts):** 3 rijen met naam, temperatuur (1 decimaal) en "↓ min° ↑ max°". Een onbereikbare module toont "–" in `--muted`. _Toegevoegd 2026-09-13: een module is tikbaar. Een tik zet die module bovenaan de lijst (de overige twee behouden hun onderlinge volgorde) en vergroot alleen het temperatuurgetal van 50 naar 92px. Nogmaals tikken diff --git a/public/app.js b/public/app.js index c075186..41c6c84 100644 --- a/public/app.js +++ b/public/app.js @@ -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, + windArrowDeg, fKmh, formatHour, formatFullDate, pinRoom, axisMarks, } from './format.js'; import { icon, windArrow } from './icons.js'; @@ -104,10 +104,21 @@ function renderRain(rain, now) { function renderPressure(p) { setText('press', fHpa(p.current)); setText('press-trend', trendLabel(p.trend)); + const values = p.history.map((h) => h.hpa); const range = seriesRange(values); - setText('press-range', range ? `${Math.round(range.min)} – ${Math.round(range.max)} hPa in 24 u` : ''); + setText('press-max', range ? String(Math.round(range.max)) : '–'); + 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 `${label}`; + }).join(''); } function renderForecast(days) { diff --git a/public/format.js b/public/format.js index d2c1e11..be1673a 100644 --- a/public/format.js +++ b/public/format.js @@ -219,3 +219,13 @@ export function pinRoom(rooms, pinnedId) { copy.unshift(pinned); return copy; } + +/** + * 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. + */ +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 })); +} diff --git a/public/index.html b/public/index.html index abe5712..f6e98b7 100644 --- a/public/index.html +++ b/public/index.html @@ -36,11 +36,19 @@
-

Luchtdruk –

- +

Luchtdruk –

+
+
––
+ +
+
+
diff --git a/public/styles.css b/public/styles.css index ba5b6d0..bd16263 100644 --- a/public/styles.css +++ b/public/styles.css @@ -80,7 +80,7 @@ html, body { .a-hero { align-self: center; } .a-label { font-size: 22px; color: var(--muted); font-weight: 500; display: flex; gap: 14px; align-items: baseline; } .a-trend { font-size: 17px; font-weight: 400; } -.a-temp { font-size: 200px; font-weight: 200; letter-spacing: -.045em; line-height: .95; margin: 4px 0 18px -8px; } +.a-temp { font-size: 200px; font-weight: 200; letter-spacing: -.045em; line-height: .95; margin: 4px 0 10px -8px; } .a-hum { font-size: 17px; font-weight: 400; } .a-hum:not(:empty)::before { content: "·"; margin-right: 14px; } .a-range { width: 560px; } @@ -89,13 +89,17 @@ html, body { .a-ends { display: flex; justify-content: space-between; margin-top: 14px; font-size: 17px; color: var(--muted); } .a-ends b { font-size: 24px; font-weight: 600; margin-right: 6px; } -/* Luchtdruk, 24 uur */ -.a-pressure { width: 560px; margin-top: 18px; } +/* Luchtdruk, 24 uur. Grafiek met een y-as (hoogste/laagste waarde) en tijdlabels op de x-as. */ +.a-pressure { width: 560px; margin-top: 14px; } .a-plabel { font-size: 17px; color: var(--muted); display: flex; gap: 12px; align-items: baseline; margin-bottom: 6px; } .a-plabel b { font-size: 20px; font-weight: 600; color: var(--ink); } -.a-prange { margin-left: auto; font-size: 14px; } -.a-spark { display: block; width: 560px; height: 56px; overflow: visible; } +.a-pgrid { display: grid; grid-template-columns: 44px 1fr; column-gap: 8px; row-gap: 3px; } +.a-py { display: flex; flex-direction: column; justify-content: space-between; text-align: right; font-size: 13px; color: var(--muted); line-height: 1; padding: 1px 0; } +.a-px { position: relative; height: 17px; font-size: 13px; color: var(--muted); } +.a-px span { position: absolute; top: 0; white-space: nowrap; } +.a-spark { display: block; width: 100%; height: 56px; overflow: visible; } .a-spark-base { stroke: var(--line); stroke-width: 1; } +.a-spark-grid { stroke: var(--line); stroke-width: 1; vector-effect: non-scaling-stroke; opacity: .6; } .a-spark-line { fill: none; stroke: var(--rain); stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round; vector-effect: non-scaling-stroke; } /* Binnen */ diff --git a/public/sw.js b/public/sw.js index d9bf80a..9c8b662 100644 --- a/public/sw.js +++ b/public/sw.js @@ -1,6 +1,6 @@ /* Service worker: app-shell cache-first, API network-first met timeout en fallback op de laatste response. */ -const VERSION = 'weerscherm-v7'; +const VERSION = 'weerscherm-v8'; const SHELL = [ './', './index.html', diff --git a/tests/js/format.test.js b/tests/js/format.test.js index 28d8cda..c2b6f7a 100644 --- a/tests/js/format.test.js +++ b/tests/js/format.test.js @@ -121,7 +121,7 @@ test('msUntil computes the wait until the next local time', () => { import { co2Level, fPct, fHpa, seriesRange, sparklinePath, sparklinePoints, - windArrowDeg, fKmh, formatHour, formatFullDate, pinRoom, + windArrowDeg, fKmh, formatHour, formatFullDate, pinRoom, axisMarks, } from '../../public/format.js'; test('co2Level uses the Netatmo thresholds', () => { @@ -208,3 +208,28 @@ test('pinRoom does not mutate the input array', () => { pinRoom(rooms, 'c'); assert.deepEqual(rooms, original); }); + +test('axisMarks maps fractions to indices and percentages', () => { + // 49 punten = 24 uur per half uur + assert.deepEqual(axisMarks(49), [ + { index: 0, percent: 0 }, + { index: 12, percent: 25 }, + { index: 24, percent: 50 }, + { index: 36, percent: 75 }, + { index: 48, percent: 100 }, + ]); +}); + +test('axisMarks honours custom fractions and rounds to the nearest point', () => { + assert.deepEqual(axisMarks(10, [0, 0.5, 1]), [ + { index: 0, percent: 0 }, + { index: 5, percent: 50 }, + { index: 9, percent: 100 }, + ]); + assert.deepEqual(axisMarks(4, [0.33]), [{ index: 1, percent: 33 }]); +}); + +test('axisMarks gives nothing to label below two points', () => { + assert.deepEqual(axisMarks(1), []); + assert.deepEqual(axisMarks(0), []); +});