Kundenansicht neu: Wasser-Leiste breiter mit Wörtern, Unterseiten im Vollbild
Wunsch vom 29.10. nach dem ersten Blick am iPhone: - Die Leiste nutzt die Breite (12 px Rand, höchstens 480 px), Symbol über dem Wort, und steht ganz über dem Wisch-Balken des iPhones (Safe-Area plus 6 px statt minus 10 px). - Unterseiten öffnen sich ganz und werden mit Unschärfe eingeblendet statt hochzufahren. Oben links die Überschrift, oben rechts „Zurück“ als Glasknopf, der beim Scrollen stehen bleibt (neuer Platz #kn-blatt-zurueck). - Reiter einer Funktion stehen unten in derselben Wasser-Leiste (Art 'reiter', Kurzwörter, kleinere Schrift wenn es eng wird), ohne Zurück. Ohne Reiter bleibt unten nichts. Die alte Leiste gibt beim Neuaufbau ihren WebGL-Kontext frei. Glas und Unschärfe stehen weiter allein in wasser-leiste.css; huelle.css bleibt ohne Glas. Desktop unverändert. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
c997703251
commit
1f350ca432
10 files changed
+147
-95
No files matched your search
+23
-12
@@ -3,7 +3,8 @@
|
||||
Bereich, reißt bei langen Sprüngen in Tröpfchen und fließt mit einer Welle wieder zusammen; jeder Tipp schickt eine Welle durch die Pille, jedes
|
||||
Symbol spielt eine kleine Szene. Das Wasser zeichnet ein kleiner WebGL-Shader (nur in der Pille). Er ruht, solange die Pille nicht zu sehen ist,
|
||||
und kommt einige Sekunden nach der letzten Bedienung zur Ruhe (Akku). Ohne WebGL zeigt die Pille einen CSS-Tropfen (wasser-leiste.css).
|
||||
Rollen wie die Fahrstuhl-Zeile davor: tablist mit tab, aria-selected und aria-current; Zähler als .kn-zahl am Symbol, Beschriftung per aria-label.
|
||||
Zwei Arten: 'bereiche' (Hauptleiste: Symbol über dem Wort, Zähler als .kn-zahl am Symbol) und 'reiter' (Reiter einer Unterseite: nur Wörter,
|
||||
Kurzwort `kurz` falls vorhanden). Rollen wie die Fahrstuhl-Zeile davor: tablist mit tab, aria-selected und aria-current, Beschriftung per aria-label.
|
||||
Die Rechenfunktionen (Feder, Tröpfchen, Wellen) sind rein und werden mit node --test geprüft; ohne Browser baut das Modul nur die Knöpfe. */
|
||||
|
||||
import { h } from './dom.js'
|
||||
@@ -291,15 +292,17 @@ const beschriftung = (e) => {
|
||||
}
|
||||
|
||||
/**
|
||||
* Wasser-Leiste bauen. Eintrag { key, label, symbol?, fx?, einheit?, ausrufText? }; symbol und fx folgen ohne Angabe dem key (SYMBOLE, SZENEN).
|
||||
* bei(key) wird bei jedem Tipp gerufen, auch auf den aktiven Bereich (die Hülle scrollt dann nach oben).
|
||||
* Liefert das Element mit setzeAktiv(key), setzeAnzeige(key, { zahl, ausruf }), messen() und beende().
|
||||
* Wasser-Leiste bauen. Eintrag { key, label, kurz?, symbol?, fx?, einheit?, ausrufText? }. Art 'bereiche': symbol und fx folgen ohne Angabe dem key
|
||||
* (SYMBOLE, SZENEN). Art 'reiter': kein Symbol, keine Szene, sichtbar ist `kurz` (sonst label), vorgelesen label.
|
||||
* bei(key) wird bei jedem Tipp gerufen, auch auf den aktiven Eintrag (die Hülle scrollt dann nach oben).
|
||||
* Liefert das Element mit setzeAktiv(key), setzeAnzeige(key, { zahl, ausruf }), messen() und beende() (gibt auch den WebGL-Kontext frei).
|
||||
*/
|
||||
export function baueWasserLeiste({ eintraege, aktiv = '', ariaLabel = 'Bereiche', bei = () => {}, warte = (fn, ms) => setTimeout(fn, ms) }) {
|
||||
export function baueWasserLeiste({ eintraege, aktiv = '', art = 'bereiche', ariaLabel = 'Bereiche', bei = () => {}, warte = (fn, ms) => setTimeout(fn, ms) }) {
|
||||
const win = globalThis.window
|
||||
const doc = globalThis.document
|
||||
const s = neuerZustand()
|
||||
const liste = eintraege.map((e) => ({ ...e, fx: e.fx ?? SZENEN[e.key] ?? 'pop', zahl: 0, ausruf: false }))
|
||||
const bereiche = art === 'bereiche'
|
||||
const liste = eintraege.map((e) => ({ ...e, fx: e.fx ?? (bereiche ? SZENEN[e.key] ?? 'pop' : 'none'), zahl: 0, ausruf: false }))
|
||||
let aktuell = aktiv
|
||||
let glc = null
|
||||
let glVersucht = false // WebGL erst, wenn die Pille zu sehen ist (am Desktop nie)
|
||||
@@ -308,15 +311,15 @@ export function baueWasserLeiste({ eintraege, aktiv = '', ariaLabel = 'Bereiche'
|
||||
const jetzt = () => (globalThis.performance?.now ? globalThis.performance.now() : Date.now())
|
||||
|
||||
for (const e of liste) {
|
||||
e.ico = h('span', { class: 'kn-wl-ico' }, baueSymbol(e.symbol ?? e.key, doc))
|
||||
e.ico = bereiche ? h('span', { class: 'kn-wl-ico' }, baueSymbol(e.symbol ?? e.key, doc)) : null
|
||||
e.tab = h('button', { class: 'kn-wl-item', role: 'tab', type: 'button', 'data-key': e.key, 'aria-selected': 'false', 'aria-label': e.label, tabindex: '-1', on: { click: () => waehle(e.key) } },
|
||||
e.ico, h('span', { class: 'kn-wl-label' }, e.label))
|
||||
e.ico, h('span', { class: 'kn-wl-label' }, e.kurz ?? e.label))
|
||||
}
|
||||
const tabs = liste.map((e) => e.tab)
|
||||
const canvas = h('canvas', { class: 'kn-wl-wasser', 'aria-hidden': 'true' })
|
||||
const tropfen = h('span', { class: 'kn-wl-tropfen', 'aria-hidden': 'true' })
|
||||
const wurzel = h('div', {
|
||||
class: 'kn-wl', role: 'tablist', 'aria-label': ariaLabel, 'data-gl': 'on', 'data-brechung': 'aus',
|
||||
class: 'kn-wl', role: 'tablist', 'aria-label': ariaLabel, 'data-art': bereiche ? 'bereiche' : 'reiter', 'data-gl': 'on', 'data-brechung': 'aus',
|
||||
on: { keydown: beiTaste, pointermove: beiMaus, pointerleave: () => { s.maus.on = false; schleife() }, pointerdown: beiDruck },
|
||||
}, baueFilter(doc), h('span', { class: 'kn-wl-glas', 'aria-hidden': 'true' }), canvas, tropfen, tabs)
|
||||
|
||||
@@ -370,6 +373,10 @@ export function baueWasserLeiste({ eintraege, aktiv = '', ariaLabel = 'Bereiche'
|
||||
function messen() {
|
||||
if (!(wurzel.offsetWidth > 0)) return // nicht zu sehen (Desktop, Tastatur offen) oder ohne Browser
|
||||
if (!glVersucht) starteGL()
|
||||
if (!bereiche) { // passen die Wörter nicht, wird die Schrift kleiner (wie schriftStufe in reiter.js)
|
||||
wurzel.removeAttribute('data-eng')
|
||||
if (wurzel.scrollWidth > wurzel.clientWidth + 1) wurzel.setAttribute('data-eng', '')
|
||||
}
|
||||
s.w = wurzel.offsetWidth
|
||||
s.h = wurzel.offsetHeight
|
||||
const el = liste.find((e) => e.key === aktuell)?.tab
|
||||
@@ -398,13 +405,14 @@ export function baueWasserLeiste({ eintraege, aktiv = '', ariaLabel = 'Bereiche'
|
||||
wecke()
|
||||
}
|
||||
function symbolMitte(i) {
|
||||
const b = liste[i]?.ico?.getBoundingClientRect?.()
|
||||
const b = (liste[i]?.ico ?? liste[i]?.tab)?.getBoundingClientRect?.()
|
||||
return b ? lokal(b.left + b.width / 2, b.top + b.height / 2) : null
|
||||
}
|
||||
/** Szene des Symbols (neu) starten; die anderen Symbole kehren in die Ruhe zurück. */
|
||||
function spiele(i) {
|
||||
liste.forEach((e, j) => { if (j !== i) e.ico.removeAttribute('data-fx') })
|
||||
liste.forEach((e, j) => { if (j !== i) e.ico?.removeAttribute('data-fx') })
|
||||
const e = liste[i]
|
||||
if (!e.ico) return
|
||||
e.ico.removeAttribute('data-fx')
|
||||
if (e.fx === 'none') return
|
||||
void e.ico.offsetWidth // Stil auflösen, damit die Animation von vorn beginnt
|
||||
@@ -467,7 +475,7 @@ export function baueWasserLeiste({ eintraege, aktiv = '', ariaLabel = 'Bereiche'
|
||||
|
||||
function setzeAnzeige(key, { zahl = 0, ausruf = false } = {}) {
|
||||
const e = liste.find((x) => x.key === key)
|
||||
if (!e) return
|
||||
if (!e?.ico) return
|
||||
e.zahl = zahl
|
||||
e.ausruf = ausruf
|
||||
const text = anzeigeText({ zahl, ausruf })
|
||||
@@ -522,6 +530,9 @@ export function baueWasserLeiste({ eintraege, aktiv = '', ariaLabel = 'Bereiche'
|
||||
for (const fn of aufraeumen.splice(0)) fn()
|
||||
if (raf) win?.cancelAnimationFrame?.(raf)
|
||||
raf = 0
|
||||
glc?.gl.getExtension('WEBGL_lose_context')?.loseContext() // Browser halten nur wenige Kontexte: jede Unterseite baut ihre Reiter neu
|
||||
glc = null
|
||||
glVersucht = true
|
||||
}
|
||||
|
||||
wurzel.setzeAktiv = setzeAktiv
|
||||
|
||||
Reference in new issue
Block a user