patx/voldservices

fix scroll

Commit b03d641 · patx · 2026-08-19T00:58:21-04:00

Changeset
b03d64177e42b4ef6b44eb378ba6c6ab0edcfdcd
Parents
89fb6a47ee8f2c34992426eea383472f361a9cbe

View source at this commit

Comments

No comments yet.

Log in to comment

Diff

diff --git a/docs/index.html b/docs/index.html
index 4432000..538fb74 100644
--- a/docs/index.html
+++ b/docs/index.html
@@ -1442,14 +1442,13 @@
           <img class="hero-fallback" src="https://voldservices.com/img/hero-scrub-poster.jpg" alt="" aria-hidden="true" />
           <video
             id="hero-video"
+            data-desktop-src="/hero-scrub.mp4"
+            data-mobile-src="/hero-scrub-mobile.mp4"
             poster="https://voldservices.com/img/hero-scrub-poster.jpg"
             muted playsinline webkit-playsinline preload="auto"
             controlslist="nodownload nofullscreen noremoteplayback"
             disablepictureinpicture disableremoteplayback
-            aria-hidden="true" tabindex="-1">
-            <source media="(max-width: 720px)" src="https://voldservices.com/hero-scrub-mobile.mp4" type="video/mp4" />
-            <source src="https://voldservices.com/hero-scrub.mp4" type="video/mp4" />
-          </video>
+            aria-hidden="true" tabindex="-1"></video>
         </div>
         <div class="hero-grain" aria-hidden="true"></div>
 
@@ -2140,6 +2139,7 @@
       var heroVideoVisible = false;
       var heroVideoRevealPending = false;
       var heroVideoProgress = 0;
+      var heroVideoObjectUrl = '';
 
       function clamp01(value) {
         return Math.max(0, Math.min(1, value));
@@ -2238,6 +2238,41 @@
         if (!heroUsesTouch || heroTouchStarted) primeHeroVideo();
       }
 
+      function attachHeroVideoSource(source) {
+        heroVideo.src = source;
+        heroVideo.load();
+      }
+
+      /* The origin currently serves MP4 files without byte-range responses.
+         Mobile's smaller encode can finish downloading before that becomes
+         noticeable, but desktop seeks otherwise snap back to frame zero.
+         Buffer the desktop file into a blob so its full timeline is locally
+         seekable, keeping the poster visible until the first frame is ready. */
+      function loadHeroVideo() {
+        var mobileSource = window.innerWidth <= 720;
+        var source = mobileSource
+          ? heroVideo.getAttribute('data-mobile-src')
+          : heroVideo.getAttribute('data-desktop-src');
+
+        if (mobileSource || typeof window.fetch !== 'function' || typeof window.URL.createObjectURL !== 'function') {
+          attachHeroVideoSource(source);
+          return;
+        }
+
+        window.fetch(source, { cache: 'force-cache' })
+          .then(function (response) {
+            if (!response.ok) throw new Error('Hero video request failed');
+            return response.blob();
+          })
+          .then(function (blob) {
+            heroVideoObjectUrl = window.URL.createObjectURL(blob);
+            attachHeroVideoSource(heroVideoObjectUrl);
+          })
+          .catch(function () {
+            attachHeroVideoSource(source);
+          });
+      }
+
       if (reduceMotion) {
         heroVideo.pause();
       } else {
@@ -2248,8 +2283,14 @@
 
         // Cached media can reach metadata before this bottom-of-page script runs.
         if (heroVideo.readyState >= 1) prepareHeroVideo();
+
+        loadHeroVideo();
       }
 
+      window.addEventListener('beforeunload', function () {
+        if (heroVideoObjectUrl) window.URL.revokeObjectURL(heroVideoObjectUrl);
+      });
+
       // If the user scrolls again while a seek is being decoded, catch the
       // playhead up to the most recent position as soon as that frame lands.
       heroVideo.addEventListener('seeked', function () {