Changeset c0e463b in Klonkt for src/assets/js/audio-player.js
- Timestamp:
- 08/22/2026 11:05:19 AM (3 weeks ago)
- Branches:
- main
- Children:
- 5e72413
- Parents:
- 4fbe2c1
- File:
-
- 1 edited
-
src/assets/js/audio-player.js (modified) (10 diffs)
Legend:
- Unmodified
- Added
- Removed
-
src/assets/js/audio-player.js
r4fbe2c1 rc0e463b 27 27 vol: '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M3 9v6h4l5 5V4L7 9H3zM16 8a5 5 0 010 8M19 5a9 9 0 010 14" stroke="currentColor" stroke-width="1.8" fill="none" stroke-linecap="round" stroke-linejoin="round"/></svg>', 28 28 mute: '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M3 9v6h4l5 5V4L7 9H3zM17 9l5 5M22 9l-5 5" stroke="currentColor" stroke-width="1.8" fill="none" stroke-linecap="round" stroke-linejoin="round"/></svg>', 29 // Dubbele driehoeken: het teken voor spoelen, niet voor overslaan. 30 rew: '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M11 12l9-7v14zM2 12l9-7v14z" fill="currentColor"/></svg>', 31 ff: '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M13 12L4 5v14zM22 12l-9-7v14z" fill="currentColor"/></svg>', 29 32 musicNote: '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M9 17V5l12-2v12" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><circle cx="6" cy="17" r="3" fill="currentColor"/><circle cx="18" cy="15" r="3" fill="currentColor"/></svg>', 30 33 }; … … 128 131 const prevBtn = $('audio-prev'); 129 132 const nextBtn = $('audio-next'); 133 // De vier knoppen die in bandmodus van betekenis veranderen: vorige/volgende 134 // worden terugspoelen/vooruitspoelen. Ze staan hier bij elkaar zodat het 135 // omzetten op EEN plek gebeurt. 136 const SPOELKNOPPEN = ['audio-prev', 'audio-next', 'audio-sheet-prev', 'audio-sheet-next']; 130 137 const muteBtn = $('audio-mute'); 131 138 const volumeSlider = $('audio-volume'); … … 491 498 } 492 499 500 // De lengte van de hele band, uit de bekende trackduren. Null zodra er van 501 // een nummer geen duur bekend is: een som met gaten is een verzonnen getal, 502 // en dan valt de teller liever terug op wat hij wel zeker weet. 503 function bandDuur() { 504 if (!queue.length) return null; 505 let som = 0; 506 for (const t of queue) { 507 const d = Number(t && t.duration) || 0; 508 if (d <= 0) return null; 509 som += d; 510 } 511 return som; 512 } 513 514 /** Hoeveel band ligt er voor nummer `i`. */ 515 function bandOffset(i) { 516 let som = 0; 517 for (let n = 0; n < i && n < queue.length; n++) som += Number(queue[n].duration) || 0; 518 return som; 519 } 520 493 521 function displayTimes() { 494 // Een bandje heeft een teller, geen nummerpositie: hij telt door over de 495 // kant heen. Dat is letterlijk de ketentijdlijn, dus hier juist NIET 496 // terugrekenen naar trackcoordinaten. 497 if (tapeMode && useMse() && chain.length) { 498 return { cur: audio.currentTime || 0, dur: chain[chain.length - 1].end }; 522 // EEN BANDJE HEEFT EEN TELLER, geen nummerpositie. Hij telt door over de 523 // kant heen. 524 // 525 // Uit de trackduren en niet uit de keten, en dat verschil is zichtbaar: de 526 // keten bevat alleen wat gebufferd is, dus het totaal groeide mee tijdens 527 // het luisteren (2:05 met een nummer geladen, 4:07 met drie). En sprong je 528 // naar nummer drie, dan begon de keten daar opnieuw op nul en stond de 529 // teller weer aan het begin van de band. 530 if (tapeMode) { 531 const totaal = bandDuur(); 532 if (totaal) return { cur: bandOffset(currentIndex) + (trackTijd().cur || 0), dur: totaal }; 533 if (useMse() && chain.length) return { cur: audio.currentTime || 0, dur: chain[chain.length - 1].end }; 499 534 } 500 535 return trackTijd(); … … 621 656 albumName = (opts && opts.albumName) || ''; 622 657 tapeMode = !!(opts && opts.asTape); 658 zetKnopStanden(); 623 659 if (!queue.length) return; 624 660 loadTrack(typeof startIdx === 'number' ? Math.max(0, Math.min(startIdx, queue.length - 1)) : 0, true); … … 681 717 albumName = ''; 682 718 tapeMode = false; 719 zetKnopStanden(); 683 720 loadSeq++; // cancel any in-flight load 684 721 dropPreload(); … … 700 737 sheetQueueList.appendChild(li); 701 738 }); 739 // OP EEN BANDJE KIES JE NIET. De lijst blijft staan -- je mag zien wat 740 // erop staat -- maar zonder klik en zonder de opmaak die belooft dat het 741 // kan. Dat is hetzelfde onderscheid als bij de cassette in de post. 742 sheetQueueList.classList.toggle('is-tape', tapeMode); 743 if (tapeMode) return; 702 744 sheetQueueList.querySelectorAll('.audio-sheet-queue-item').forEach((li) => { 703 745 li.addEventListener('click', () => { … … 883 925 // ============================================================ 884 926 playBtn.addEventListener('click', togglePlay); 885 prevBtn.addEventListener('click', prev); 886 nextBtn.addEventListener('click', next); 927 /** 928 * De vier knoppen in de juiste stand zetten. In bandmodus zijn het 929 * spoelknoppen: ander teken, ander woord, en vasthouden spoelt door. 930 */ 931 function zetKnopStanden() { 932 for (const id of SPOELKNOPPEN) { 933 const b = $(id); 934 if (!b) continue; 935 const vooruit = id.endsWith('next'); 936 b.innerHTML = tapeMode ? (vooruit ? SVG.ff : SVG.rew) : (vooruit ? SVG.next : SVG.prev); 937 const label = tapeMode ? (vooruit ? 'Vooruitspoelen' : 'Terugspoelen') : (vooruit ? 'Volgende' : 'Vorige'); 938 b.setAttribute('aria-label', label); 939 b.setAttribute('title', label); 940 b.classList.toggle('is-wind', tapeMode); 941 } 942 } 943 944 // Een KLIK: een nummer verder, of in bandmodus een stukje spoelen. Dat laatste 945 // is er voor toetsenbord en schermlezer, want vasthouden is met een 946 // spatiebalk geen gebaar. 947 const TIK_SPOEL_S = 5; 948 prevBtn.addEventListener('click', () => (tapeMode ? seekBy(-TIK_SPOEL_S) : prev())); 949 nextBtn.addEventListener('click', () => (tapeMode ? seekBy(TIK_SPOEL_S) : next())); 887 950 sheetPlay.addEventListener('click', togglePlay); 888 sheetPrev.addEventListener('click', prev); 889 sheetNext.addEventListener('click', next); 951 sheetPrev.addEventListener('click', () => (tapeMode ? seekBy(-TIK_SPOEL_S) : prev())); 952 sheetNext.addEventListener('click', () => (tapeMode ? seekBy(TIK_SPOEL_S) : next())); 953 954 // VASTHOUDEN spoelt door. Op de knop zelf indrukken, maar loslaten op 955 // document: glijdt je vinger van de knop af, dan hoort de band te stoppen en 956 // niet door te blijven spoelen. 957 for (const id of SPOELKNOPPEN) { 958 const b = $(id); 959 if (!b) continue; 960 b.addEventListener('pointerdown', () => { if (tapeMode) startWind(id.endsWith('next') ? 1 : -1); }); 961 } 962 document.addEventListener('pointerup', stopWind); 963 document.addEventListener('pointercancel', stopWind); 890 964 891 965 // ============================================================ … … 1199 1273 } 1200 1274 1275 /** 1276 * SPOELEN, met de knop ingedrukt. 1277 * 1278 * Stond eerst in assets/js/mod/tape.js, want daar zat de cassette. Nu de 1279 * speler zelf ook spoelknoppen krijgt zou dezelfde lus op twee plekken staan, 1280 * en dan lopen ze uit elkaar zodra iemand aan de versnelling draait. Hier is 1281 * de enige plek; de cassette op de pagina roept deze aan. 1282 */ 1283 const WIND_START = 4; // maal de normale snelheid bij het indrukken 1284 const WIND_MAX = 16; 1285 const WIND_VERSNELLING = 1.35; 1286 const WIND_STAP_MS = 120; 1287 let winder = null; 1288 1289 function stopWind() { 1290 if (winder) { clearInterval(winder.timer); winder = null; } 1291 } 1292 1293 function startWind(richting) { 1294 stopWind(); 1295 const r = richting < 0 ? -1 : 1; 1296 let snelheid = WIND_START; 1297 let vorige = null; 1298 let stil = 0; 1299 const timer = setInterval(() => { 1300 snelheid = Math.min(WIND_MAX, snelheid * WIND_VERSNELLING); 1301 seekBy(r * snelheid * (WIND_STAP_MS / 1000)); 1302 // AAN DE KOP EN DE STAART STOPPEN. De positie wordt afgeklemd, dus daar 1303 // gebeurt niets meer -- maar zonder deze controle blijft de knop malen en 1304 // draaien de spoelen door alsof er nog band is. 1305 const nu = displayTimes().cur; 1306 if (vorige !== null && Math.abs(nu - vorige) < 0.05) { 1307 if (++stil >= 2) { stopWind(); return; } 1308 } else stil = 0; 1309 vorige = nu; 1310 }, WIND_STAP_MS); 1311 winder = { timer, richting: r }; 1312 } 1313 1201 1314 /** Waar staat de teller, over het hele bandje. */ 1202 1315 function tapeTijden() { return displayTimes(); } … … 1204 1317 window.pcmsAudioPlayer = { 1205 1318 setQueue, play, pause, next, prev, close, openSheet, closeSheet, seekBy, tapeTijden, 1319 startWind, stopWind, 1206 1320 isTape: () => tapeMode, 1321 isWinding: () => !!winder, 1207 1322 isPlaying: () => isPlaying, 1208 1323 currentTrack: () => queue[currentIndex] || null, … … 1252 1367 albumName = s.albumName || ''; 1253 1368 tapeMode = !!s.tapeMode; 1369 zetKnopStanden(); 1254 1370 pendingSeek = s.time || 0; 1255 1371 const idx = Math.max(0, Math.min(s.currentIndex || 0, queue.length - 1));
Note:
See TracChangeset
for help on using the changeset viewer.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)