feat: assen bij de luchtdrukgrafiek
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 <noreply@anthropic.com>
This commit is contained in:
@@ -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
|
||||
|
||||
+13
-2
@@ -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 `<span style="${style}">${label}</span>`;
|
||||
}).join('');
|
||||
}
|
||||
|
||||
function renderForecast(days) {
|
||||
|
||||
@@ -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 }));
|
||||
}
|
||||
|
||||
+13
-5
@@ -36,11 +36,19 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="a-pressure">
|
||||
<p class="a-plabel">Luchtdruk <b class="num" id="press">–</b><span class="a-trend" id="press-trend"></span><span class="a-prange num" id="press-range"></span></p>
|
||||
<svg class="a-spark" viewBox="0 0 560 56" preserveAspectRatio="none" aria-hidden="true">
|
||||
<line class="a-spark-base" x1="0" y1="55.5" x2="560" y2="55.5"/>
|
||||
<path class="a-spark-line" id="press-path" d=""/>
|
||||
</svg>
|
||||
<p class="a-plabel">Luchtdruk <b class="num" id="press">–</b><span class="a-trend" id="press-trend"></span></p>
|
||||
<div class="a-pgrid">
|
||||
<div class="a-py num"><span id="press-max">–</span><span id="press-min">–</span></div>
|
||||
<svg class="a-spark" viewBox="0 0 560 56" preserveAspectRatio="none" aria-hidden="true">
|
||||
<line class="a-spark-grid" x1="140" y1="0" x2="140" y2="56"/>
|
||||
<line class="a-spark-grid" x1="280" y1="0" x2="280" y2="56"/>
|
||||
<line class="a-spark-grid" x1="420" y1="0" x2="420" y2="56"/>
|
||||
<line class="a-spark-base" x1="0" y1="55.5" x2="560" y2="55.5"/>
|
||||
<path class="a-spark-line" id="press-path" d=""/>
|
||||
</svg>
|
||||
<div></div>
|
||||
<div class="a-px num" id="press-hours"></div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
+9
-5
@@ -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 */
|
||||
|
||||
+1
-1
@@ -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',
|
||||
|
||||
+26
-1
@@ -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), []);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user