File size: 3,532 Bytes
4fb0ce9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
/**
 * 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 LiquidIntroProps {
  onEnter: () => void;
}

export function LiquidIntro({ onEnter }: LiquidIntroProps) {
  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("/liquid-dark.webp");
      app.liquidPlane.material.metalness = 0.75;
      app.liquidPlane.material.roughness = 0.58;
      app.liquidPlane.uniforms.displacementScale.value = 5;
      app.setRain(false);

      // Small delay to let the first frame render
      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"
      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">
          <span className="text-black">LFM2.5</span>{" "}
          <span className="text-gray-900">WebGPU</span>
        </h1>

        <p className="mt-6 text-lg text-gray-800 select-none animate-pulse-gentle">
          Click anywhere to start
        </p>
      </div>

      <div
        className={`absolute bottom-4 right-4 text-right text-gray-500/70 select-none transition-opacity duration-500 flex flex-col space-y-[4px] ${
          fading ? "opacity-0" : ready ? "opacity-100" : "opacity-0"
        }`}
      >
        <a
          href="https://codepen.io/soju22/pen/myVWBGa"
          target="_blank"
          rel="noreferrer"
          className="hover:text-gray-800 transition-colors text-[14px]"
          onClick={(e) => e.stopPropagation()}
        >
          Liquid effect by Kevin Levron
        </a>
        <a
          href="https://creativecommons.org/licenses/by-nc-sa/4.0/"
          target="_blank"
          rel="noreferrer"
          className="hover:text-gray-800 transition-colors text-[12px]"
          onClick={(e) => e.stopPropagation()}
        >
          Licensed under CC BY-NC-SA 4.0
        </a>
      </div>

      <div
        className={`absolute inset-0 bg-white transition-opacity duration-600 pointer-events-none ${
          fading ? "opacity-100" : "opacity-0"
        }`}
      />
    </div>
  );
}