patx/demo

optimize mobile scroll no video controls

Commit 5888b61 · patx · 2026-08-16T02:38:58-04:00

Changeset
5888b61c827437450953e382418e889f43a64cba
Parents
8ebb1ca83f43515b67f33afa761bfadf6914328f

View source at this commit

Comments

No comments yet.

Log in to comment

Diff

diff --git a/docs/voldservices/index.html b/docs/voldservices/index.html
index 2be7ff9..f8371f8 100644
--- a/docs/voldservices/index.html
+++ b/docs/voldservices/index.html
@@ -423,9 +423,19 @@
     .hero-media video {
       z-index: 1;
       opacity: 0;
+      pointer-events: none;
       transition: opacity .18s ease-out;
     }
     .hero-media.is-video-ready video { opacity: 1; }
+    .hero-media video::-webkit-media-controls,
+    .hero-media video::-webkit-media-controls-panel,
+    .hero-media video::-webkit-media-controls-play-button,
+    .hero-media video::-webkit-media-controls-start-playback-button {
+      display: none !important;
+      -webkit-appearance: none;
+      opacity: 0;
+      pointer-events: none;
+    }
     .hero-media::after {
       content: "";
       position: absolute;
@@ -1270,7 +1280,9 @@
           <video
             id="hero-video"
             poster="https://gitman.io/patx/demo/raw/docs/voldservices/img/hero-scrub-poster.jpg"
-            muted playsinline webkit-playsinline autoplay preload="auto"
+            muted playsinline webkit-playsinline preload="auto"
+            controlslist="nodownload nofullscreen noremoteplayback"
+            disablepictureinpicture disableremoteplayback
             aria-hidden="true" tabindex="-1">
             <source media="(max-width: 720px)" src="https://gitman.io/patx/demo/raw/docs/voldservices/hero-scrub-mobile.mp4" type="video/mp4" />
             <source src="https://gitman.io/patx/demo/raw/docs/voldservices/hero-scrub.mp4" type="video/mp4" />
@@ -1929,6 +1941,8 @@
       var heroVideoReady = false;
       var heroVideoPrimed = false;
       var heroVideoPrimePending = false;
+      var heroTouchStarted = false;
+      var heroUsesTouch = ('ontouchstart' in window) || navigator.maxTouchPoints > 0;
       var heroVideoVisible = false;
       var heroVideoRevealPending = false;
       var heroVideoProgress = 0;
@@ -1989,29 +2003,23 @@
 
       function finishHeroVideoPrime() {
         heroVideo.pause();
-        heroVideo.autoplay = false;
-        heroVideo.removeAttribute('autoplay');
         heroVideoPrimePending = false;
         heroVideoPrimed = true;
         heroVideoReady = true;
-        window.removeEventListener('touchstart', primeHeroVideo);
+        window.removeEventListener('touchstart', primeHeroVideoFromTouch);
         syncHeroVideo(heroVideoProgress, true);
       }
 
       function usePausedSeekFallback() {
         heroVideo.pause();
-        heroVideo.autoplay = false;
-        heroVideo.removeAttribute('autoplay');
         heroVideoPrimePending = false;
         heroVideoReady = true;
         syncHeroVideo(heroVideoProgress, true);
       }
 
       // iOS may load metadata for a muted video without activating its decoder.
-      // A brief hidden play/pause primes frame presentation so later paused
-      // currentTime changes repaint while the user scrolls. Muted inline play
-      // is allowed automatically; the first touch provides a second chance
-      // when Low Power Mode or a browser policy blocks autoplay.
+      // On touch devices, a brief hidden play/pause on the first scroll touch
+      // primes frame presentation without autoplay or visible player chrome.
       function primeHeroVideo() {
         if (reduceMotion || heroVideoPrimed || heroVideoPrimePending || heroVideo.readyState < 1) return;
 
@@ -2027,16 +2035,25 @@
         }
       }
 
+      function primeHeroVideoFromTouch() {
+        heroTouchStarted = true;
+        primeHeroVideo();
+      }
+
+      function prepareHeroVideo() {
+        if (!heroUsesTouch || heroTouchStarted) primeHeroVideo();
+      }
+
       if (reduceMotion) {
-        heroVideo.autoplay = false;
-        heroVideo.removeAttribute('autoplay');
         heroVideo.pause();
       } else {
-        heroVideo.addEventListener('loadedmetadata', primeHeroVideo);
-        window.addEventListener('touchstart', primeHeroVideo, { passive: true });
+        heroVideo.addEventListener('loadedmetadata', prepareHeroVideo);
+        if (heroUsesTouch) {
+          window.addEventListener('touchstart', primeHeroVideoFromTouch, { passive: true });
+        }
 
         // Cached media can reach metadata before this bottom-of-page script runs.
-        if (heroVideo.readyState >= 1) primeHeroVideo();
+        if (heroVideo.readyState >= 1) prepareHeroVideo();
       }
 
       // If the user scrolls again while a seek is being decoded, catch the