mirror of
https://github.com/Novattz/creamlinux-installer.git
synced 2026-08-01 19:18:25 -04:00
116 lines
3.2 KiB
TypeScript
116 lines
3.2 KiB
TypeScript
import { useEffect, useRef } from 'react'
|
|
|
|
/**
|
|
* Animated background component that draws an interactive particle effect
|
|
*/
|
|
const AnimatedBackground = () => {
|
|
const canvasRef = useRef<HTMLCanvasElement>(null)
|
|
|
|
useEffect(() => {
|
|
const canvas = canvasRef.current
|
|
if (!canvas) return
|
|
|
|
const ctx = canvas.getContext('2d')
|
|
if (!ctx) return
|
|
|
|
// Set canvas size to match window
|
|
const setCanvasSize = () => {
|
|
canvas.width = window.innerWidth
|
|
canvas.height = window.innerHeight
|
|
}
|
|
|
|
setCanvasSize()
|
|
window.addEventListener('resize', setCanvasSize)
|
|
|
|
// Create particles
|
|
const particles: Particle[] = []
|
|
const particleCount = 30
|
|
|
|
interface Particle {
|
|
x: number
|
|
y: number
|
|
size: number
|
|
speedX: number
|
|
speedY: number
|
|
opacity: number
|
|
color: string
|
|
}
|
|
|
|
// Color palette matching theme
|
|
const colors = [
|
|
'rgba(74, 118, 196, 0.5)', // primary blue
|
|
'rgba(155, 125, 255, 0.5)', // purple
|
|
'rgba(251, 177, 60, 0.5)', // gold
|
|
]
|
|
|
|
// Create initial particles
|
|
for (let i = 0; i < particleCount; i++) {
|
|
particles.push({
|
|
x: Math.random() * canvas.width,
|
|
y: Math.random() * canvas.height,
|
|
size: Math.random() * 3 + 1,
|
|
speedX: Math.random() * 0.2 - 0.1,
|
|
speedY: Math.random() * 0.2 - 0.1,
|
|
opacity: Math.random() * 0.07 + 0.03,
|
|
color: colors[Math.floor(Math.random() * colors.length)],
|
|
})
|
|
}
|
|
|
|
// Animation loop
|
|
const animate = () => {
|
|
// Clear canvas with transparent black to create fade effect
|
|
ctx.fillStyle = 'rgba(15, 15, 15, 0.1)'
|
|
ctx.fillRect(0, 0, canvas.width, canvas.height)
|
|
|
|
// Update and draw particles
|
|
particles.forEach((particle) => {
|
|
// Update position
|
|
particle.x += particle.speedX
|
|
particle.y += particle.speedY
|
|
|
|
// Wrap around edges
|
|
if (particle.x < 0) particle.x = canvas.width
|
|
if (particle.x > canvas.width) particle.x = 0
|
|
if (particle.y < 0) particle.y = canvas.height
|
|
if (particle.y > canvas.height) particle.y = 0
|
|
|
|
// Draw particle
|
|
ctx.beginPath()
|
|
ctx.arc(particle.x, particle.y, particle.size, 0, Math.PI * 2)
|
|
ctx.fillStyle = particle.color.replace('0.5', `${particle.opacity}`)
|
|
ctx.fill()
|
|
|
|
// Connect particles that are close to each other
|
|
particles.forEach((otherParticle) => {
|
|
const dx = particle.x - otherParticle.x
|
|
const dy = particle.y - otherParticle.y
|
|
const distance = Math.sqrt(dx * dx + dy * dy)
|
|
|
|
if (distance < 100) {
|
|
ctx.beginPath()
|
|
ctx.strokeStyle = particle.color.replace('0.5', `${particle.opacity * 0.5}`)
|
|
ctx.lineWidth = 0.2
|
|
ctx.moveTo(particle.x, particle.y)
|
|
ctx.lineTo(otherParticle.x, otherParticle.y)
|
|
ctx.stroke()
|
|
}
|
|
})
|
|
})
|
|
|
|
requestAnimationFrame(animate)
|
|
}
|
|
|
|
// Start animation
|
|
animate()
|
|
|
|
// Cleanup
|
|
return () => {
|
|
window.removeEventListener('resize', setCanvasSize)
|
|
}
|
|
}, [])
|
|
|
|
return <canvas ref={canvasRef} className="animated-background" aria-hidden="true" />
|
|
}
|
|
|
|
export default AnimatedBackground
|