Dvd thingy

This commit is contained in:
2025-10-21 22:31:50 +02:00
parent 6fe3859ca1
commit d9e9dcd6e8
3 changed files with 69 additions and 172 deletions
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 13 KiB

+5 -1
View File
@@ -15,7 +15,11 @@ export default function RootLayout({
}>) { }>) {
return ( return (
<html lang="en"> <html lang="en">
<body className="antialiased bg-black">{children}</body> <body className="antialiased bg-black">
<Background>
<div className="relative h-screen overflow-y-scroll">{children}</div>
</Background>
</body>
</html> </html>
); );
} }
+56 -163
View File
@@ -1,4 +1,4 @@
'use client'; "use client";
import React, { useEffect, useRef, useState } from "react"; import React, { useEffect, useRef, useState } from "react";
@@ -7,183 +7,76 @@ export default function Background({
}: Readonly<{ }: Readonly<{
children: React.ReactNode; children: React.ReactNode;
}>) { }>) {
const canvasRef = useRef<HTMLCanvasElement | null>(null); const [pos, setPos] = useState({ x: 100, y: 100 });
const animationRef = useRef<number>(0); const [vel, setVel] = useState({ x: 1, y: 1 });
const particlesRef = useRef<Particle[]>([]); const dvdRef = useRef<HTMLImageElement>(null);
const [dimensions, setDimensions] = useState<{ width: number; height: number }>({
width: 0,
height: 0,
});
// Particle configuration useEffect(() => {
const config = { let frame: number;
particleCount: 400, let x = pos.x;
maxDistance: 120, let y = pos.y;
particleSpeed: 0.5, let vx = vel.x;
particleSize: 2, let vy = vel.y;
lineOpacity: 0.6,
particleOpacity: 0.8, const speed = 0.8;
colors: { const size = 120;
particles: "#f5304c", // blue-400
connections: "#f5304c", // blue-500 // helper for collision detection
}, const detectCollision = (
nextX: number,
nextY: number,
w: number,
h: number
) => {
let collidedX = false;
let collidedY = false;
// Left or Right wall
if (nextX <= 0 || nextX + size >= w) collidedX = true;
// Top or Bottom wall
if (nextY <= 0 || nextY + size >= h) collidedY = true;
return { collidedX, collidedY };
}; };
// Particle class
class Particle {
x: number;
y: number;
vx: number;
vy: number;
size: number;
constructor(width: number, height: number) {
this.x = Math.random() * width;
this.y = Math.random() * height;
this.vx = (Math.random() - 0.5) * config.particleSpeed;
this.vy = (Math.random() - 0.5) * config.particleSpeed;
this.size = config.particleSize + Math.random() * 2;
}
update(width: number, height: number) {
this.x += this.vx;
this.y += this.vy;
// Bounce off edges
if (this.x < 0 || this.x > width) {
this.vx = -this.vx;
this.x = Math.max(0, Math.min(width, this.x));
}
if (this.y < 0 || this.y > height) {
this.vy = -this.vy;
this.y = Math.max(0, Math.min(height, this.y));
}
}
draw(ctx: CanvasRenderingContext2D) {
ctx.beginPath();
ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);
ctx.fillStyle = `${config.colors.particles}${Math.round(
config.particleOpacity * 255
)
.toString(16)
.padStart(2, "0")}`;
ctx.fill();
}
}
// Initialize particles
const initParticles = (width: number, height: number) => {
particlesRef.current = [];
for (let i = 0; i < config.particleCount; i++) {
particlesRef.current.push(new Particle(width, height));
}
};
// Draw connections between nearby particles
const drawConnections = (ctx: CanvasRenderingContext2D, particles: Particle[]) => {
for (let i = 0; i < particles.length; i++) {
for (let j = i + 1; j < particles.length; j++) {
const dx = particles[i].x - particles[j].x;
const dy = particles[i].y - particles[j].y;
const distance = Math.sqrt(dx * dx + dy * dy);
if (distance < config.maxDistance) {
const opacity = 1 - distance / config.maxDistance;
ctx.beginPath();
ctx.moveTo(particles[i].x, particles[i].y);
ctx.lineTo(particles[j].x, particles[j].y);
ctx.strokeStyle = `${config.colors.connections}${Math.round(
opacity * config.lineOpacity * 255
)
.toString(16)
.padStart(2, "0")}`;
ctx.lineWidth = 1;
ctx.stroke();
}
}
}
};
// Animation loop
const animate = () => { const animate = () => {
const canvas = canvasRef.current; const w = window.innerWidth;
if (!canvas) return; const h = window.innerHeight;
const ctx = canvas.getContext("2d"); const nextX = x + vx * speed;
if (!ctx) return; const nextY = y + vy * speed;
const { width, height } = dimensions; const { collidedX, collidedY } = detectCollision(nextX, nextY, w, h);
// Clear canvas if (collidedX) vx *= -1;
ctx.clearRect(0, 0, width, height); if (collidedY) vy *= -1;
// Update and draw particles x += vx * speed;
particlesRef.current.forEach((particle) => { y += vy * speed;
particle.update(width, height);
particle.draw(ctx);
});
// Draw connections setPos({ x, y });
drawConnections(ctx, particlesRef.current); setVel({ x: vx, y: vy });
animationRef.current = requestAnimationFrame(animate); frame = requestAnimationFrame(animate);
}; };
// Handle window resize frame = requestAnimationFrame(animate);
const handleResize = () => { return () => cancelAnimationFrame(frame);
const canvas = canvasRef.current;
if (!canvas || !canvas.parentElement) return;
const rect = canvas.parentElement.getBoundingClientRect();
const width = rect.width;
const height = rect.height;
const dpr = window.devicePixelRatio || 1;
canvas.width = width * dpr;
canvas.height = height * dpr;
canvas.style.width = `${width}px`;
canvas.style.height = `${height}px`;
const ctx = canvas.getContext("2d");
if (ctx) {
ctx.setTransform(1, 0, 0, 1, 0, 0); // reset scaling before re-scaling
ctx.scale(dpr, dpr);
}
setDimensions({ width, height });
};
useEffect(() => {
handleResize();
window.addEventListener("resize", handleResize);
return () => {
window.removeEventListener("resize", handleResize);
if (animationRef.current) {
cancelAnimationFrame(animationRef.current);
}
};
}, []); }, []);
useEffect(() => {
if (dimensions.width && dimensions.height) {
initParticles(dimensions.width, dimensions.height);
animate();
}
return () => {
if (animationRef.current) {
cancelAnimationFrame(animationRef.current);
}
};
}, [dimensions]);
return ( return (
<div className="relative w-full min-h-screen overflow-hidden bg-black"> <div className="relative w-full min-h-screen overflow-hidden bg-black">
<canvas ref={canvasRef} className="absolute inset-0 w-full h-full" /> <img
ref={dvdRef}
{children} style={{
transform: `translate(${pos.x}px, ${pos.y}px)`,
}}
src="/logo.png"
alt="Logo"
className="size-24 rounded-full border-slate-400 border"
/>
<div className="relative z-10">{children}</div>
</div> </div>
); );
}; }