Fishies 0.8.1

This commit is contained in:
CRTkafa 2026-10-01 12:41:30 +07:00
commit 5821ce9f4d
76 changed files with 2198 additions and 0 deletions

38
src/fx-image.ts Normal file
View file

@ -0,0 +1,38 @@
/** Pictures that never paint half-loaded (owner, 1 October 2026: "never, ever should I see an
* image while it is loading").
*
* Every picture the site, the game and the launcher show is an `<img data-fx="wait">`
* (FishiesImg). loader.css keeps its pixels out of its own box while it waits, and shows the
* Fishies spinner on a glass placeholder of the same size in their place, so nothing moves.
* Once the file is complete *and* decoded (`img.decode()`), the state becomes `fade` (a short
* fade in) or, for a picture that was already in memory when it mounted, `ready` (no fade:
* a cached picture asking to fade in is theatre). A broken picture becomes `error`: the
* placeholder stays, the spinner stops.
*
* The same `reveal` runs twice over: as an inline script in the document head, listening for
* every image `load` in the capture phase, so a server-rendered picture appears as soon as it
* is decoded rather than when the page's JavaScript hydrates; and from FishiesImg's effect,
* for a picture that was already complete before anything was listening. Both are
* idempotent: only a picture still in `wait` for its current address changes state. */
export type FxState = 'wait' | 'fade' | 'ready' | 'error'
/** Self-contained on purpose: `FX_IMAGE_SCRIPT` inlines its source, so it may reference
* nothing outside its own body. */
export function reveal(img: HTMLImageElement, instant?: boolean): void {
if (img.getAttribute('data-fx') !== 'wait' || !img.complete) return
const src = img.currentSrc || img.src
const set = (state: string) => {
if (img.getAttribute('data-fx') === 'wait' && (img.currentSrc || img.src) === src) img.setAttribute('data-fx', state)
}
if (!img.naturalWidth) { set('error'); return }
if (instant) { set('ready'); return }
if (typeof img.decode !== 'function') { set('fade'); return }
img.decode().then(() => set('fade'), () => set(img.naturalWidth ? 'fade' : 'error'))
}
/** The document-head half: catches `load`/`error` of every waiting picture, including the
* server-rendered ones that finish before hydration. */
export const FX_IMAGE_SCRIPT = `(function(){var r=${reveal.toString()};` +
`function h(e){var t=e.target;if(t&&t.tagName==='IMG'&&t.hasAttribute('data-fx'))r(t)}` +
`document.addEventListener('load',h,true);document.addEventListener('error',h,true)})()`