Restored to '0b21ba282eef95fac68017b564697d9e128bab4e'

Replit-Restored-To: 0b21ba282e
This commit is contained in:
keshavanandmusi
2025-12-14 23:56:02 +00:00
parent a63ccf96bf
commit 64196c80ab
3 changed files with 79 additions and 196 deletions

View File

@@ -4,23 +4,28 @@ import { Button } from "@/components/ui/button";
const SSH_COMMAND = "ssh portfolio@keshavanand.net";
const CHARACTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789@#$%^&*()_+-=[]{}|;:,.<>?/\\~`アイウエオカキクケコサシスセソタチツテト";
const CHARACTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789@#$%^&*()_+-=[]{}|;:,.<>?/\\~`";
interface MatrixColumn {
interface Particle {
x: number;
y: number;
speed: number;
chars: string[];
length: number;
baseOpacity: number;
char: string;
opacity: number;
targetX: number;
targetY: number;
vx: number;
vy: number;
life: number;
maxLife: number;
size: number;
color: string;
}
function ParticleBackground() {
const canvasRef = useRef<HTMLCanvasElement>(null);
const columnsRef = useRef<MatrixColumn[]>([]);
const mouseRef = useRef({ x: -1000, y: -1000 });
const particlesRef = useRef<Particle[]>([]);
const mouseRef = useRef({ x: 0, y: 0 });
const animationRef = useRef<number>();
const timeRef = useRef(0);
useEffect(() => {
const canvas = canvasRef.current;
@@ -29,147 +34,97 @@ function ParticleBackground() {
const ctx = canvas.getContext("2d");
if (!ctx) return;
const fontSize = 14;
const columnSpacing = fontSize * 1.2;
const initColumns = () => {
const cols: MatrixColumn[] = [];
const numColumns = Math.ceil(canvas.width / columnSpacing);
for (let i = 0; i < numColumns; i++) {
const length = Math.floor(Math.random() * 20) + 10;
const chars: string[] = [];
for (let j = 0; j < length; j++) {
chars.push(CHARACTERS[Math.floor(Math.random() * CHARACTERS.length)]);
}
cols.push({
x: i * columnSpacing,
y: Math.random() * canvas.height - canvas.height,
speed: Math.random() * 1.5 + 0.5,
chars,
length,
baseOpacity: Math.random() * 0.4 + 0.1,
});
}
columnsRef.current = cols;
};
const resizeCanvas = () => {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
initColumns();
};
resizeCanvas();
window.addEventListener("resize", resizeCanvas);
const handleMouseMove = (e: MouseEvent) => {
mouseRef.current = { x: e.clientX, y: e.clientY };
const colors = [
"#a6e3a1",
"#94e2d5",
"#89dceb",
"#74c7ec",
"#89b4fa",
"#cba6f7",
];
const createParticle = (x: number, y: number): Particle => {
const angle = Math.random() * Math.PI * 2;
const speed = Math.random() * 2 + 1;
const color = colors[Math.floor(Math.random() * colors.length)];
return {
x: x + (Math.random() - 0.5) * 100,
y: y + (Math.random() - 0.5) * 100,
char: CHARACTERS[Math.floor(Math.random() * CHARACTERS.length)],
opacity: Math.random() * 0.8 + 0.2,
targetX: x,
targetY: y,
vx: Math.cos(angle) * speed,
vy: Math.sin(angle) * speed,
life: 0,
maxLife: Math.random() * 60 + 40,
size: Math.random() * 10 + 12,
color,
};
};
const getColor = (y: number, distanceToMouse: number, charIndex: number, totalChars: number) => {
const heightRatio = y / canvas.height;
const mouseInfluence = Math.max(0, 1 - distanceToMouse / 250);
const headGlow = charIndex === totalChars - 1 ? 1 : 0;
const handleMouseMove = (e: MouseEvent) => {
mouseRef.current = { x: e.clientX, y: e.clientY };
if (headGlow && mouseInfluence > 0.3) {
return `rgba(255, 255, 255, ${0.9 + mouseInfluence * 0.1})`;
for (let i = 0; i < 3; i++) {
if (particlesRef.current.length < 200) {
particlesRef.current.push(createParticle(e.clientX, e.clientY));
}
}
const r = Math.floor(30 + mouseInfluence * 50 + heightRatio * 20);
const g = Math.floor(180 + mouseInfluence * 75 - heightRatio * 40);
const b = Math.floor(160 + heightRatio * 95 + mouseInfluence * 40);
return `rgb(${r}, ${g}, ${b})`;
};
const animate = () => {
timeRef.current++;
ctx.fillStyle = "rgba(0, 0, 0, 0.1)";
ctx.fillStyle = "#000000";
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.font = `${fontSize}px 'JetBrains Mono', monospace`;
columnsRef.current.forEach((col) => {
const dx = mouseRef.current.x - col.x;
const dy = mouseRef.current.y - col.y;
const distanceToMouse = Math.sqrt(dx * dx + dy * dy);
particlesRef.current = particlesRef.current.filter((p) => {
p.life++;
const gravityRadius = 200;
let gravityEffect = 0;
let pullX = 0;
const dx = mouseRef.current.x - p.x;
const dy = mouseRef.current.y - p.y;
const distance = Math.sqrt(dx * dx + dy * dy);
if (distanceToMouse < gravityRadius) {
gravityEffect = (gravityRadius - distanceToMouse) / gravityRadius;
pullX = (dx / distanceToMouse) * gravityEffect * 2;
}
for (let i = 0; i < col.chars.length; i++) {
const charY = col.y + i * fontSize;
if (charY < -fontSize || charY > canvas.height + fontSize) continue;
const charDx = mouseRef.current.x - (col.x + pullX);
const charDy = mouseRef.current.y - charY;
const charDistance = Math.sqrt(charDx * charDx + charDy * charDy);
const fadeRatio = i / col.chars.length;
let opacity = col.baseOpacity * (0.3 + fadeRatio * 0.7);
if (charDistance < 250) {
const boost = (250 - charDistance) / 250;
opacity = Math.min(1, opacity + boost * 0.8);
}
if (i === col.chars.length - 1) {
opacity = Math.min(1, opacity * 1.5);
}
const displayX = col.x + pullX * (1 - i / col.chars.length);
ctx.globalAlpha = opacity;
ctx.fillStyle = getColor(charY, charDistance, i, col.chars.length);
ctx.fillText(col.chars[i], displayX, charY);
if (charDistance < 150 && i === col.chars.length - 1) {
ctx.globalAlpha = opacity * 0.3;
ctx.shadowColor = getColor(charY, charDistance, i, col.chars.length);
ctx.shadowBlur = 10;
ctx.fillText(col.chars[i], displayX, charY);
ctx.shadowBlur = 0;
}
if (distance < 150) {
const force = (150 - distance) / 150;
p.vx += (dx / distance) * force * 0.3;
p.vy += (dy / distance) * force * 0.3;
}
ctx.globalAlpha = 1;
p.vx *= 0.96;
p.vy *= 0.96;
if (Math.random() < 0.02) {
const randomIndex = Math.floor(Math.random() * col.chars.length);
col.chars[randomIndex] = CHARACTERS[Math.floor(Math.random() * CHARACTERS.length)];
p.x += p.vx;
p.y += p.vy;
const lifeRatio = p.life / p.maxLife;
const fadeOpacity = lifeRatio < 0.2
? lifeRatio * 5
: lifeRatio > 0.7
? (1 - lifeRatio) * 3.33
: 1;
const distanceOpacity = Math.max(0, 1 - distance / 300);
const finalOpacity = p.opacity * fadeOpacity * distanceOpacity;
if (finalOpacity > 0.01) {
ctx.font = `${p.size}px 'JetBrains Mono', monospace`;
ctx.fillStyle = p.color;
ctx.globalAlpha = finalOpacity;
ctx.fillText(p.char, p.x, p.y);
ctx.globalAlpha = 1;
}
const speedMultiplier = 1 + gravityEffect * 0.5;
col.y += col.speed * speedMultiplier;
if (col.y > canvas.height + col.length * fontSize) {
col.y = -col.length * fontSize;
col.speed = Math.random() * 1.5 + 0.5;
col.baseOpacity = Math.random() * 0.4 + 0.1;
}
return p.life < p.maxLife;
});
if (mouseRef.current.x > 0 && mouseRef.current.y > 0) {
const gradient = ctx.createRadialGradient(
mouseRef.current.x, mouseRef.current.y, 0,
mouseRef.current.x, mouseRef.current.y, 150
);
gradient.addColorStop(0, "rgba(148, 226, 213, 0.03)");
gradient.addColorStop(0.5, "rgba(137, 180, 250, 0.02)");
gradient.addColorStop(1, "rgba(0, 0, 0, 0)");
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, canvas.width, canvas.height);
}
animationRef.current = requestAnimationFrame(animate);
};