Fishies 0.8.1
This commit is contained in:
commit
5821ce9f4d
76 changed files with 2198 additions and 0 deletions
38
src/fx-image.ts
Normal file
38
src/fx-image.ts
Normal 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)})()`
|
||||
Loading…
Add table
Add a link
Reference in a new issue