/* * Schleifenvideos: im Hintergrund einer Sektion oder im Video-Baustein. * * Abgespielt wird erst von hier aus, nicht mit autoplay im HTML. So lädt die Seite zuerst ihren * Inhalt, das Video folgt danach (preload="none"), und es läuft nur, solange es zu sehen ist. * Wer „Bewegung reduzieren“ eingestellt hat oder Daten sparen will, bekommt das Standbild und kann * das Video selbst starten. Der Knopf gehört dazu: Bewegung, die länger als fünf Sekunden läuft, * muss sich anhalten lassen (WCAG 2.2.2). Ohne Skript bleibt das Standbild. */ const reduced = matchMedia('(prefers-reduced-motion: reduce)'); const saving = navigator.connection?.saveData === true; for (const video of document.querySelectorAll('[data-video-loop]')) { const toggle = video.parentElement.querySelector(':scope > .video-toggle'); // Ein data-video-loop aus „Eigenes HTML“ hat keinen Knopf; ohne ihn bleibt es beim Standbild. if (!toggle) continue; let wanted = !reduced.matches && !saving; let visible = false; const paint = () => { toggle.classList.toggle('is-paused', !wanted); toggle.setAttribute('aria-label', wanted ? toggle.dataset.pause : toggle.dataset.play); }; const sync = () => { if (!wanted || !visible) { video.pause(); return; } video.play().catch(error => { // Ein Anhalten mitten im Starten ist kein Fehler. Verweigert der Browser das Abspielen, // etwa im Stromsparmodus, zeigt der Knopf die Wahrheit. if (error.name === 'NotAllowedError') { wanted = false; paint(); } }); }; toggle.hidden = false; toggle.addEventListener('click', () => { wanted = !wanted; paint(); sync(); }); reduced.addEventListener('change', () => { if (reduced.matches) { wanted = false; paint(); sync(); } }); new IntersectionObserver(([entry]) => { visible = entry.isIntersecting; sync(); }).observe(video); paint(); }