diff options
Diffstat (limited to 'public/js/slideshow.js')
| -rw-r--r-- | public/js/slideshow.js | 113 |
1 files changed, 113 insertions, 0 deletions
diff --git a/public/js/slideshow.js b/public/js/slideshow.js new file mode 100644 index 0000000..2260034 --- /dev/null +++ b/public/js/slideshow.js @@ -0,0 +1,113 @@ +'use strict'; + +let ssIdx = 0; +let ssTimer = null; +let ssPlaying = true; + +const img = document.getElementById('ss-img'); +const vid = document.getElementById('ss-video'); +const title = document.getElementById('ss-title'); +const cap = document.getElementById('ss-caption'); +const ctr = document.getElementById('ss-counter'); +const btn = document.getElementById('ss-play-btn'); + +function ssInterval() { + return (parseFloat(document.getElementById('ss-interval').value) || 4) * 1000; +} + +function ssShow(i, instant) { + const e = SS_ENTRIES[i]; + if (!e) return; + ssIdx = i; + + title.textContent = e.title !== e.name ? e.title : ''; + cap.textContent = e.caption || ''; + ctr.textContent = `${i + 1} / ${SS_ENTRIES.length}`; + + if (instant) { + // First load — show without transition + applyEntry(e); + return; + } + + if (e.type === 'video') { + // No preload possible for video; fade out then swap + crossFade(() => applyEntry(e)); + } else { + // Preload the image so the fade-in shows it immediately + const preload = new Image(); + preload.onload = preload.onerror = () => crossFade(() => applyEntry(e)); + preload.src = e.src; + } +} + +function applyEntry(e) { + if (e.type === 'video') { + img.style.display = 'none'; + vid.style.display = ''; + vid.src = e.src; + void vid.offsetWidth; // flush so transition fires + vid.classList.remove('fading'); + vid.play().catch(() => {}); + } else { + vid.pause && vid.pause(); + vid.style.display = 'none'; + img.style.display = ''; + img.src = e.src; + img.alt = e.title; + void img.offsetWidth; // flush so transition fires + img.classList.remove('fading'); + } +} + +function crossFade(cb) { + img.classList.add('fading'); + vid.classList.add('fading'); + setTimeout(cb, 500); // matches CSS transition duration +} + +function ssSchedule() { + clearTimeout(ssTimer); + if (ssPlaying && SS_ENTRIES.length > 1) { + ssTimer = setTimeout(() => { + ssShow((ssIdx + 1) % SS_ENTRIES.length); + ssSchedule(); + }, ssInterval()); + } +} + +function ssNext() { + ssShow((ssIdx + 1) % SS_ENTRIES.length); + if (ssPlaying) ssSchedule(); +} + +function ssPrev() { + ssShow((ssIdx - 1 + SS_ENTRIES.length) % SS_ENTRIES.length); + if (ssPlaying) ssSchedule(); +} + +function ssToggle() { + ssPlaying = !ssPlaying; + btn.textContent = ssPlaying ? '⏸ Pause' : '▶ Play'; + if (ssPlaying) ssSchedule(); else clearTimeout(ssTimer); +} + +document.addEventListener('keydown', e => { + if (e.key === 'ArrowRight') ssNext(); + if (e.key === 'ArrowLeft') ssPrev(); + if (e.key === ' ') { e.preventDefault(); ssToggle(); } +}); + +let swipeX = null; +document.addEventListener('touchstart', e => { swipeX = e.changedTouches[0].clientX; }, { passive: true }); +document.addEventListener('touchend', e => { + if (swipeX === null) return; + const dx = e.changedTouches[0].clientX - swipeX; + if (Math.abs(dx) > 50) (dx < 0 ? ssNext : ssPrev)(); + swipeX = null; +}, { passive: true }); + +if (SS_ENTRIES.length > 0) { + ssShow(0, true); + ssSchedule(); +} |
