Compare commits
2 Commits
7c691a8044
...
a3f611e227
| Author | SHA1 | Date | |
|---|---|---|---|
| a3f611e227 | |||
| b578e68c2e |
@@ -16,16 +16,15 @@ const DIALOGUE_FALLBACK_TIMEOUT_MS = 12000;
|
||||
const NO_DIALOGUE_FALLBACK_MS = 3000;
|
||||
|
||||
/**
|
||||
* Transition overlay: black screen, logo fade-in, transition dialogue
|
||||
* with subtitles, then redirect to /. A safety timeout guarantees the
|
||||
* redirect happens even if the dialogue audio fails to fire `ended`.
|
||||
* Transition overlay: black screen with transition dialogue and subtitles,
|
||||
* then redirect to /. A safety timeout guarantees the redirect happens even if
|
||||
* the dialogue audio fails to fire `ended`.
|
||||
*/
|
||||
export function SiteTransitionOverlay(): React.JSX.Element {
|
||||
const navigate = useNavigate();
|
||||
const reset = useSiteStore((state) => state.reset);
|
||||
const prefersReducedMotion = usePrefersReducedMotion();
|
||||
const [screenOpacity, setScreenOpacity] = useState(0);
|
||||
const [logoOpacity, setLogoOpacity] = useState(0);
|
||||
|
||||
useEffect(() => {
|
||||
setSiteVisited();
|
||||
@@ -37,13 +36,11 @@ export function SiteTransitionOverlay(): React.JSX.Element {
|
||||
// initial frame at opacity 0 before flipping to 1.
|
||||
const fadeInId = window.setTimeout(() => {
|
||||
setScreenOpacity(1);
|
||||
setLogoOpacity(1);
|
||||
}, 0);
|
||||
timeoutIds.push(fadeInId);
|
||||
|
||||
const redirectToGame = (): void => {
|
||||
if (isCancelled) return;
|
||||
setLogoOpacity(0);
|
||||
const id = window.setTimeout(() => {
|
||||
if (isCancelled) return;
|
||||
reset();
|
||||
@@ -119,23 +116,6 @@ export function SiteTransitionOverlay(): React.JSX.Element {
|
||||
transition: fadeTransition,
|
||||
}}
|
||||
/>
|
||||
<img
|
||||
src="/assets/logo/logo.jpg"
|
||||
alt="Logo Altera"
|
||||
style={{
|
||||
position: "relative",
|
||||
zIndex: 1,
|
||||
width: "min(300px, 45vw)",
|
||||
height: "auto",
|
||||
objectFit: "contain",
|
||||
opacity: logoOpacity,
|
||||
transition: fadeTransition,
|
||||
transitionDelay:
|
||||
!prefersReducedMotion && logoOpacity === 1
|
||||
? `${FADE_DURATION_MS}ms`
|
||||
: "0ms",
|
||||
}}
|
||||
/>
|
||||
{/* Subtitles must live inside this overlay's stacking context
|
||||
(z-index 1000) so they render above the black screen. The
|
||||
<Subtitles /> in SiteLayout sits behind this overlay. */}
|
||||
|
||||
+14
-3
@@ -96,9 +96,16 @@ export function HomePage(): React.JSX.Element | null {
|
||||
gl.shadowMap.type = THREE.PCFShadowMap;
|
||||
gl.shadowMap.autoUpdate = true;
|
||||
|
||||
// The browser hands us a WEBGL_lose_context extension we can use to
|
||||
// ask the GPU to restore the context after a loss. Without this the
|
||||
// page stays frozen on a black canvas until the user reloads.
|
||||
const loseContextExt = gl.getContext().getExtension("WEBGL_lose_context");
|
||||
|
||||
const handleContextLost = (event: Event) => {
|
||||
event.preventDefault();
|
||||
logger.error("WebGL", "Context lost - GPU resources exhausted");
|
||||
logger.error("WebGL", "Context lost - attempting auto-restore");
|
||||
// Give the GPU a moment to free resources before asking it back.
|
||||
window.setTimeout(() => loseContextExt?.restoreContext(), 500);
|
||||
};
|
||||
|
||||
const handleContextRestored = () => {
|
||||
@@ -121,10 +128,14 @@ export function HomePage(): React.JSX.Element | null {
|
||||
// all hooks (rules of hooks) but BEFORE any expensive render.
|
||||
if (!hasSiteBeenVisitedToday()) return null;
|
||||
|
||||
const showFadeToVideoOverlay =
|
||||
introStep === "fade-to-video" ||
|
||||
(introStep === "loading-map" && sceneLoadingState.status === "ready");
|
||||
|
||||
const renderIntroOverlay = () => {
|
||||
if (showFadeToVideoOverlay) return <FadeToVideoOverlay />;
|
||||
|
||||
switch (introStep) {
|
||||
case "fade-to-video":
|
||||
return <FadeToVideoOverlay />;
|
||||
case "video":
|
||||
return <IntroVideoPlayer />;
|
||||
case "dialogue-intro":
|
||||
|
||||
Reference in New Issue
Block a user