feat(RealityOverridePage): implement star field animation beneath Three.js vortex
This commit is contained in:
parent
e87aeb6726
commit
53ed27e250
|
|
@ -6,6 +6,22 @@ import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass.js'
|
|||
import { UnrealBloomPass } from 'three/examples/jsm/postprocessing/UnrealBloomPass.js';
|
||||
|
||||
interface Msg { text: string; id: number; }
|
||||
interface Star { x: number; y: number; sz: number; ph: number; spd: number; col: [number, number, number]; }
|
||||
|
||||
const STAR_PALETTE: [number, number, number][] = [
|
||||
[255, 255, 255], [255, 240, 210], [210, 220, 255], [255, 210, 210], [200, 255, 230],
|
||||
];
|
||||
|
||||
function mkStar(W: number, H: number): Star {
|
||||
return {
|
||||
x: Math.random() * W | 0,
|
||||
y: Math.random() * H | 0,
|
||||
sz: Math.random() < 0.8 ? 1 : Math.random() < 0.6 ? 2 : 3,
|
||||
ph: Math.random() * Math.PI * 2,
|
||||
spd: Math.random() * 0.04 + 0.005,
|
||||
col: STAR_PALETTE[Math.random() * STAR_PALETTE.length | 0],
|
||||
};
|
||||
}
|
||||
|
||||
// ── Shaders (verbatim from public/vortex/script.js) ──────────────────────────
|
||||
|
||||
|
|
@ -79,6 +95,8 @@ const FRAG_TRAIL = `
|
|||
|
||||
export default function RealityOverridePage({ onBack }: { onBack: () => void }) {
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const starCanvasRef = useRef<HTMLCanvasElement>(null);
|
||||
const starsRef = useRef<Star[]>([]);
|
||||
const wsRef = useRef<WebSocket | null>(null);
|
||||
const msgIdRef = useRef(0);
|
||||
const fadeTimer = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);
|
||||
|
|
@ -231,6 +249,43 @@ export default function RealityOverridePage({ onBack }: { onBack: () => void })
|
|||
};
|
||||
}, []);
|
||||
|
||||
// ── Star field (always running, visible under Three.js when vortex is hidden) ─
|
||||
useEffect(() => {
|
||||
const canvas = starCanvasRef.current;
|
||||
if (!canvas) return;
|
||||
let animId = 0;
|
||||
let prevW = 0, prevH = 0;
|
||||
|
||||
const loop = () => {
|
||||
const W = canvas.clientWidth, H = canvas.clientHeight;
|
||||
if (!W || !H) { animId = requestAnimationFrame(loop); return; }
|
||||
if (W !== prevW || H !== prevH) {
|
||||
canvas.width = W; canvas.height = H;
|
||||
prevW = W; prevH = H;
|
||||
starsRef.current = Array.from({ length: 200 }, () => mkStar(W, H));
|
||||
}
|
||||
const ctx = canvas.getContext('2d')!;
|
||||
ctx.fillStyle = '#000010';
|
||||
ctx.fillRect(0, 0, W, H);
|
||||
for (const st of starsRef.current) {
|
||||
st.ph += st.spd;
|
||||
const br = Math.sin(st.ph) * 0.4 + 0.6;
|
||||
const [r, g, b] = st.col;
|
||||
ctx.fillStyle = `rgba(${r},${g},${b},${br})`;
|
||||
ctx.fillRect(st.x, st.y, st.sz, st.sz);
|
||||
if (st.sz >= 3 && br > 0.85) {
|
||||
ctx.fillStyle = `rgba(${r},${g},${b},${br * 0.35})`;
|
||||
ctx.fillRect(st.x - 2, st.y + 1, st.sz + 4, 1);
|
||||
ctx.fillRect(st.x + 1, st.y - 2, 1, st.sz + 4);
|
||||
}
|
||||
}
|
||||
animId = requestAnimationFrame(loop);
|
||||
};
|
||||
|
||||
animId = requestAnimationFrame(loop);
|
||||
return () => cancelAnimationFrame(animId);
|
||||
}, []);
|
||||
|
||||
// ── WebSocket ───────────────────────────────────────────────────────────────
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
|
|
@ -266,7 +321,10 @@ export default function RealityOverridePage({ onBack }: { onBack: () => void })
|
|||
onTouchEnd={handleTouchEnd}
|
||||
>
|
||||
|
||||
{/* Three.js canvas mount */}
|
||||
{/* Star field — always visible beneath the vortex */}
|
||||
<canvas ref={starCanvasRef} className="absolute inset-0 w-full h-full" style={{ imageRendering: 'pixelated' }} />
|
||||
|
||||
{/* Three.js vortex — fades out on double-click/tap */}
|
||||
<div ref={containerRef} className={`absolute inset-0 transition-opacity duration-500 ${bgVisible ? 'opacity-100' : 'opacity-0'}`} />
|
||||
|
||||
{/* CSS keyframe for command flash */}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user