Unten steht jetzt nur noch der Scrubber, wie in der App

Der strukturelle Unterschied, den ich übersehen hatte: in der iPad-App
liegt unten nichts als der Regler über die volle Breite, darunter Zeit
links und Restzeit rechts. Bei mir standen Zeit, Regler und Restzeit
nebeneinander — der Regler wirkte dadurch wie ein Formularfeld zwischen
zwei Beschriftungen — und darunter lag noch eine Werkzeugleiste mit
Lautstärkeregler und Tempo-Auswahlfeld, die es in der App gar nicht gibt.

Alles Einstellbare sitzt jetzt oben rechts beisammen: Tonspur,
Untertitel, Qualität, Geschwindigkeit, Ton, Vollbild. Das Tempo als
Liste wie die anderen; ein Auswahlfeld mitten in der Bedienleiste war das
einzige Bauteil, das nach Formular aussah. Die Lautstärke über Mausrad
auf dem Tonknopf und über die Pfeiltasten.

Und ein Fehler, der genau das verhinderte, was gewünscht war: Die
Bedienfläche lag als Ganzes über dem Bild und schluckte jeden Klick.
"Ins Bild tippen hält an" funktionierte damit nur, solange die Bedienung
unsichtbar war — also fast nie. Jetzt nehmen nur ihre Knöpfe Klicks
entgegen. Nachgemessen: Klick pausiert, Pfeil rechts spult 30 Sekunden.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Scarriffle
2026-09-14 18:44:41 +02:00
co-authored by Claude Opus 5
parent 24e40f7403
commit 6d8440ede8
+46 -37
View File
@@ -246,12 +246,19 @@ img { display: block; }
}
#player video { width: 100%; height: 100%; object-fit: contain; background: #000; cursor: pointer; }
/* **Die Fläche selbst nimmt keine Klicks entgegen, nur ihre Knöpfe.**
Sonst liegt sie über dem Bild und schluckt jeden Klick — und „ins Bild
tippen hält an" funktioniert genau dann nicht, wenn die Bedienung zu
sehen ist, also fast immer. */
.pl-chrome {
position: absolute; inset: 0; opacity: 0; pointer-events: none;
transition: opacity .25s ease;
display: grid; grid-template-rows: auto 1fr auto;
}
.pl-chrome.on { opacity: 1; pointer-events: auto; }
.pl-chrome.on { opacity: 1; }
.pl-chrome.on button,
.pl-chrome.on .pl-track,
.pl-chrome.on select { pointer-events: auto; }
/* Zwei Verläufe statt einer Fläche: die Mitte bleibt frei, und genau dort
ist das Bild. */
@@ -315,15 +322,18 @@ img { display: block; }
beides unlesbar, und die Zahl ist die eigentliche Auskunft. */
#pl-back, #pl-fwd { font-size: 15px; font-weight: 600; font-variant-numeric: tabular-nums; }
.pl-scrub { display: flex; align-items: center; gap: var(--s-regular); }
.pl-scrub span {
font-size: 13px; color: var(--text-2); font-variant-numeric: tabular-nums;
min-width: 52px;
/* Wie in der App: der Regler über die **volle Breite**, die Zeiten darunter.
Nebeneinander gestellt nimmt er in der Mitte Platz weg und wirkt wie ein
Formularfeld zwischen zwei Beschriftungen. */
.pl-times {
display: flex; justify-content: space-between;
margin-top: var(--s-tight);
font-size: 13px; color: rgba(255,255,255,.8);
font-variant-numeric: tabular-nums;
}
.pl-scrub span:last-child { text-align: right; }
.pl-track {
position: relative; flex: 1; height: 22px; cursor: pointer;
position: relative; width: 100%; height: 22px; cursor: pointer;
display: flex; align-items: center;
}
.pl-track::before {
@@ -341,14 +351,7 @@ img { display: block; }
}
.pl-track:hover i { transform: translateX(-50%) scale(1.25); }
.pl-tools {
display: flex; align-items: center; gap: var(--s-regular);
margin-top: var(--s-snug);
}
.pl-spacer { flex: 1; }
.pl-tools input[type=range] { width: 92px; accent-color: var(--accent); }
.pl-rate { font-size: 13px; color: var(--text-2); display: flex; align-items: center; gap: var(--s-snug); }
.pl-rate .pick { width: auto; padding: 6px var(--s-regular); font-size: 13px; }
/* Während geladen wird, treten die Transportknöpfe zurück: sie tun ohnehin
nichts, und der Kreisel lag vorher genau hinter dem größten von ihnen. */
@@ -1600,6 +1603,9 @@ function openPlayer(item) {
<button class="pl-icon" id="pl-audio" aria-label="Tonspur" hidden>${icon('waveform')}</button>
<button class="pl-icon" id="pl-subs" aria-label="Untertitel" hidden>${icon('captions')}</button>
<button class="pl-icon pl-quality" id="pl-qual" aria-label="Qualität">Original</button>
<button class="pl-icon" id="pl-speed" aria-label="Geschwindigkeit">${icon('speed')}</button>
<button class="pl-icon" id="pl-mute" aria-label="Ton aus">${icon('sound')}</button>
<button class="pl-icon" id="pl-full" aria-label="Vollbild">${icon('expand')}</button>
</div>
<div class="pl-center">
@@ -1609,18 +1615,11 @@ function openPlayer(item) {
</div>
<div class="pl-bottom">
<div class="pl-scrub">
<span id="pl-now">0:00</span>
<div class="pl-track" id="pl-track"><div class="pl-buf"></div><div class="pl-fill"></div><i></i></div>
<div class="pl-times">
<span id="pl-now">0:00</span>
<span id="pl-left">–:--</span>
</div>
<div class="pl-tools">
<button class="pl-icon" id="pl-mute" aria-label="Ton aus">${icon('sound')}</button>
<input type="range" id="pl-vol" min="0" max="1" step="0.05" value="1" aria-label="Lautstärke">
<span class="pl-spacer"></span>
<label class="pl-rate">Tempo <select class="pick" id="pl-rate"></select></label>
<button class="pl-icon" id="pl-full" aria-label="Vollbild">${icon('expand')}</button>
</div>
</div>
</div>
<div class="pl-menu" id="pl-menu" hidden></div>
@@ -1715,20 +1714,29 @@ function openPlayer(item) {
};
// --- Ton, Tempo, Vollbild ---
const vol = el2('pl-vol');
vol.oninput = () => { v.volume = +vol.value; v.muted = +vol.value === 0; };
el2('pl-mute').onclick = () => {
v.muted = !v.muted;
el2('pl-mute').innerHTML = icon(v.muted ? 'muted' : 'sound');
const paintTon = () => {
el2('pl-mute').innerHTML = icon(v.muted || v.volume === 0 ? 'muted' : 'sound');
el2('pl-mute').title = `Lautstärke ${Math.round(v.volume * 100)} %`;
};
el2('pl-mute').onclick = () => { v.muted = !v.muted; paintTon(); };
const rate = el2('pl-rate');
RATES.forEach((r) => {
const o = document.createElement('option');
o.value = r; o.textContent = r === 1 ? 'Normal' : r + '×'; o.selected = r === P.rate;
rate.appendChild(o);
});
rate.onchange = () => { v.playbackRate = +rate.value; };
// Das Tempo als Liste wie Ton und Untertitel — ein Auswahlfeld mitten in
// der Bedienleiste war das einzige Bauteil, das nach Formular aussah.
el2('pl-speed').onclick = () => zeigeListe(el2('pl-speed'), 'Geschwindigkeit',
RATES.map((r) => ({
text: r === 1 ? 'Normal' : r + '×',
gewaehlt: Math.abs(v.playbackRate - r) < 0.01,
waehlen: () => { v.playbackRate = r; closeMenu(); show(); },
})));
// Lautstärke über das Mausrad auf dem Tonknopf und über die Pfeiltasten —
// ein Schieberegler in der Leiste steht in der App auch nicht.
el2('pl-mute').addEventListener('wheel', (e) => {
e.preventDefault();
v.volume = Math.min(1, Math.max(0, v.volume - Math.sign(e.deltaY) * 0.05));
v.muted = v.volume === 0;
paintTon();
}, { passive: false });
el2('pl-full').onclick = () =>
document.fullscreenElement ? document.exitFullscreen() : layer.requestFullscreen?.();
@@ -1740,8 +1748,8 @@ function openPlayer(item) {
' ': () => toggle(), k: () => toggle(),
ArrowLeft: () => { v.currentTime -= P.skipBackward; },
ArrowRight: () => { v.currentTime += P.skipForward; },
ArrowUp: () => { v.volume = Math.min(1, v.volume + 0.1); vol.value = v.volume; },
ArrowDown: () => { v.volume = Math.max(0, v.volume - 0.1); vol.value = v.volume; },
ArrowUp: () => { v.volume = Math.min(1, v.volume + 0.1); paintTon(); },
ArrowDown: () => { v.volume = Math.max(0, v.volume - 0.1); paintTon(); },
f: () => el2('pl-full').click(),
Escape: () => close(),
};
@@ -2068,6 +2076,7 @@ function streamURL(path, query) {
const ICONS = {
play: '<path d="M8 5.5v13l11-6.5z" fill="currentColor" stroke="none"/>',
waveform: '<path d="M3 12h2M7 8v8M11 4.5v15M15 8v8M19 11h2"/>',
speed: '<path d="M12 20a8 8 0 100-16 8 8 0 000 16z"/><path d="M12 12l4-3"/>',
captions: '<rect x="3" y="5.5" width="18" height="13" rx="3"/><path d="M9.5 10.5a2.2 2.2 0 100 3M16 10.5a2.2 2.2 0 100 3"/>',
captionsOn: '<rect x="3" y="5.5" width="18" height="13" rx="3" fill="currentColor" stroke="none"/><path d="M9.5 10.5a2.2 2.2 0 100 3M16 10.5a2.2 2.2 0 100 3" stroke="#0B0B0D"/>',
check: '<path d="M5 12.5l4.5 4.5L19 7.5"/>',