feat: add pitch-deck service to core infrastructure
Migrated pitch-deck from breakpilot-pwa to breakpilot-core. Container: bp-core-pitch-deck on port 3012. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
83
pitch-deck/components/ParticleBackground.tsx
Normal file
83
pitch-deck/components/ParticleBackground.tsx
Normal file
@@ -0,0 +1,83 @@
|
||||
'use client'
|
||||
|
||||
import { useEffect, useRef } from 'react'
|
||||
|
||||
interface Particle {
|
||||
x: number
|
||||
y: number
|
||||
size: number
|
||||
speed: number
|
||||
opacity: number
|
||||
}
|
||||
|
||||
export default function ParticleBackground() {
|
||||
const canvasRef = useRef<HTMLCanvasElement>(null)
|
||||
const particlesRef = useRef<Particle[]>([])
|
||||
const frameRef = useRef<number>(0)
|
||||
|
||||
useEffect(() => {
|
||||
const canvas = canvasRef.current
|
||||
if (!canvas) return
|
||||
|
||||
const ctx = canvas.getContext('2d')
|
||||
if (!ctx) return
|
||||
|
||||
function resize() {
|
||||
canvas!.width = window.innerWidth
|
||||
canvas!.height = window.innerHeight
|
||||
}
|
||||
|
||||
function initParticles() {
|
||||
const count = Math.min(150, Math.floor((window.innerWidth * window.innerHeight) / 8000))
|
||||
particlesRef.current = Array.from({ length: count }, () => ({
|
||||
x: Math.random() * canvas!.width,
|
||||
y: Math.random() * canvas!.height,
|
||||
size: Math.random() * 1.5 + 0.5,
|
||||
speed: Math.random() * 0.3 + 0.1,
|
||||
opacity: Math.random() * 0.5 + 0.1,
|
||||
}))
|
||||
}
|
||||
|
||||
function animate() {
|
||||
if (!ctx || !canvas) return
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height)
|
||||
|
||||
for (const p of particlesRef.current) {
|
||||
p.y -= p.speed
|
||||
if (p.y < -10) {
|
||||
p.y = canvas.height + 10
|
||||
p.x = Math.random() * canvas.width
|
||||
}
|
||||
|
||||
ctx.beginPath()
|
||||
ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2)
|
||||
ctx.fillStyle = `rgba(255, 255, 255, ${p.opacity})`
|
||||
ctx.fill()
|
||||
}
|
||||
|
||||
frameRef.current = requestAnimationFrame(animate)
|
||||
}
|
||||
|
||||
resize()
|
||||
initParticles()
|
||||
animate()
|
||||
|
||||
window.addEventListener('resize', () => {
|
||||
resize()
|
||||
initParticles()
|
||||
})
|
||||
|
||||
return () => {
|
||||
cancelAnimationFrame(frameRef.current)
|
||||
window.removeEventListener('resize', resize)
|
||||
}
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<canvas
|
||||
ref={canvasRef}
|
||||
className="fixed inset-0 pointer-events-none z-0"
|
||||
style={{ opacity: 0.6 }}
|
||||
/>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user