feat(v3): Intro auf MP4 umgestellt (video statt HTML-Bundle)
- static/intro.html entfernt, static/intro.mp4 als Quelle - Overlay nutzt <video> autoplay/muted, Ausblenden exakt per 'ended'-Event - Replay-Button z-index-Fix (lag hinter Streamlit-Header) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
7e0866ba4b
commit
c2b2589dd0
|
|
@ -1,23 +1,26 @@
|
|||
import streamlit.components.v1 as components
|
||||
|
||||
# Animationslaenge 16,4 s + ~1 s Puffer fuers "Unpacking" der Bundler-Animation.
|
||||
INTRO_DURATION_MS = 17400
|
||||
# Statische URL (Streamlit Static Serving). Beim Live-Test ggf. auf "./static/intro.html"
|
||||
# oder "/static/intro.html" korrigieren, falls "app/static/..." 404 liefert.
|
||||
_INTRO_SRC = "app/static/intro.html"
|
||||
# Sicherheitsnetz-Timer (ms): das Ausblenden geschieht primaer ueber das 'ended'-Event des
|
||||
# Videos (exakt am Ende). Dieser Timer greift nur, falls 'ended' mal nicht feuert
|
||||
# (z. B. Autoplay blockiert). Grosszuegig ueber der Videolaenge (~15,8 s) waehlen.
|
||||
INTRO_FALLBACK_MS = 20000
|
||||
# Statische URL (Streamlit Static Serving). Beim Live-Test ggf. auf "./static/intro.mp4"
|
||||
# oder "/static/intro.mp4" korrigieren, falls "app/static/..." 404 liefert.
|
||||
_INTRO_SRC = "app/static/intro.mp4"
|
||||
_SEEN_KEY = "einrechnung_intro_seen"
|
||||
|
||||
|
||||
def render_intro_overlay(duration_ms: int = INTRO_DURATION_MS,
|
||||
def render_intro_overlay(fallback_ms: int = INTRO_FALLBACK_MS,
|
||||
intro_src: str = _INTRO_SRC,
|
||||
seen_key: str = _SEEN_KEY) -> None:
|
||||
"""Injiziert ein einmaliges Intro-Overlay in die Streamlit-Elternseite (Ansatz A).
|
||||
"""Injiziert ein einmaliges Intro-Overlay (MP4) in die Streamlit-Elternseite (Ansatz A).
|
||||
|
||||
Ein unsichtbarer components.html-Baustein (Hoehe 0) fuehrt JS aus, das ein
|
||||
position:fixed-Overlay in window.parent.document einfuegt - ein echtes Overlay ueber der
|
||||
Seite. Autoplay nur einmal pro Browser (localStorage), "Ueberspringen"-Button + Auto-Fade,
|
||||
Replay-Button oben rechts (aendert den "gesehen"-Merker nicht). Die Animation laeuft
|
||||
isoliert in einem inneren iframe (eigenes CSS/JS, keine Kollision mit Streamlit).
|
||||
Seite. Die Animation ist ein <video> (autoplay, muted, ohne Steuerleiste, ohne Loop):
|
||||
Ausblenden exakt am Ende via 'ended'-Event (Timer nur als Sicherheitsnetz). Autoplay nur
|
||||
einmal pro Browser (localStorage), "Ueberspringen"-Button + Replay-Button oben rechts
|
||||
(Replay aendert den "gesehen"-Merker nicht).
|
||||
"""
|
||||
html = f"""
|
||||
<script>
|
||||
|
|
@ -26,14 +29,14 @@ def render_intro_overlay(duration_ms: int = INTRO_DURATION_MS,
|
|||
if (window.parent.__introInstalled) return; // Rerun-Schutz: nur einmal pro Seitenladen
|
||||
window.parent.__introInstalled = true;
|
||||
|
||||
var DURATION = {duration_ms};
|
||||
var FALLBACK = {fallback_ms};
|
||||
var SRC = {intro_src!r};
|
||||
var SEEN_KEY = {seen_key!r};
|
||||
|
||||
var style = doc.createElement('style');
|
||||
style.textContent = `
|
||||
#eir-backdrop {{
|
||||
position: fixed; inset: 0; z-index: 100000;
|
||||
position: fixed; inset: 0; z-index: 2147483646;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
background: rgba(8,8,10,0.82); -webkit-backdrop-filter: blur(2px); backdrop-filter: blur(2px);
|
||||
opacity: 0; transition: opacity .45s ease; pointer-events: none;
|
||||
|
|
@ -46,7 +49,7 @@ def render_intro_overlay(duration_ms: int = INTRO_DURATION_MS,
|
|||
box-shadow: 0 24px 80px rgba(0,0,0,0.6), 0 0 0 1px rgba(245,126,26,0.25);
|
||||
}}
|
||||
@media (max-width: 820px) {{ #eir-panel {{ width: 92vw; }} }}
|
||||
#eir-frame {{ width: 100%; height: 100%; border: 0; display: block; background: #0a0a0a; }}
|
||||
#eir-video {{ width: 100%; height: 100%; display: block; object-fit: contain; background: #0a0a0a; }}
|
||||
#eir-skip {{
|
||||
position: absolute; top: 10px; right: 12px; z-index: 2;
|
||||
font: 600 13px/1 -apple-system, BlinkMacSystemFont, sans-serif;
|
||||
|
|
@ -55,7 +58,7 @@ def render_intro_overlay(duration_ms: int = INTRO_DURATION_MS,
|
|||
}}
|
||||
#eir-skip:hover {{ background: rgba(0,0,0,0.7); }}
|
||||
#eir-replay {{
|
||||
position: fixed; top: 14px; right: 16px; z-index: 99999;
|
||||
position: fixed; top: 12px; right: 14px; z-index: 2147483647;
|
||||
width: 40px; height: 40px; border-radius: 50%;
|
||||
display: flex; align-items: center; justify-content: center; cursor: pointer;
|
||||
background: #17171a; border: 1px solid rgba(245,126,26,0.55);
|
||||
|
|
@ -77,45 +80,30 @@ def render_intro_overlay(duration_ms: int = INTRO_DURATION_MS,
|
|||
backdrop.innerHTML =
|
||||
'<div id="eir-panel">' +
|
||||
'<div id="eir-skip">Überspringen ✕</div>' +
|
||||
'<iframe id="eir-frame" title="Intro-Animation"></iframe>' +
|
||||
'<video id="eir-video" muted playsinline preload="none" src="' + SRC + '"></video>' +
|
||||
'</div>';
|
||||
doc.body.appendChild(backdrop);
|
||||
|
||||
var frame = doc.getElementById('eir-frame');
|
||||
var video = doc.getElementById('eir-video');
|
||||
var skip = doc.getElementById('eir-skip');
|
||||
var hideTimer = null;
|
||||
|
||||
// Der Bundler-Export bringt eine eigene Player-Leiste mit (Play/Pause, Zeitleiste,
|
||||
// "Export video"). Fuer ein sauberes Intro blenden wir sie aus. Die Leiste ist das
|
||||
// Eltern-Element der Transport-Buttons und wird evtl. asynchron aufgebaut -> Retry.
|
||||
function stripChrome() {{
|
||||
var idoc;
|
||||
try {{ idoc = frame.contentDocument; }} catch (e) {{ return; }}
|
||||
if (!idoc || (frame.src || '').indexOf('about:blank') !== -1) return;
|
||||
var tries = 0;
|
||||
(function attempt() {{
|
||||
var b;
|
||||
try {{ b = idoc.querySelector('button[aria-label*="lay"], button[title*="lay"], button'); }} catch (e) {{ return; }}
|
||||
if (b && b.parentElement) {{ b.parentElement.style.display = 'none'; return; }}
|
||||
if (tries++ < 40) setTimeout(attempt, 150); // bis zu ~6 s auf asynchrone Player-UI warten
|
||||
}})();
|
||||
}}
|
||||
frame.addEventListener('load', stripChrome);
|
||||
|
||||
function hide() {{
|
||||
clearTimeout(hideTimer);
|
||||
backdrop.classList.remove('eir-show');
|
||||
setTimeout(function() {{ frame.src = 'about:blank'; }}, 500);
|
||||
setTimeout(function() {{ try {{ video.pause(); }} catch (e) {{}} }}, 500);
|
||||
}}
|
||||
|
||||
function show() {{
|
||||
frame.src = 'about:blank';
|
||||
requestAnimationFrame(function() {{ frame.src = SRC; }}); // aus Cache -> Neustart von vorn
|
||||
backdrop.classList.add('eir-show');
|
||||
try {{ video.currentTime = 0; }} catch (e) {{}} // Neustart von vorn (auch bei Replay)
|
||||
var p = video.play();
|
||||
if (p && p.catch) p.catch(function() {{}}); // Autoplay-Ablehnung ignorieren
|
||||
clearTimeout(hideTimer);
|
||||
hideTimer = setTimeout(hide, DURATION);
|
||||
hideTimer = setTimeout(hide, FALLBACK); // Sicherheitsnetz, falls 'ended' ausbleibt
|
||||
}}
|
||||
|
||||
video.addEventListener('ended', hide); // exaktes Ausblenden am Videoende
|
||||
skip.addEventListener('click', hide);
|
||||
backdrop.addEventListener('click', function(e) {{ if (e.target === backdrop) hide(); }});
|
||||
replay.addEventListener('click', show);
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
Binary file not shown.
Loading…
Reference in New Issue