119 lines
4.5 KiB
JavaScript
119 lines
4.5 KiB
JavaScript
|
|
/* Stadionzeitung-Swipe-Viewer: rüstet den gestapelten No-JS-Fallback
|
||
|
|
([data-stadionzeitung-viewer]) auf einen horizontalen Swiper um. Kein
|
||
|
|
eigener Touch-Gesten-Code — natives CSS-Scroll-Snap (components.css) macht
|
||
|
|
das Swipen butterweich, JS liefert nur Zähler, Pfeiltasten und die
|
||
|
|
Vor-/Zurück-Buttons obendrauf. Ohne JS bleiben alle Seiten normal gestapelt. */
|
||
|
|
(function () {
|
||
|
|
'use strict';
|
||
|
|
|
||
|
|
function initViewer(root) {
|
||
|
|
var track = root.querySelector('[data-viewer-track]');
|
||
|
|
var pages = Array.prototype.slice.call(root.querySelectorAll('[data-viewer-page]'));
|
||
|
|
var controls = root.querySelector('[data-viewer-controls]');
|
||
|
|
var counter = root.querySelector('[data-viewer-current]');
|
||
|
|
var prevBtn = root.querySelector('[data-viewer-prev]');
|
||
|
|
var nextBtn = root.querySelector('[data-viewer-next]');
|
||
|
|
var fullBtn = root.querySelector('[data-viewer-full]');
|
||
|
|
|
||
|
|
if (!track || pages.length <= 1) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
var reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
||
|
|
var index = 0;
|
||
|
|
var ticking = false;
|
||
|
|
|
||
|
|
function updateUI() {
|
||
|
|
if (counter) {
|
||
|
|
counter.textContent = String(index + 1);
|
||
|
|
}
|
||
|
|
if (prevBtn) {
|
||
|
|
prevBtn.disabled = index === 0;
|
||
|
|
}
|
||
|
|
if (nextBtn) {
|
||
|
|
nextBtn.disabled = index === pages.length - 1;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
function scrollToIndex(i) {
|
||
|
|
index = Math.max(0, Math.min(pages.length - 1, i));
|
||
|
|
track.scrollTo({ left: index * track.clientWidth, behavior: reduce ? 'auto' : 'smooth' });
|
||
|
|
updateUI();
|
||
|
|
}
|
||
|
|
|
||
|
|
track.addEventListener('scroll', function () {
|
||
|
|
if (ticking) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
ticking = true;
|
||
|
|
requestAnimationFrame(function () {
|
||
|
|
index = Math.round(track.scrollLeft / track.clientWidth);
|
||
|
|
updateUI();
|
||
|
|
ticking = false;
|
||
|
|
});
|
||
|
|
}, { passive: true });
|
||
|
|
|
||
|
|
window.addEventListener('resize', function () {
|
||
|
|
// Re-snap ohne Animation, z. B. nach Rotation am Handy — sonst
|
||
|
|
// steht der Track auf einer krummen Zwischenposition.
|
||
|
|
track.scrollTo({ left: index * track.clientWidth, behavior: 'auto' });
|
||
|
|
});
|
||
|
|
|
||
|
|
if (prevBtn) {
|
||
|
|
prevBtn.addEventListener('click', function () { scrollToIndex(index - 1); });
|
||
|
|
}
|
||
|
|
if (nextBtn) {
|
||
|
|
nextBtn.addEventListener('click', function () { scrollToIndex(index + 1); });
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Vollbild-Lesemodus. Bewusst NICHT die Fullscreen-API: iOS Safari
|
||
|
|
unterstützt requestFullscreen() nur auf <video>, der Knopf wäre auf
|
||
|
|
iPhones wirkungslos. Zwei Klassen und CSS erledigen dasselbe überall
|
||
|
|
gleich — und die Adressleiste des Browsers bleibt erreichbar. */
|
||
|
|
function setFull(on) {
|
||
|
|
document.body.classList.toggle('is-sz-full', on);
|
||
|
|
root.classList.toggle('is-full', on);
|
||
|
|
if (fullBtn) {
|
||
|
|
fullBtn.setAttribute('aria-pressed', on ? 'true' : 'false');
|
||
|
|
}
|
||
|
|
// Die Track-Breite hat sich gerade geändert — ohne Neu-Einrasten
|
||
|
|
// stünde der Swiper auf einer krummen Zwischenposition.
|
||
|
|
requestAnimationFrame(function () {
|
||
|
|
track.scrollTo({ left: index * track.clientWidth, behavior: 'auto' });
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
if (fullBtn) {
|
||
|
|
fullBtn.addEventListener('click', function () {
|
||
|
|
setFull(!root.classList.contains('is-full'));
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
document.addEventListener('keydown', function (event) {
|
||
|
|
var active = document.activeElement;
|
||
|
|
if (active && /^(INPUT|TEXTAREA|SELECT)$/.test(active.tagName)) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
if (event.key === 'Escape' && root.classList.contains('is-full')) {
|
||
|
|
event.preventDefault();
|
||
|
|
setFull(false);
|
||
|
|
if (fullBtn) {
|
||
|
|
fullBtn.focus();
|
||
|
|
}
|
||
|
|
} else if (event.key === 'ArrowLeft') {
|
||
|
|
scrollToIndex(index - 1);
|
||
|
|
} else if (event.key === 'ArrowRight') {
|
||
|
|
scrollToIndex(index + 1);
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
root.classList.add('is-enhanced');
|
||
|
|
if (controls) {
|
||
|
|
controls.hidden = false;
|
||
|
|
}
|
||
|
|
updateUI();
|
||
|
|
}
|
||
|
|
|
||
|
|
Array.prototype.slice.call(document.querySelectorAll('[data-stadionzeitung-viewer]')).forEach(initViewer);
|
||
|
|
})();
|