diff --git a/frontend/fullscreen-orientation.css b/frontend/fullscreen-orientation.css index 1c2308f..8722cb6 100644 --- a/frontend/fullscreen-orientation.css +++ b/frontend/fullscreen-orientation.css @@ -69,3 +69,7 @@ /* Raise existing ancestry instead of moving a playing video in WebKit. */ body.landscape-fs .orientation-ancestor { z-index: 10001 !important; transform: none !important; filter: none !important; overflow: visible !important; } body.landscape-fs { overflow: hidden; } +/* While the app's own fullscreen is up, nothing of the app may show around or over it. */ +body.landscape-fs .orientation-ancestor { -webkit-backdrop-filter: none !important; backdrop-filter: none !important; contain: none !important; will-change: auto !important; perspective: none !important; clip-path: none !important; isolation: auto !important; } +body.landscape-fs :is(.topbar, #bottomNav, #miniBar, #sectionRail, #toastContainer, .sheet-backdrop, .f7s-sheet) { visibility: hidden !important; } +body.landscape-fs > .player-stage.orientation-overlay { z-index: 2147483000 !important; } diff --git a/frontend/fullscreen-orientation.js b/frontend/fullscreen-orientation.js index 28e0601..c7e5ee9 100644 --- a/frontend/fullscreen-orientation.js +++ b/frontend/fullscreen-orientation.js @@ -17,6 +17,12 @@ let active = false, custom = false, native = false, locked = false, angle = 0, timer, hideTimer; const ancestors = []; for (let el = stage.parentElement; el && el !== document.body; el = el.parentElement) ancestors.push(el); + // iOS (no element fullscreen) draws its own fixed overlay. Any ancestor with a filter, + // backdrop-filter, containment or clipping traps a fixed child, which showed the app + // around the video on iPhone. Lift the stage to for the overlay and put it back after. + let home = null; + function lift() { if (home || stage.parentElement === document.body) return; home = { parent: stage.parentElement, next: stage.nextSibling }; document.body.append(stage); } + function unlift() { if (!home) return; const { parent, next } = home; home = null; if (next && next.parentNode === parent) parent.insertBefore(stage, next); else parent.append(stage); } const hud = document.createElement('div'); hud.className = 'fullscreen-hud'; hud.hidden = true; hud.innerHTML = '
'; stage.append(hud); @@ -56,7 +62,7 @@ const value = normalize(settings().fullscreenOrientation); if (!custom && value !== 'auto') { try { await screen.orientation.lock(value); locked = true; if (!active) { screen.orientation.unlock(); locked = false; return; } } - catch { if (!active) return; custom = true; stage.classList.add('orientation-overlay'); } + catch { if (!active) return; custom = true; lift(); stage.classList.add('orientation-overlay'); } } layout(); } @@ -77,6 +83,7 @@ hud.hidden = true; hud.classList.remove('hud-hidden'); stage.classList.remove('orientation-overlay', 'orientation-fullscreen'); delete stage.dataset.fullscreenRotation; document.body.classList.remove('landscape-fs'); delete stage.dataset.fullscreenFit; + unlift(); if (locked) { screen.orientation?.unlock?.(); locked = false; } } function leave() { @@ -92,7 +99,7 @@ if (!stage.requestFullscreen) throw new Error('Custom fullscreen'); await stage.requestFullscreen(); native = true; - } catch { custom = true; stage.classList.add('orientation-overlay'); } + } catch { custom = true; lift(); stage.classList.add('orientation-overlay'); } if (!active) return; await orient(); if (!active) return;