Scope weather effects
Scope objects
Scope selection changes
This commit is contained in:
fraxle
2026-06-07 01:44:53 +01:00
parent f22833ee63
commit 1459740d96
7 changed files with 411 additions and 72 deletions
+6 -6
View File
@@ -252,7 +252,7 @@
transition: background 0.15s, border-color 0.15s, color 0.15s; transition: background 0.15s, border-color 0.15s, color 0.15s;
} }
.scope-play:hover { .scope-play:hover {
background: rgba(200, 146, 42, 0.18); background: rgba(16, 10, 3, 0.8);
border-color: rgba(200, 146, 42, 0.85); border-color: rgba(200, 146, 42, 0.85);
color: #f5edd6; color: #f5edd6;
} }
@@ -263,19 +263,19 @@
color: #ffd98a; color: #ffd98a;
} }
.scope-play.is-playing:hover { .scope-play.is-playing:hover {
background: #2e1d0a; background: #160d03;
color: #ffe6b0; color: #ffe6b0;
} }
.scope-play-icon { font-size: 9px; line-height: 1; } .scope-play-icon { font-size: 9px; line-height: 1; }
/* While playing, the simulated clock is the main readout — big, bright and /* While playing, the simulated clock is the readout — same size as the idle
monospaced so the digits don't jitter as they tick. */ label, but monospaced + tabular so the digits don't jitter as they tick. */
.scope-play.is-playing .scope-play-label { .scope-play.is-playing .scope-play-label {
font-family: 'JetBrains Mono', ui-monospace, monospace; font-family: 'JetBrains Mono', ui-monospace, monospace;
font-size: 15px; font-size: 11px;
font-weight: 700; font-weight: 700;
letter-spacing: 0.5px; letter-spacing: 0.5px;
font-variant-numeric: tabular-nums; font-variant-numeric: tabular-nums;
min-width: 52px; min-width: 40px;
text-align: center; text-align: center;
color: #ffd98a; color: #ffd98a;
} }
+22 -4
View File
@@ -103,10 +103,28 @@
border-left: none; border-left: none;
} }
/* Each tab's background is an inline per-day weather gradient, so we darken /* The selected-tab background is a different gradient (radial vs linear), and
the whole tab with a filter on hover (!important to beat the inline filter). */ CSS can't tween between gradients — so we paint the active gradient as an
.utci-day-tab:hover { overlay pseudo-element behind the tab content and fade its opacity on hover.
filter: brightness(0.93) !important; z-index:-1 keeps it above the tab's own background but below the labels. */
.utci-day-tab::before {
content: '';
position: absolute;
inset: 0;
background: var(--w-bg-active);
opacity: 0;
transition: opacity 0.2s ease;
pointer-events: none;
z-index: -1;
}
/* On hover, a non-active tab fades in the selected gradient + filter so it
previews the selected shade (!important to beat the inline filter). */
.utci-day-tab:hover:not(.active)::before {
opacity: 1;
}
.utci-day-tab:hover:not(.active) {
filter: saturate(1.1) brightness(1.04) !important;
} }
.utci-day-tab.active { .utci-day-tab.active {
+5 -1
View File
@@ -526,11 +526,15 @@ export function UTCIForecast() {
precip=${scopeRow?.precip ?? 0} precip=${scopeRow?.precip ?? 0}
snow=${scopeRow?.snow ?? 0} snow=${scopeRow?.snow ?? 0}
visKm=${scopeRow?.visKm ?? null} visKm=${scopeRow?.visKm ?? null}
env=${utciEnv}
wind=${scopeRow?.va ?? 0}
gust=${scopeRow?.gust ?? 0}
wd=${scopeRow?.wd ?? null}
/> />
${hourlyRows.length > 0 && html` ${hourlyRows.length > 0 && html`
<button type="button" <button type="button"
class=${`scope-play ${playing ? 'is-playing' : ''}`} class=${`scope-play ${playing ? 'is-playing' : ''}`}
onClick=${() => setPlaying(p => !p)} onClick=${() => { if (playing) { setPlaying(false); setSimMs(null); } else setPlaying(true); }}
title=${playing ? 'Stop time-lapse' : 'Play a 24-hour time-lapse on the scope'}> title=${playing ? 'Stop time-lapse' : 'Play a 24-hour time-lapse on the scope'}>
<span class="scope-play-icon">${playing ? '◼' : '▶'}</span> <span class="scope-play-icon">${playing ? '◼' : '▶'}</span>
<span class="scope-play-label">${playing ? simClock : 'Day cycle'}</span> <span class="scope-play-label">${playing ? simClock : 'Day cycle'}</span>
+333 -13
View File
File diff suppressed because one or more lines are too long
+1
View File
@@ -338,6 +338,7 @@ export function DayTabs({
: `${band.label} · day ${i + 1} of 14`} : `${band.label} · day ${i + 1} of 14`}
style=${{ style=${{
background: isActive ? wBgActive : wBg, background: isActive ? wBgActive : wBg,
'--w-bg-active': wBgActive,
color: wText, color: wText,
borderTop: '1px solid #c9b08a', borderTop: '1px solid #c9b08a',
borderRight: 'none', borderRight: 'none',
+3 -1
View File
@@ -14,7 +14,7 @@
<div id="cell"></div> <div id="cell"></div>
<script type="module"> <script type="module">
import { h, render } from './assets/vendor/preact.js'; import { h, render } from './assets/vendor/preact.js';
import { ScopeReticle } from './assets/js/components.js'; const { ScopeReticle } = await import('./assets/js/components.js?v=' + Date.now());
const q = new URLSearchParams(location.search); const q = new URLSearchParams(location.search);
const n = (k, d) => { const v = q.get(k); return v == null ? d : Number(v); }; const n = (k, d) => { const v = q.get(k); return v == null ? d : Number(v); };
@@ -30,6 +30,8 @@
activeEvent: storm ? { id: 'storm' } : null, activeEvent: storm ? { id: 'storm' } : null,
cc: n('cc', 0), ccLow: n('ccLow', 0), ccMid: n('ccMid', 0), ccHigh: n('ccHigh', 0), cc: n('cc', 0), ccLow: n('ccLow', 0), ccMid: n('ccMid', 0), ccHigh: n('ccHigh', 0),
precip: n('precip', 0), snow: n('snow', 0), visKm: n('vis', 30), precip: n('precip', 0), snow: n('snow', 0), visKm: n('vis', 30),
env: q.get('env') || 'open',
wind: n('wind', 0), gust: n('gust', 0), wd: q.get('wd') != null ? n('wd', 0) : null,
}), document.getElementById('cell')); }), document.getElementById('cell'));
</script> </script>
</body> </body>
+37 -43
View File
@@ -3,64 +3,58 @@
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />
<title>SunScope — Scope Lab (dev)</title> <title>SunScope — Ground Lab (dev)</title>
<style> <style>
body { margin: 0; background: #1a140c; font-family: Manrope, system-ui, sans-serif; color: #f5edd6; } body { margin: 0; background: #1a140c; font-family: Manrope, system-ui, sans-serif; color: #f5edd6; }
h1 { text-align: center; font-weight: 700; font-size: 18px; padding: 16px 0 4px; opacity: 0.8; } h1 { text-align: center; font-weight: 700; font-size: 18px; padding: 14px 0 2px; opacity: 0.8; }
p.note { text-align:center; opacity:0.45; font-size:12px; margin:0 0 12px; } p.note { text-align:center; opacity:0.45; font-size:12px; margin:0 0 10px; }
.grid { display: grid; grid-template-columns: repeat(4, 220px); gap: 18px 18px; justify-content: center; padding: 16px 16px 40px; } .row-label { text-align:center; font-size:12px; font-weight:700; opacity:0.6; margin:14px 0 2px; letter-spacing:1px; text-transform:uppercase; }
.cell { display: flex; flex-direction: column; align-items: center; gap: 4px; } .grid { display: grid; grid-template-columns: repeat(8, 150px); gap: 6px 8px; justify-content: center; padding: 0 12px; }
.label { font-size: 12px; font-weight: 700; letter-spacing: 0.4px; opacity: 0.9; } .cell { display: flex; flex-direction: column; align-items: center; }
iframe { width: 220px; height: 220px; border: 0; background: transparent; } .label { font-size: 11px; font-weight: 700; opacity: 0.85; }
iframe { width: 150px; height: 150px; border: 0; background: transparent; }
</style> </style>
</head> </head>
<body> <body>
<h1>SunScope — Scope Lab</h1> <h1>SunScope — Ground Lab</h1>
<p class="note">dev preview · not shipped · each scope is a real ScopeReticle instance</p> <p class="note">dev preview · per-environment grounds across the cycle</p>
<div id="lab" class="grid"></div> <div id="lab"></div>
<script> <script>
const clear = 'cc=0&ccLow=0&ccMid=0&ccHigh=0&precip=0&snow=0&vis=30'; const envs = ['open','forest','alpine','urban','beach','openwater','desert','river'];
const cloudy = 'cc=75&ccLow=60&ccMid=50&ccHigh=30&precip=0&snow=0&vis=20'; const envName = { open:'Grass', forest:'Forest', alpine:'Alpine', urban:'City', beach:'Beach', openwater:'Open Water', desert:'Desert', river:'River' };
const rain = 'cc=95&ccLow=90&ccMid=70&ccHigh=40&precip=5&snow=0&vis=6';
const snowwx = 'cc=95&ccLow=90&ccMid=70&ccHigh=30&precip=0&snow=1.2&vis=4';
const storm = 'cc=100&ccLow=95&ccMid=80&ccHigh=50&precip=9&snow=0&vis=3&storm=1';
// hour: 7 = sunrise palette, 19 = sunset palette, 12 = noon, 23 = night // [rowLabel, base query]
const scenes = [ const rows = [
['Day · clear', 'elev=45&hour=12&glob=800&' + clear], ['Midday · clear', 'elev=45&hour=12&glob=800&cc=0&vis=30'],
['Day · cloudy', 'elev=45&hour=12&glob=500&' + cloudy], ['Sunset · clear', 'elev=2&hour=19&glob=110&cc=10&vis=30'],
['Day · rain', 'elev=45&hour=12&glob=250&' + rain], ['Night · clear', 'elev=-15&hour=23&glob=0&cc=10&vis=30'],
['Day · snow', 'elev=30&hour=12&glob=200&' + snowwx], ['Midday · overcast','elev=45&hour=12&glob=300&cc=95&ccLow=90&ccMid=70&vis=18'],
['Day · rain', 'elev=40&hour=12&glob=220&cc=98&ccLow=92&ccMid=75&precip=5&vis=6'],
['Sunrise · clear', 'elev=2&hour=7&glob=120&' + clear], ['Day · snow', 'elev=30&hour=12&glob=200&cc=95&ccLow=90&snow=1.2&vis=4'],
['Sunrise · cloudy', 'elev=2&hour=7&glob=90&' + cloudy],
['Sunrise · rain', 'elev=2&hour=7&glob=60&' + rain],
['Sunrise · snow', 'elev=2&hour=7&glob=70&' + snowwx],
['Sunset · clear', 'elev=2&hour=19&glob=120&' + clear],
['Sunset · cloudy', 'elev=2&hour=19&glob=90&' + cloudy],
['Sunset · rain', 'elev=2&hour=19&glob=60&' + rain],
['Sunset · storm', 'elev=1&hour=19&glob=40&' + storm],
['Night · clear', 'elev=-15&hour=23&glob=0&' + clear],
['Night · cloudy', 'elev=-15&hour=23&glob=0&' + cloudy],
['Night · rain', 'elev=-12&hour=23&glob=0&' + rain],
['Night · snow', 'elev=-12&hour=23&glob=0&' + snowwx],
]; ];
const lab = document.getElementById('lab'); const lab = document.getElementById('lab');
for (const [label, qs] of scenes) { for (const [rowLabel, q] of rows) {
const rl = document.createElement('div');
rl.className = 'row-label';
rl.textContent = rowLabel;
lab.appendChild(rl);
const grid = document.createElement('div');
grid.className = 'grid';
for (const env of envs) {
const cell = document.createElement('div'); const cell = document.createElement('div');
cell.className = 'cell'; cell.className = 'cell';
const lab2 = document.createElement('span'); const lbl = document.createElement('span');
lab2.className = 'label'; lbl.className = 'label';
lab2.textContent = label; lbl.textContent = envName[env];
const ifr = document.createElement('iframe'); const ifr = document.createElement('iframe');
ifr.src = './scope-cell.html?' + qs; ifr.src = `./scope-cell?${q}&env=${env}&_=${Date.now()}`;
ifr.loading = 'eager'; ifr.loading = 'eager';
cell.append(lab2, ifr); cell.append(lbl, ifr);
lab.append(cell); grid.append(cell);
}
lab.appendChild(grid);
} }
</script> </script>
</body> </body>