Polish: autoplay, Mon-YYYY IC labels, scientific var names, larger fonts
Browse files- script.js +12 -5
- styles.css +7 -7
script.js
CHANGED
|
@@ -26,7 +26,7 @@ const app = {
|
|
| 26 |
M:0, T:0, nLon:0, nLat:0, W:0,
|
| 27 |
vmean:0, vstd:1,
|
| 28 |
rightMode:'truth', n:8, t:0, dragging:false,
|
| 29 |
-
currentIC:null, currentVar:null, playing:false, timer:null,
|
| 30 |
};
|
| 31 |
|
| 32 |
// IEEE-754 half → float
|
|
@@ -37,7 +37,8 @@ function h2f(h) {
|
|
| 37 |
return (s ? -1 : 1) * Math.pow(2, e-15) * (1 + f/1024);
|
| 38 |
}
|
| 39 |
|
| 40 |
-
|
|
|
|
| 41 |
|
| 42 |
async function init() {
|
| 43 |
const [index, coast] = await Promise.all([
|
|
@@ -50,7 +51,8 @@ async function init() {
|
|
| 50 |
app.index.ics.map(ic => ({ label: fmtIC(ic.init_time), val: ic.hash })),
|
| 51 |
onICPick);
|
| 52 |
buildTokens('var-tokens',
|
| 53 |
-
Object.
|
|
|
|
| 54 |
onVarPick);
|
| 55 |
wireControls();
|
| 56 |
buildGeometry();
|
|
@@ -154,6 +156,7 @@ async function loadBin(icHash, varLabel) {
|
|
| 154 |
rebuildFrames();
|
| 155 |
drawFigure();
|
| 156 |
setStatus('', 100, true);
|
|
|
|
| 157 |
}
|
| 158 |
|
| 159 |
function setStatus(text, pct, hide) {
|
|
@@ -210,7 +213,7 @@ function sceneCfg(domainX) {
|
|
| 210 |
function titleAnn(x, text) {
|
| 211 |
return { x, y:1.0, xref:'paper', yref:'paper', text, showarrow:false,
|
| 212 |
xanchor:'center', yanchor:'top',
|
| 213 |
-
font:{ family:'IBM Plex Mono, monospace', size:
|
| 214 |
}
|
| 215 |
|
| 216 |
function drawFigure() {
|
|
@@ -319,9 +322,13 @@ function wireControls() {
|
|
| 319 |
});
|
| 320 |
}
|
| 321 |
|
| 322 |
-
function togglePlay() {
|
|
|
|
|
|
|
|
|
|
| 323 |
function startPlay() {
|
| 324 |
if (!app.meanFrames) return;
|
|
|
|
| 325 |
app.playing = true;
|
| 326 |
document.getElementById('play').textContent = '❚❚ pause';
|
| 327 |
app.timer = setInterval(() => setT((app.t + 1) % app.T), 320);
|
|
|
|
| 26 |
M:0, T:0, nLon:0, nLat:0, W:0,
|
| 27 |
vmean:0, vstd:1,
|
| 28 |
rightMode:'truth', n:8, t:0, dragging:false,
|
| 29 |
+
currentIC:null, currentVar:null, playing:false, timer:null, autoplay:true,
|
| 30 |
};
|
| 31 |
|
| 32 |
// IEEE-754 half → float
|
|
|
|
| 37 |
return (s ? -1 : 1) * Math.pow(2, e-15) * (1 + f/1024);
|
| 38 |
}
|
| 39 |
|
| 40 |
+
const _MON = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
|
| 41 |
+
function fmtIC(s){ const [y,m] = s.split('-'); return `${_MON[+m-1]} ${y}`; } // Feb 2016
|
| 42 |
|
| 43 |
async function init() {
|
| 44 |
const [index, coast] = await Promise.all([
|
|
|
|
| 51 |
app.index.ics.map(ic => ({ label: fmtIC(ic.init_time), val: ic.hash })),
|
| 52 |
onICPick);
|
| 53 |
buildTokens('var-tokens',
|
| 54 |
+
Object.entries(app.index.variables).map(
|
| 55 |
+
([l, v]) => ({ label: v.long_name || l, val: l })),
|
| 56 |
onVarPick);
|
| 57 |
wireControls();
|
| 58 |
buildGeometry();
|
|
|
|
| 156 |
rebuildFrames();
|
| 157 |
drawFigure();
|
| 158 |
setStatus('', 100, true);
|
| 159 |
+
if (app.autoplay) startPlay(); // auto-animate on load (and after switches)
|
| 160 |
}
|
| 161 |
|
| 162 |
function setStatus(text, pct, hide) {
|
|
|
|
| 213 |
function titleAnn(x, text) {
|
| 214 |
return { x, y:1.0, xref:'paper', yref:'paper', text, showarrow:false,
|
| 215 |
xanchor:'center', yanchor:'top',
|
| 216 |
+
font:{ family:'IBM Plex Mono, monospace', size:16, color:'#aab0bb' } };
|
| 217 |
}
|
| 218 |
|
| 219 |
function drawFigure() {
|
|
|
|
| 322 |
});
|
| 323 |
}
|
| 324 |
|
| 325 |
+
function togglePlay() {
|
| 326 |
+
if (app.playing) { app.autoplay = false; stopPlay(); }
|
| 327 |
+
else { app.autoplay = true; startPlay(); }
|
| 328 |
+
}
|
| 329 |
function startPlay() {
|
| 330 |
if (!app.meanFrames) return;
|
| 331 |
+
clearInterval(app.timer);
|
| 332 |
app.playing = true;
|
| 333 |
document.getElementById('play').textContent = '❚❚ pause';
|
| 334 |
app.timer = setInterval(() => setT((app.t + 1) % app.T), 320);
|
styles.css
CHANGED
|
@@ -25,15 +25,15 @@ body {
|
|
| 25 |
/* Header — one line, no hero */
|
| 26 |
.head { display:flex; justify-content:space-between; align-items:baseline;
|
| 27 |
gap:1.5rem; flex-wrap:wrap; }
|
| 28 |
-
.head h1 { font-size:1.
|
| 29 |
.head h1 .muted { color:var(--muted); font-weight:400; }
|
| 30 |
.head-links { display:flex; gap:1.25rem; flex-shrink:0; }
|
| 31 |
-
.head-links a { color:var(--muted); text-decoration:none; font-size:.
|
| 32 |
display:inline-flex; align-items:center; gap:.35rem;
|
| 33 |
border-bottom:1px solid transparent; transition:.15s; }
|
| 34 |
.head-links a:hover { color:var(--text); border-bottom-color:var(--dim); }
|
| 35 |
.head-links svg { width:14px; height:14px; }
|
| 36 |
-
.caption { margin-top:.
|
| 37 |
color:var(--muted); letter-spacing:.02em; }
|
| 38 |
|
| 39 |
/* Plot — globes float on the dark canvas */
|
|
@@ -55,13 +55,13 @@ body {
|
|
| 55 |
/* Controls */
|
| 56 |
.controls { display:flex; flex-direction:column; gap:.7rem; margin-top:.6rem; }
|
| 57 |
.row { display:flex; align-items:baseline; gap:.5rem; flex-wrap:wrap; }
|
| 58 |
-
.row > .label { font-family:var(--mono); font-size:.
|
| 59 |
text-transform:uppercase; letter-spacing:.08em;
|
| 60 |
min-width:9.5rem; flex-shrink:0; }
|
| 61 |
|
| 62 |
/* Inline text-token selector */
|
| 63 |
.tokens { display:flex; flex-wrap:wrap; align-items:baseline; }
|
| 64 |
-
.token { font-size:.
|
| 65 |
transition:color .15s; white-space:nowrap; }
|
| 66 |
.token:hover { color:var(--text); }
|
| 67 |
.token.active { color:var(--text); text-decoration:underline;
|
|
@@ -79,7 +79,7 @@ input[type=range]::-webkit-slider-thumb { -webkit-appearance:none; appearance:no
|
|
| 79 |
background:var(--accent); }
|
| 80 |
input[type=range]::-moz-range-thumb { width:12px; height:12px; border:0;
|
| 81 |
border-radius:50%; background:var(--accent); }
|
| 82 |
-
.val { font-family:var(--mono); font-size:.
|
| 83 |
min-width:2ch; }
|
| 84 |
|
| 85 |
/* Timeline + play */
|
|
@@ -88,7 +88,7 @@ input[type=range]::-moz-range-thumb { width:12px; height:12px; border:0;
|
|
| 88 |
background:none; border:0; cursor:pointer; padding:0;
|
| 89 |
letter-spacing:.04em; }
|
| 90 |
.play:hover { color:var(--text); }
|
| 91 |
-
.lead { font-family:var(--mono); font-size:.
|
| 92 |
min-width:9ch; }
|
| 93 |
|
| 94 |
@media (max-width:720px) {
|
|
|
|
| 25 |
/* Header — one line, no hero */
|
| 26 |
.head { display:flex; justify-content:space-between; align-items:baseline;
|
| 27 |
gap:1.5rem; flex-wrap:wrap; }
|
| 28 |
+
.head h1 { font-size:1.6rem; font-weight:600; letter-spacing:-0.02em; }
|
| 29 |
.head h1 .muted { color:var(--muted); font-weight:400; }
|
| 30 |
.head-links { display:flex; gap:1.25rem; flex-shrink:0; }
|
| 31 |
+
.head-links a { color:var(--muted); text-decoration:none; font-size:.95rem;
|
| 32 |
display:inline-flex; align-items:center; gap:.35rem;
|
| 33 |
border-bottom:1px solid transparent; transition:.15s; }
|
| 34 |
.head-links a:hover { color:var(--text); border-bottom-color:var(--dim); }
|
| 35 |
.head-links svg { width:14px; height:14px; }
|
| 36 |
+
.caption { margin-top:.5rem; font-family:var(--mono); font-size:.95rem;
|
| 37 |
color:var(--muted); letter-spacing:.02em; }
|
| 38 |
|
| 39 |
/* Plot — globes float on the dark canvas */
|
|
|
|
| 55 |
/* Controls */
|
| 56 |
.controls { display:flex; flex-direction:column; gap:.7rem; margin-top:.6rem; }
|
| 57 |
.row { display:flex; align-items:baseline; gap:.5rem; flex-wrap:wrap; }
|
| 58 |
+
.row > .label { font-family:var(--mono); font-size:.85rem; color:var(--muted);
|
| 59 |
text-transform:uppercase; letter-spacing:.08em;
|
| 60 |
min-width:9.5rem; flex-shrink:0; }
|
| 61 |
|
| 62 |
/* Inline text-token selector */
|
| 63 |
.tokens { display:flex; flex-wrap:wrap; align-items:baseline; }
|
| 64 |
+
.token { font-size:1.05rem; color:var(--muted); cursor:pointer;
|
| 65 |
transition:color .15s; white-space:nowrap; }
|
| 66 |
.token:hover { color:var(--text); }
|
| 67 |
.token.active { color:var(--text); text-decoration:underline;
|
|
|
|
| 79 |
background:var(--accent); }
|
| 80 |
input[type=range]::-moz-range-thumb { width:12px; height:12px; border:0;
|
| 81 |
border-radius:50%; background:var(--accent); }
|
| 82 |
+
.val { font-family:var(--mono); font-size:.9rem; color:var(--text);
|
| 83 |
min-width:2ch; }
|
| 84 |
|
| 85 |
/* Timeline + play */
|
|
|
|
| 88 |
background:none; border:0; cursor:pointer; padding:0;
|
| 89 |
letter-spacing:.04em; }
|
| 90 |
.play:hover { color:var(--text); }
|
| 91 |
+
.lead { font-family:var(--mono); font-size:.9rem; color:var(--muted);
|
| 92 |
min-width:9ch; }
|
| 93 |
|
| 94 |
@media (max-width:720px) {
|