Spaces:
Running
Running
| /** | |
| * Liquid effect adapted from https://www.framer.com/@kevin-levron/ | |
| */ | |
| import { useEffect, useRef, useCallback, useState } from "react"; | |
| import LiquidBackground from "../utils/liquid1.min.js"; | |
| type LiquidApp = ReturnType<typeof LiquidBackground>; | |
| interface GemmaIntroProps { | |
| onEnter: () => void; | |
| } | |
| export function GemmaIntro({ onEnter }: GemmaIntroProps) { | |
| const canvasRef = useRef<HTMLCanvasElement>(null); | |
| const appRef = useRef<LiquidApp | null>(null); | |
| const [fading, setFading] = useState(false); | |
| const [ready, setReady] = useState(false); | |
| useEffect(() => { | |
| let disposed = false; | |
| async function init() { | |
| if (disposed || !canvasRef.current) return; | |
| const app = LiquidBackground(canvasRef.current); | |
| appRef.current = app; | |
| app.loadImage("/gemma4.png"); | |
| app.liquidPlane.material.metalness = 0.82; | |
| app.liquidPlane.material.roughness = 0.48; | |
| app.liquidPlane.uniforms.displacementScale.value = 6; | |
| app.setRain(false); | |
| setTimeout(() => { | |
| if (!disposed) setReady(true); | |
| }, 300); | |
| } | |
| init(); | |
| return () => { | |
| disposed = true; | |
| if (appRef.current) { | |
| appRef.current.dispose(); | |
| appRef.current = null; | |
| } | |
| }; | |
| }, []); | |
| const handleClick = useCallback(() => { | |
| if (fading) return; | |
| setFading(true); | |
| setTimeout(() => { | |
| if (appRef.current) { | |
| appRef.current.dispose(); | |
| appRef.current = null; | |
| } | |
| onEnter(); | |
| }, 600); | |
| }, [fading, onEnter]); | |
| return ( | |
| <div | |
| className="absolute inset-0 z-30 cursor-pointer bg-black" | |
| onClick={handleClick} | |
| > | |
| <canvas | |
| ref={canvasRef} | |
| className={`absolute inset-0 w-full h-full transition-opacity duration-700 ${ | |
| ready ? "opacity-100" : "opacity-0" | |
| }`} | |
| /> | |
| <div | |
| className={`absolute inset-0 flex flex-col items-center justify-end transition-opacity duration-500 pb-10 ${ | |
| fading ? "opacity-0" : ready ? "opacity-100" : "opacity-0" | |
| }`} | |
| > | |
| <h1 className="text-6xl sm:text-7xl font-bold tracking-tight select-none drop-shadow-[0_2px_12px_rgba(0,0,0,0.5)]"> | |
| <span className="text-white">Gemma 4</span>{" "} | |
| <span className="text-white/90">WebGPU</span> | |
| </h1> | |
| <p className="mt-6 text-lg text-white/80 select-none animate-pulse-gentle drop-shadow-[0_1px_6px_rgba(0,0,0,0.5)]"> | |
| Click anywhere to start | |
| </p> | |
| </div> | |
| <div | |
| className={`absolute inset-0 bg-black transition-opacity duration-600 pointer-events-none ${ | |
| fading ? "opacity-100" : "opacity-0" | |
| }`} | |
| /> | |
| </div> | |
| ); | |
| } | |