perf: smoother video player — rAF-driven scrub bar, less cold-start stutter

scrub fill/time were driven off timeupdate, which browsers fire irregularly (a handful of times a second) — visible as the bar stepping instead of gliding. now driven by requestAnimationFrame while playing (display refresh rate), with a direct sync on pause/seeked so it still tracks immediately when not running.\n\npreload=none meant zero data was fetched until the play button was pressed, causing a visible stall right at playback start. preload=metadata lets the browser prime the connection and read the moov atom ahead of time without fetching the full video for visitors who never press play.
This commit is contained in:
kgva CMS
2026-08-15 00:24:17 +00:00
parent dd915e1797
commit 46aff56577
2 changed files with 21 additions and 5 deletions
+20 -4
View File
@@ -23,14 +23,30 @@
if (big) big.addEventListener("click", toggle);
v.addEventListener("click", toggle);
v.addEventListener("play", function () { root.classList.add("is-playing"); if (play) play.textContent = "❚❚"; });
v.addEventListener("pause", function () { root.classList.remove("is-playing"); if (play) play.textContent = "▶"; });
v.addEventListener("loadedmetadata", function () { if (time) time.textContent = "00:00 / " + fmt(v.duration); });
v.addEventListener("timeupdate", function () {
// drive the scrub bar off rAF instead of `timeupdate` — timeupdate only
// fires a handful of times a second (browser-dependent), which made the
// fill visibly step instead of glide; rAF ties it to the display's own
// refresh rate, only running while actually playing.
var raf = null;
function sync() {
var p = v.duration ? v.currentTime / v.duration : 0;
if (fill) fill.style.width = (p * 100) + "%";
if (time) time.textContent = fmt(v.currentTime) + " / " + fmt(v.duration || 0);
}
function tick() { sync(); raf = requestAnimationFrame(tick); }
v.addEventListener("play", function () {
root.classList.add("is-playing"); if (play) play.textContent = "❚❚";
if (!raf) raf = requestAnimationFrame(tick);
});
v.addEventListener("pause", function () {
root.classList.remove("is-playing"); if (play) play.textContent = "▶";
if (raf) { cancelAnimationFrame(raf); raf = null; }
sync(); // one last sync so the bar lands exactly on the paused frame
});
v.addEventListener("ended", function () { if (raf) { cancelAnimationFrame(raf); raf = null; } });
v.addEventListener("loadedmetadata", function () { if (time) time.textContent = "00:00 / " + fmt(v.duration); });
v.addEventListener("seeked", sync); // scrubbing while paused still needs to reflect immediately
if (scrub) scrub.addEventListener("click", function (e) {
var r = scrub.getBoundingClientRect();
if (v.duration) v.currentTime = ((e.clientX - r.left) / r.width) * v.duration;