Kundenansicht neu: kein Hineinzoomen beim Tippen auf dem iPhone
Messung am iPhone: die Seite ist verkleinert gezeichnet (462 px Breite auf 393 pt, visualViewport.scale 0,85, wohl Seitenzoom 85 %). Unsere 16-px-Felder haben auf dem Bildschirm dann nur 13,6 pt, iOS zoomt beim Antippen auf 100 % hinein und danach nicht zurück: Ausschnitt, rechts abgeschnitten, die Eingabe liegt hinter der Tastatur. zoom-ausgleich.js setzt auf iOS bei einem Maßstab unter 0,99 die Klasse kn-zoom-ausgleich und den Faktor 1,01/scale an <html>; huelle.css gibt dann allen Textfeldern der Kundenansicht 16 px mal Faktor, also auf dem Bildschirm wieder 16 pt. Nie umgestellt, während ein Feld den Fokus hat. Bei 100 % ändert sich nichts (Chromium: pixelgleich zu main). Die Messung meldet zusätzlich Maßstab (3 Stellen), Faktor, Seitenbreite und Schriftgröße des Feldes im Fokus. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
17bce304eb
commit
1a09bebe41
6 files changed
+99
-5
No files matched your search
@@ -6,7 +6,7 @@ const MAX = 80
|
||||
const ZIEL = '/api/diag/hoehe'
|
||||
const rund = (x) => (Number.isFinite(x) ? Math.round(x * 10) / 10 : null)
|
||||
|
||||
export function starteDiagHoehe({ win = window, doc = document }) {
|
||||
export function starteDiagHoehe({ win = window, doc = document, zoom = null }) {
|
||||
const t0 = Date.now()
|
||||
let n = 0
|
||||
const probe = (stil) => {
|
||||
@@ -31,7 +31,9 @@ export function starteDiagHoehe({ win = window, doc = document }) {
|
||||
iw: win.innerWidth, ih: win.innerHeight, ow: win.outerWidth, oh: win.outerHeight,
|
||||
cw: doc.documentElement.clientWidth, ch: doc.documentElement.clientHeight, dh: doc.documentElement.scrollHeight,
|
||||
sw: win.screen?.width, sh: win.screen?.height, sah: win.screen?.availHeight, dpr: win.devicePixelRatio,
|
||||
vv: vv ? [rund(vv.width), rund(vv.height), rund(vv.offsetTop), rund(vv.pageTop), rund(vv.scale)] : null,
|
||||
vv: vv ? [rund(vv.width), rund(vv.height), rund(vv.offsetTop), rund(vv.pageTop), Math.round(vv.scale * 1000) / 1000] : null,
|
||||
zf: zoom?.faktor?.() ?? null, swd: doc.documentElement.scrollWidth,
|
||||
fs: aktiv && aktiv !== doc.body ? win.getComputedStyle(aktiv).fontSize : null,
|
||||
sy: rund(win.scrollY),
|
||||
sa: [cs.paddingTop, cs.paddingRight, cs.paddingBottom, cs.paddingLeft].map((p) => rund(parseFloat(p))),
|
||||
vh: [vh.offsetHeight, dvh.offsetHeight, svh.offsetHeight, lvh.offsetHeight],
|
||||
|
||||
@@ -195,6 +195,11 @@
|
||||
.kn:has(#bereich-chat:not([hidden])) .kn-kopf-titel { opacity: 1; transform: none; }
|
||||
}
|
||||
|
||||
/* ---------- iOS bei verkleinerter Seite (zoom-ausgleich.js) ----------
|
||||
Ist die Seite kleiner gezeichnet (Seitenzoom unter 100 %), hätten 16-px-Felder auf dem Bildschirm weniger als 16 pt, und iOS zoomt beim Tippen
|
||||
hinein und nicht wieder heraus. Die Klasse an <html> setzt nur zoom-ausgleich.js; der Faktor gleicht den Maßstab aus. */
|
||||
.kn-zoom-ausgleich .kn :is(input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="file"]):not([type="color"]):not([type="button"]):not([type="submit"]):not([type="reset"]):not([type="image"]), textarea, select) { font-size: calc(16px * var(--kn-zoom-ausgleich, 1)) !important; }
|
||||
|
||||
/* ---------- Querformat (Handy gedreht): niedrigere Zeile, keine große Überschrift ---------- */
|
||||
|
||||
@media (max-height: 500px) and (max-width: 880px), (max-height: 500px) and (pointer: coarse) {
|
||||
|
||||
@@ -7,7 +7,8 @@ import { h, leeren, text } from './dom.js'
|
||||
import { api as apiStandard } from './api.js'
|
||||
import * as routerStandard from './router.js'
|
||||
import { BEREICHE, FUNKTIONS_ETAGEN, hashFuerAnsicht, hashFuerRoute, elternVon, titelFuer, zeigtWiderrufsstreifen } from './routen.js'
|
||||
import { HANDY_ABFRAGE, baueNavigation, beobachteTastatur, leisteSichtbar } from './leiste.js'
|
||||
import { HANDY_ABFRAGE, baueNavigation, beobachteTastatur, istTextfeld, leisteSichtbar } from './leiste.js'
|
||||
import { beobachteZoom } from './zoom-ausgleich.js'
|
||||
import { starteDiagHoehe } from './diag-hoehe.js'
|
||||
import { baueEtage } from './etage.js'
|
||||
import { erzeugeZaehler } from './zaehler.js'
|
||||
@@ -278,8 +279,10 @@ export async function starteHuelle({ win = window, doc = document, api = apiStan
|
||||
if (handyMq.addEventListener) handyMq.addEventListener('change', beiMedien)
|
||||
else handyMq.addListener?.(beiMedien)
|
||||
beobachteTastatur({ win, doc, onChange: (z) => doc.body.classList.toggle('kn-tastatur', !leisteSichtbar(z)) })
|
||||
// iOS: bei verkleinerter Seite die Feldschrift so vergrößern, dass iOS beim Tippen nicht hineinzoomt (zoom-ausgleich.js)
|
||||
const zoom = istIos() ? beobachteZoom({ win, doc, istTextfeld }) : null
|
||||
// VORÜBERGEHENDE MESSUNG (2026-10-29, diag-hoehe.js): Bildschirmwerte am iPhone ins Server-Log, wird nach der Klärung entfernt
|
||||
if (istIos() && istHandy() && !viewAs) starteDiagHoehe({ win, doc })
|
||||
if (istIos() && istHandy() && !viewAs) starteDiagHoehe({ win, doc, zoom })
|
||||
|
||||
// 7. Fristen und Zähler laden parallel und brechen den Start nie ab
|
||||
ladeFristen(api).then((daten) => { fristen = daten; streifen.setze(daten); aktualisiereKonto() })
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
/* Kundenansicht neu: iOS zoomt beim Antippen eines Feldes hinein, wenn dessen Schrift auf dem Bildschirm kleiner als 16 pt ist, und zoomt danach
|
||||
nicht zurück: man sieht nur noch einen Ausschnitt, rechts ist alles abgeschnitten, die Eingabe liegt hinter der Tastatur. Unsere Felder haben am
|
||||
Handy 16 px. Ist die Seite aber verkleinert gezeichnet (Seitenzoom in Safari unter 100 %; gemessen am iPhone 2026-10-29: 85 %,
|
||||
visualViewport.scale 0,85), sind das auf dem Bildschirm nur 13,6 pt. Dann bekommen alle Textfelder so viel größere Schrift, dass sie auf dem
|
||||
Bildschirm wieder 16 pt hat: Klasse kn-zoom-ausgleich und Faktor --kn-zoom-ausgleich an <html>, die Regel steht in huelle.css. Bei 100 % passiert nichts. */
|
||||
|
||||
/** Faktor für die Feldschrift beim Maßstab `scale`; 1 = kein Ausgleich (100 % oder hineingezoomt). 1 % Luft gegen Rundung, auf drei Stellen. */
|
||||
export function ausgleichFaktor(scale) {
|
||||
if (!Number.isFinite(scale) || scale <= 0 || scale >= 0.99) return 1
|
||||
return Math.ceil((1.01 / scale) * 1000) / 1000
|
||||
}
|
||||
|
||||
/**
|
||||
* Stellt den Faktor beim Start und bei jeder Maßstabsänderung (visualViewport resize) ein, nie während ein Textfeld den Fokus hat
|
||||
* (iOS hat dann schon entschieden). Rückgabe { faktor, beende }.
|
||||
*/
|
||||
export function beobachteZoom({ win = window, doc = document, istTextfeld = () => false }) {
|
||||
const vv = win.visualViewport
|
||||
if (!vv) return { faktor: () => 1, beende() {} }
|
||||
const wurzel = doc.documentElement
|
||||
let faktor = 1
|
||||
const setze = () => {
|
||||
if (istTextfeld(doc.activeElement)) return
|
||||
const neu = ausgleichFaktor(vv.scale)
|
||||
if (neu === faktor) return
|
||||
faktor = neu
|
||||
if (faktor === 1) {
|
||||
wurzel.classList.remove('kn-zoom-ausgleich')
|
||||
wurzel.style.removeProperty('--kn-zoom-ausgleich')
|
||||
} else {
|
||||
wurzel.style.setProperty('--kn-zoom-ausgleich', String(faktor))
|
||||
wurzel.classList.add('kn-zoom-ausgleich')
|
||||
}
|
||||
}
|
||||
setze()
|
||||
vv.addEventListener('resize', setze)
|
||||
return { faktor: () => faktor, beende() { vv.removeEventListener('resize', setze) } }
|
||||
}
|
||||
@@ -16,5 +16,5 @@ test('huelle.css: am Handy hat die Spalte im Chat mindestens volle Höhe (100vh,
|
||||
test('huelle.js: der alte Höhen-Wächter (Hülle aus- und einblenden) ist weg, die Messung läuft nur am iPhone und nicht in der Mitarbeiter-Ansicht', () => {
|
||||
const js = lies('huelle.js')
|
||||
assert.doesNotMatch(js, /beobachteHoehe|'\.\/hoehe\.js'/)
|
||||
assert.match(js, /if \(istIos\(\) && istHandy\(\) && !viewAs\) starteDiagHoehe\(\{ win, doc \}\)/)
|
||||
assert.match(js, /if \(istIos\(\) && istHandy\(\) && !viewAs\) starteDiagHoehe\(\{ win, doc, zoom \}\)/)
|
||||
})
|
||||
@@ -0,0 +1,46 @@
|
||||
import { test } from 'node:test'
|
||||
import assert from 'node:assert/strict'
|
||||
import { ausgleichFaktor, beobachteZoom } from '../public/neu/zoom-ausgleich.js'
|
||||
import { lies } from './support/knHuelle.js'
|
||||
|
||||
test('ausgleichFaktor: 85 % (iPhone 2026-10-29) ergibt 16 px × 1,188 ≈ 16,2 pt auf dem Bildschirm; 100 %, hineingezoomt und Unsinn ergeben 1', () => {
|
||||
const f = ausgleichFaktor(0.8506)
|
||||
assert.equal(f, 1.188)
|
||||
assert.ok(16 * f * 0.8506 >= 16)
|
||||
assert.equal(ausgleichFaktor(0.5), 2.02)
|
||||
for (const s of [1, 0.99, 1.5, 0, -1, NaN, undefined]) assert.equal(ausgleichFaktor(s), 1)
|
||||
})
|
||||
|
||||
function aufbau(scale) {
|
||||
const hoerer = []
|
||||
const vv = { scale, addEventListener: (t, fn) => hoerer.push(fn), removeEventListener: (t, fn) => hoerer.splice(hoerer.indexOf(fn), 1), feuere: () => hoerer.forEach((fn) => fn()) }
|
||||
const klassen = new Set(); const stil = {}
|
||||
const doc = {
|
||||
activeElement: null,
|
||||
documentElement: {
|
||||
classList: { add: (k) => klassen.add(k), remove: (k) => klassen.delete(k) },
|
||||
style: { setProperty: (k, v) => { stil[k] = v }, removeProperty: (k) => { delete stil[k] } },
|
||||
},
|
||||
}
|
||||
return { vv, doc, klassen, stil, hoerer, win: { visualViewport: vv } }
|
||||
}
|
||||
|
||||
test('beobachteZoom: setzt Klasse und Faktor bei 85 %, nimmt sie bei 100 % weg, ändert nichts während ein Textfeld den Fokus hat', () => {
|
||||
const t = aufbau(0.8506)
|
||||
const feld = { feld: true }
|
||||
const z = beobachteZoom({ win: t.win, doc: t.doc, istTextfeld: (el) => el?.feld === true })
|
||||
assert.deepEqual([[...t.klassen], t.stil, z.faktor()], [['kn-zoom-ausgleich'], { '--kn-zoom-ausgleich': '1.188' }, 1.188])
|
||||
t.doc.activeElement = feld; t.vv.scale = 1; t.vv.feuere() // iOS zoomt beim Tippen: nichts umstellen
|
||||
assert.equal(z.faktor(), 1.188)
|
||||
t.doc.activeElement = null; t.vv.feuere()
|
||||
assert.deepEqual([[...t.klassen], t.stil, z.faktor()], [[], {}, 1])
|
||||
z.beende(); assert.equal(t.hoerer.length, 0)
|
||||
const ohne = beobachteZoom({ win: {}, doc: t.doc }); assert.equal(ohne.faktor(), 1); ohne.beende()
|
||||
})
|
||||
|
||||
test('huelle.css und huelle.js: Ausgleich nur über die Klasse an <html>, für alle Textfelder der Kundenansicht, nur auf iOS verdrahtet', () => {
|
||||
const css = lies('huelle.css')
|
||||
assert.match(css, /\.kn-zoom-ausgleich \.kn :is\(input:not\(\[type="checkbox"\]\)[^{]*, textarea, select\) \{ font-size: calc\(16px \* var\(--kn-zoom-ausgleich, 1\)\) !important; \}/)
|
||||
const js = lies('huelle.js')
|
||||
assert.match(js, /const zoom = istIos\(\) \? beobachteZoom\(\{ win, doc, istTextfeld \}\) : null/)
|
||||
})
|
||||
Reference in new issue
Block a user