Ein Kreisel ist kein Pausenknopf

Während der Player auf Daten wartete, stand im mittleren Knopf das
Abspieldreieck und der Ladekreis lag dahinter. Das las sich als
„angehalten": man drückte auf Abspielen, und es geschah nichts — es lief
ja bereits, es kam nur nichts an.

Jetzt sitzt der Kreisel **im** Knopf, dort wo sonst das Symbol steht. Der
freistehende Kreisel in der Bildmitte bleibt für den Fall, dass die
Bedienelemente ausgeblendet sind. Zwei übereinander gibt es nicht mehr.

`togglePlayPause` zählt das Warten jetzt zum Laufen. Vorher führte ein
Tipp in diesem Moment zu `play()` auf etwas, das schon spielte.

Dazu auf dem Mac, wie im Browser auch:

- Ein Klick ins Bild hält an, statt erst die Bedienelemente zu holen. Die
  kommen dort von allein, sobald sich der Zeiger regt.
- Leertaste hält an, Pfeil links und rechts springen.

Mit dem Finger bleibt es beim Alten: ohne Zeiger gibt es keine Bewegung
ohne Berührung, und ein Tipp, der sofort anhält, nähme den einzigen Weg
zu Scrubber, Tonspur und Qualität.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Scarriffle
2026-09-15 20:33:24 +02:00
co-authored by Claude Opus 5
parent 3af6c3112f
commit 9a4b6b1e06
6 changed files with 245 additions and 39 deletions
+44 -12
View File
@@ -348,19 +348,26 @@ img { display: block; }
/* 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. */
#player.loading .pl-center { opacity: 0; pointer-events: none; }
/* Während des ersten Ladens haben Vor- und Zurückspulen keine Wirkung — es
gibt noch keine Zeitachse. Der mittlere Knopf bleibt stehen: er trägt
jetzt selbst den Kreisel. */
#player.loading .pl-center > :not(.pl-play) { opacity: 0; pointer-events: none; }
.pl-center { transition: opacity .2s ease; }
.pl-busy {
position: absolute; display: none; pointer-events: none;
}
.pl-busy.on { display: block; }
.pl-busy i {
.pl-busy i, .pl-wait {
display: block; width: 44px; height: 44px; border-radius: 999px;
border: 3px solid rgba(255,255,255,.2); border-top-color: var(--accent);
animation: spin .8s linear infinite;
}
/* Feste Größe für den mittleren Knopf, damit die beiden daneben stehen
bleiben: der Kreisel ist schmaler als das Symbol, das er vertritt. */
.pl-round.pl-play { width: 68px; height: 68px; }
.pl-error {
position: absolute; max-width: 460px; padding: var(--s-section);
background: var(--elevated); border: 1px solid rgba(229,72,77,.35);
@@ -1624,9 +1631,16 @@ function openPlayer(item) {
const chrome = layer.querySelector('.pl-chrome');
const el2 = (id) => layer.querySelector('#' + id);
/* Ob gerade auf Daten gewartet wird — am Anfang immer. */
let wartet = true;
const fail = (satz, hinweis, adresse) => {
layer.classList.remove('loading');
el2('pl-busy').classList.remove('on');
// Sonst dreht sich der Kreisel im Knopf hinter dem Fehlerkasten weiter,
// als käme noch etwas.
wartet = false;
paint();
layer.querySelector('.pl-error')?.remove();
const box = document.createElement('div');
box.className = 'pl-error';
@@ -1644,7 +1658,17 @@ function openPlayer(item) {
clearTimeout(hideTimer);
// Solange etwas steht, bleibt die Bedienung: sie auszublenden, während
// niemand spielt, versteckt den einzigen Knopf, der weiterhilft.
if (!v.paused) hideTimer = setTimeout(() => chrome.classList.remove('on'), 3000);
if (!v.paused) {
hideTimer = setTimeout(() => { chrome.classList.remove('on'); spin(); }, 3000);
}
spin();
};
/* Der freistehende Kreisel — nur, wenn die Bedienung aus ist. Sonst
stünden zwei übereinander: einer in der Mitte des Bildes und einer im
Abspielknopf, der genau davor sitzt. */
const spin = () => {
el2('pl-busy').classList.toggle('on', wartet && !chrome.classList.contains('on'));
};
layer.addEventListener('mousemove', show);
layer.addEventListener('touchstart', show, { passive: true });
@@ -1656,16 +1680,24 @@ function openPlayer(item) {
el2('pl-fwd').onclick = () => { v.currentTime = Math.min(v.duration || 1e9, v.currentTime + P.skipForward); show(); };
v.addEventListener('click', toggle);
/* Zeichnet den mittleren Knopf: Kreisel, Dreieck oder Doppelbalken.
Solange auf Daten gewartet wird, steht dort der Kreisel — **kein**
Dreieck. Ein Dreieck heißt „angehalten, drück mich", und beides stimmte
nicht: es lief bereits, und der Klick bewirkte nichts. */
const paint = () => {
el2('pl-toggle').innerHTML = icon(v.paused ? 'play' : 'pause', 52);
el2('pl-toggle').setAttribute('aria-label', v.paused ? 'Abspielen' : 'Anhalten');
show();
const knopf = el2('pl-toggle');
knopf.innerHTML = wartet ? '<i class="pl-wait"></i>' : icon(v.paused ? 'play' : 'pause', 52);
knopf.setAttribute('aria-label', wartet ? 'Lädt' : v.paused ? 'Abspielen' : 'Anhalten');
};
v.addEventListener('play', paint);
v.addEventListener('pause', paint);
v.addEventListener('waiting', () => el2('pl-busy').classList.add('on'));
const waiting = (an) => { wartet = an; paint(); spin(); };
v.addEventListener('play', () => { paint(); show(); });
v.addEventListener('pause', () => { waiting(false); show(); });
v.addEventListener('waiting', () => waiting(true));
v.addEventListener('playing', () => {
el2('pl-busy').classList.remove('on');
waiting(false);
layer.classList.remove('loading');
});
@@ -1814,7 +1846,7 @@ function openPlayer(item) {
lauf.stelle = v.currentTime || 0;
layer.querySelector('.pl-error')?.remove();
layer.classList.add('loading');
el2('pl-busy').classList.add('on');
waiting(true);
closeMenu();
startStream(item, v, fail, lauf);
};
@@ -1890,7 +1922,7 @@ function openPlayer(item) {
show();
layer.classList.add('loading');
el2('pl-busy').classList.add('on');
waiting(true);
startStream(item, v, fail, lauf);
}