2.9
Scope weather effects Scope objects Scope selection changes
This commit is contained in:
@@ -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
@@ -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
@@ -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
File diff suppressed because one or more lines are too long
@@ -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
@@ -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>
|
||||||
|
|||||||
+41
-47
@@ -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 cell = document.createElement('div');
|
const rl = document.createElement('div');
|
||||||
cell.className = 'cell';
|
rl.className = 'row-label';
|
||||||
const lab2 = document.createElement('span');
|
rl.textContent = rowLabel;
|
||||||
lab2.className = 'label';
|
lab.appendChild(rl);
|
||||||
lab2.textContent = label;
|
const grid = document.createElement('div');
|
||||||
const ifr = document.createElement('iframe');
|
grid.className = 'grid';
|
||||||
ifr.src = './scope-cell.html?' + qs;
|
for (const env of envs) {
|
||||||
ifr.loading = 'eager';
|
const cell = document.createElement('div');
|
||||||
cell.append(lab2, ifr);
|
cell.className = 'cell';
|
||||||
lab.append(cell);
|
const lbl = document.createElement('span');
|
||||||
|
lbl.className = 'label';
|
||||||
|
lbl.textContent = envName[env];
|
||||||
|
const ifr = document.createElement('iframe');
|
||||||
|
ifr.src = `./scope-cell?${q}&env=${env}&_=${Date.now()}`;
|
||||||
|
ifr.loading = 'eager';
|
||||||
|
cell.append(lbl, ifr);
|
||||||
|
grid.append(cell);
|
||||||
|
}
|
||||||
|
lab.appendChild(grid);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
Reference in New Issue
Block a user