diff options
Diffstat (limited to 'docs/prototypes/widgets.js')
| -rw-r--r-- | docs/prototypes/widgets.js | 50 |
1 files changed, 27 insertions, 23 deletions
diff --git a/docs/prototypes/widgets.js b/docs/prototypes/widgets.js index e11862c..0126949 100644 --- a/docs/prototypes/widgets.js +++ b/docs/prototypes/widgets.js @@ -3659,9 +3659,9 @@ GW.nixie = function (host, opts = {}) { Contract (everything a consumer needs; no page globals touched): opts: rows (1) x cells (4) grid; chars (flap order, default space+A-Z+0-9); - flapMs (70, per-cell rate jittered 0.8x-1.35x so letters finish + flapMs (85, per-cell rate jittered 0.8x-1.35x so letters finish at different times, base mutable via setFlapMs); animate (true); - skin ('dark'|'white'|'light'|'paper'); font ('mono'|'helv'); + skin ('white'|'dark'|'light'|'paper', card default white); font ('mono'|'helv'); words (the demo pool, at least rows+1 entries: next() sends every row to a different random word, mutually distinct; set(i) pins the top row for determinism); onChange(idx|-1, top word) fires at @@ -3679,7 +3679,8 @@ GW.splitFlap = function (host, opts = {}) { const rows = opts.rows || 1; const cells = opts.cells || 4; const chars = opts.chars || ' ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; - let flapMs = Math.min(400, Math.max(20, opts.flapMs || 70)); + const clampMs = ms => Math.min(400, Math.max(20, ms | 0)); + let flapMs = clampMs(opts.flapMs || 85); const ci = ch => Math.max(0, chars.indexOf(ch)); const fit = (str, w) => String(str).padEnd(w, ' ').slice(0, w); @@ -3692,7 +3693,14 @@ GW.splitFlap = function (host, opts = {}) { const st = []; // row-major cell states const settleCbs = []; if (opts.onSettle) settleCbs.push(opts.onSettle); let announced = true; // no announcement for the silent first paint + let idx = 0; // last set() index; -1 after a scramble + let lastGrid = ''; // the last commanded grid — the demo picks against + // commands, not in-flight glyphs mid-cascade const allSettled = () => st.every(S => !S.running && S.cur === S.target); + const fitGrid = str => Array.from({ length: rows }, + (_, r) => fit(String(str).split('\n')[r] || '', cells)).join('\n'); + const reading = () => Array.from({ length: rows }, + (_, r) => st.slice(r * cells, (r + 1) * cells).map(S => chars[S.cur]).join('')).join('\n'); const makeCell = () => { const d = document.createElement('span'); d.className = 'flapd'; d.innerHTML = '<b class="fh ftn"></b><b class="fh fbc"></b><b class="fh ftc"></b><b class="fh fbn"></b>'; @@ -3726,11 +3734,10 @@ GW.splitFlap = function (host, opts = {}) { S.ftn.textContent = chars[nk]; S.fbn.textContent = chars[nk]; if (animate) { try { + const flip = { duration: Math.round(flapMs * S.jitter), easing: 'ease-in' }; await Promise.all([ - S.ftc.animate([{ transform: 'rotateX(0deg)' }, { transform: 'rotateX(-180deg)' }], - { duration: Math.round(flapMs * S.jitter), easing: 'ease-in' }).finished, - S.fbn.animate([{ transform: 'rotateX(180deg)' }, { transform: 'rotateX(0deg)' }], - { duration: Math.round(flapMs * S.jitter), easing: 'ease-in' }).finished, + S.ftc.animate([{ transform: 'rotateX(0deg)' }, { transform: 'rotateX(-180deg)' }], flip).finished, + S.fbn.animate([{ transform: 'rotateX(180deg)' }, { transform: 'rotateX(0deg)' }], flip).finished, ]); } catch { break; /* cancelled (card torn down mid-flip) */ } } @@ -3757,8 +3764,7 @@ GW.splitFlap = function (host, opts = {}) { }; const setText = str => { announced = false; - const ls = String(str).split('\n'); - lastGrid = Array.from({ length: rows }, (_, r) => fit(ls[r] || '', cells)).join('\n'); + lastGrid = fitGrid(str); // Assign every target before running any cell: a cell that finishes // synchronously (animate:false) must not see its neighbours' stale, // already-satisfied targets and announce a partial board as settled. @@ -3766,18 +3772,18 @@ GW.splitFlap = function (host, opts = {}) { st.forEach(run); maybeSettle(); // covers a command that changes nothing }; - const silent = str => gridTargets(str).forEach((k, i) => { - const S = st[i]; S.cur = S.target = k; - S.ftn.textContent = S.fbc.textContent = S.ftc.textContent = S.fbn.textContent = chars[k]; - }); + const silent = str => { + lastGrid = fitGrid(str); + gridTargets(str).forEach((k, i) => { + const S = st[i]; S.cur = S.target = k; + S.ftn.textContent = S.fbc.textContent = S.ftc.textContent = S.fbn.textContent = chars[k]; + }); + }; // The words demo: every advance sends each row to a completely different // word, drawn at random from the pool; rows stay mutually distinct. set(i) // pins the top row (deterministic callers, tests); next() scrambles all. - let idx = 0; - let lastGrid = null; // the last commanded grid — scramble picks against - // commands, not in-flight glyphs mid-cascade - const rowWords = () => (lastGrid || reading()).split('\n'); + const rowWords = () => lastGrid.split('\n'); const pickNew = taken => { const open = words.filter(w => !taken.includes(fit(w, cells))); return fit(open[(Math.random() * open.length) | 0] || wordAt(0), cells); @@ -3793,8 +3799,8 @@ GW.splitFlap = function (host, opts = {}) { const scramble = () => { idx = -1; const g = gridFor(null); setText(g); onChange(-1, g.split('\n')[0].trim()); }; f.addEventListener('click', scramble); - // skin: flap polarity — dark (cream on black, the default) or the inverse - // light board (black on cream). A construction axis, not an accent. + // construction axes (skin, font): swap this axis's classes per STYLES. + // Polarity and typeface are construction, not accent — no colour policy. const setStyle = (axis, name) => { const ax = GW.splitFlap.STYLES[axis]; const o = ax && ax[name]; if (!o) return; @@ -3806,12 +3812,10 @@ GW.splitFlap = function (host, opts = {}) { // first paint: consecutive pool words, silent (no cascade on load) silent(Array.from({ length: rows }, (_, r) => fit(wordAt(r), cells)).join('\n')); - const reading = () => Array.from({ length: rows }, - (_, r) => st.slice(r * cells, (r + 1) * cells).map(S => chars[S.cur]).join('')).join('\n'); return { el: f, get: () => idx, set, next: scramble, setStyle, setText, chars, reading, onSettle: cb => settleCbs.push(cb), - flapMs: () => flapMs, setFlapMs: ms => { flapMs = Math.min(400, Math.max(20, ms | 0)); }, + flapMs: () => flapMs, setFlapMs: ms => { flapMs = clampMs(ms); }, }; }; GW.splitFlap.STYLES = { @@ -3820,8 +3824,8 @@ GW.splitFlap.STYLES = { // dot: the board colour; the gallery card letters each chip in the skin's // ink so the swatch is a miniature flap cell, not an ambiguous colour dot skin: { - dark: { cls: '', dot: '#141210' }, white: { cls: 'flap-white', dot: '#141210' }, + dark: { cls: '', dot: '#141210' }, light: { cls: 'flap-light', dot: '#f3e7c5' }, paper: { cls: 'flap-paper', dot: '#ffffff' }, }, |
