Adaptive Morphing
Learning Byte Summary ▾
// Serialize seeks so repeated back-and-forth clicks don’t thrash the player. let seekingLock = false; let resumeAfterSeek = false;
video.addEventListener(‘error’, () => { const e = video.error; console.warn(‘Video error:’, e && e.code, e); });
function clampTime(t) { const dur = video.duration || 0; return Math.max(0, Math.min(t, Math.max(0, dur - 0.05))); }
function seekTo(t) { if (!Number.isFinite(video.duration) || video.duration <= 0) { video.addEventListener(‘loadedmetadata’, () => seekTo(t), { once: true }); try { video.load(); } catch (_) {} return; }
// If a seek is already in-flight, wait for it to complete before starting a new one.
if (seekingLock) {
// Queue a follow-up seek once current one completes.
video.addEventListener('seeked', () => seekTo(t), { once: true });
return;
}
seekingLock = true;
// Remember whether we were playing before the seek
const wasPlaying = !video.paused && !video.ended;
resumeAfterSeek = wasPlaying || true; // resume even if paused to feel snappy on chapter click
const target = clampTime(t);
// Do the actual seek
if (typeof video.fastSeek === 'function') {
try { video.fastSeek(target); } catch (_) { video.currentTime = target; }
} else {
video.currentTime = target;
}
// Only resume after the browser confirms the seek completed
video.addEventListener('seeked', () => {
seekingLock = false;
if (resumeAfterSeek) {
// Small microtask helps some browsers after currentTime changes
Promise.resolve().then(() => video.play()).catch(() => {});
}
highlightActiveButton();
}, { once: true }); }
function highlightActiveButton() { const ct = video.currentTime || 0; let best = 0; for (let i = 0; i < buttons.length; i++) { const mark = parseFloat(buttons[i].dataset.time) || 0; if (ct + 0.01 >= mark) best = i; } buttons.forEach((b, i) => b.classList.toggle(‘active’, i === best)); }
// Bind clicks buttons.forEach(btn => { btn.addEventListener(‘click’, () => { const t = parseFloat(btn.dataset.time); if (Number.isFinite(t)) seekTo(t); }); });
// Keep the button highlight in sync video.addEventListener(‘timeupdate’, highlightActiveButton); video.addEventListener(‘loadedmetadata’, highlightActiveButton);
// If metadata already available (bfcache/back nav), initialize immediately if (video.readyState >= 1) highlightActiveButton(); })(); </script>