aboutsummaryrefslogtreecommitdiff
path: root/docs/prototypes/widgets.js
diff options
context:
space:
mode:
Diffstat (limited to 'docs/prototypes/widgets.js')
-rw-r--r--docs/prototypes/widgets.js50
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' },
},