feat(gatekeeper): major UI upgrade - high-fidelity light theme, iridescent mouse-reactive form, and enhanced background animation
Some checks failed
Monorepo Pipeline / ⚡ Prioritize Release (push) Successful in 2s
Monorepo Pipeline / 🧪 Test (push) Successful in 1m10s
Monorepo Pipeline / 🧹 Lint (push) Failing after 3m15s
Monorepo Pipeline / 🏗️ Build (push) Successful in 1m53s
Monorepo Pipeline / 🚀 Release (push) Has been skipped
Monorepo Pipeline / 🐳 Build Gatekeeper (Product) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Build-Base (push) Has been skipped
Monorepo Pipeline / 🐳 Build Production Runtime (push) Has been skipped
🏥 Server Maintenance / 🧹 Prune & Clean (push) Failing after 4s
Some checks failed
Monorepo Pipeline / ⚡ Prioritize Release (push) Successful in 2s
Monorepo Pipeline / 🧪 Test (push) Successful in 1m10s
Monorepo Pipeline / 🧹 Lint (push) Failing after 3m15s
Monorepo Pipeline / 🏗️ Build (push) Successful in 1m53s
Monorepo Pipeline / 🚀 Release (push) Has been skipped
Monorepo Pipeline / 🐳 Build Gatekeeper (Product) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Build-Base (push) Has been skipped
Monorepo Pipeline / 🐳 Build Production Runtime (push) Has been skipped
🏥 Server Maintenance / 🧹 Prune & Clean (push) Failing after 4s
This commit is contained in:
127
packages/gatekeeper/src/components/gate-scene.tsx
Normal file
127
packages/gatekeeper/src/components/gate-scene.tsx
Normal file
@@ -0,0 +1,127 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useRef } from "react";
|
||||
|
||||
export function GateScene() {
|
||||
const canvasRef = useRef<HTMLCanvasElement>(null);
|
||||
const mouse = useRef({ x: -1000, y: -1000 });
|
||||
|
||||
useEffect(() => {
|
||||
const canvas = canvasRef.current;
|
||||
if (!canvas) return;
|
||||
const ctx = canvas.getContext("2d");
|
||||
if (!ctx) return;
|
||||
|
||||
const FONT_SIZE = 13;
|
||||
const COL_GAP = 18;
|
||||
const ROW_GAP = 20;
|
||||
|
||||
interface Cell {
|
||||
char: string;
|
||||
alpha: number;
|
||||
targetAlpha: number;
|
||||
speed: number;
|
||||
nextChange: number;
|
||||
}
|
||||
|
||||
let cells: Cell[][] = [];
|
||||
let cols = 0;
|
||||
let rows = 0;
|
||||
let animId: number;
|
||||
|
||||
const init = () => {
|
||||
cols = Math.ceil(canvas.width / COL_GAP);
|
||||
rows = Math.ceil(canvas.height / ROW_GAP);
|
||||
cells = Array.from({ length: cols }, () =>
|
||||
Array.from({ length: rows }, () => ({
|
||||
char: Math.random() > 0.5 ? "1" : "0",
|
||||
alpha: Math.random() * 0.08,
|
||||
targetAlpha: Math.random() * 0.15,
|
||||
speed: 0.008 + Math.random() * 0.02,
|
||||
nextChange: Math.floor(Math.random() * 80),
|
||||
})),
|
||||
);
|
||||
};
|
||||
|
||||
const onMouseMove = (e: MouseEvent) => {
|
||||
mouse.current = { x: e.clientX, y: e.clientY };
|
||||
};
|
||||
|
||||
const resize = () => {
|
||||
canvas.width = window.innerWidth;
|
||||
canvas.height = window.innerHeight;
|
||||
init();
|
||||
};
|
||||
|
||||
let frame = 0;
|
||||
|
||||
const draw = () => {
|
||||
frame++;
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
ctx.font = `${FONT_SIZE}px 'Courier New', monospace`;
|
||||
|
||||
for (let c = 0; c < cols; c++) {
|
||||
for (let r = 0; r < rows; r++) {
|
||||
const cell = cells[c][r];
|
||||
const x = c * COL_GAP;
|
||||
const y = r * ROW_GAP + FONT_SIZE;
|
||||
|
||||
// Mouse proximity influence
|
||||
const dx = mouse.current.x - x;
|
||||
const dy = mouse.current.y - y;
|
||||
const distSq = dx * dx + dy * dy;
|
||||
const proximity = Math.max(0, 1 - Math.sqrt(distSq) / 250);
|
||||
|
||||
// Nudge alpha toward target
|
||||
cell.alpha += (cell.targetAlpha - cell.alpha) * cell.speed;
|
||||
|
||||
// More aggressive random behavior
|
||||
if (frame >= cell.nextChange) {
|
||||
cell.targetAlpha = Math.random() * 0.25; // Higher max alpha
|
||||
cell.speed = 0.01 + Math.random() * 0.03; // Faster transitions
|
||||
cell.nextChange = frame + 20 + Math.floor(Math.random() * 80); // More frequent changes
|
||||
|
||||
// Higher flip probability near mouse
|
||||
const flipProb = 0.3 + proximity * 0.5;
|
||||
if (Math.random() < flipProb) {
|
||||
cell.char = cell.char === "0" ? "1" : "0";
|
||||
}
|
||||
}
|
||||
|
||||
const a = Math.min(0.4, cell.alpha + proximity * 0.35);
|
||||
if (a < 0.01) continue;
|
||||
|
||||
// Dark chars on light background
|
||||
ctx.fillStyle = `rgba(0, 0, 0, ${a})`;
|
||||
ctx.fillText(cell.char, x, y);
|
||||
}
|
||||
}
|
||||
|
||||
animId = requestAnimationFrame(draw);
|
||||
};
|
||||
|
||||
resize();
|
||||
window.addEventListener("resize", resize);
|
||||
window.addEventListener("mousemove", onMouseMove);
|
||||
animId = requestAnimationFrame(draw);
|
||||
|
||||
return () => {
|
||||
cancelAnimationFrame(animId);
|
||||
window.removeEventListener("resize", resize);
|
||||
window.removeEventListener("mousemove", onMouseMove);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="absolute inset-0 pointer-events-none z-0 bg-[#f5f5f7]">
|
||||
<canvas ref={canvasRef} className="absolute inset-0 w-full h-full" />
|
||||
<div
|
||||
className="absolute inset-0 pointer-events-none"
|
||||
style={{
|
||||
background:
|
||||
"radial-gradient(ellipse 55% 65% at 50% 50%, rgba(245,245,247,0.7) 0%, transparent 100%)",
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user