Files
OpenFlare/frontend/components/ui/snow-effect.tsx
T
2026-06-08 20:34:27 +08:00

97 lines
2.2 KiB
TypeScript

"use client"
import { useEffect, useRef } from "react"
interface Snowflake {
x: number
y: number
radius: number
speed: number
opacity: number
drift: number
}
export function SnowEffect() {
const canvasRef = useRef<HTMLCanvasElement>(null)
useEffect(() => {
const canvas = canvasRef.current
if (!canvas) return
const ctx = canvas.getContext("2d")
if (!ctx) return
let animationFrameId: number
let snowflakes: Snowflake[] = []
const resizeCanvas = () => {
canvas.width = window.innerWidth
canvas.height = window.innerHeight
}
const createSnowflakes = () => {
const count = Math.floor(window.innerWidth / 4)
snowflakes = []
for (let i = 0; i < count; i++) {
snowflakes.push({
x: Math.random() * canvas.width,
y: Math.random() * canvas.height,
radius: Math.random() * 3 + 1,
speed: Math.random() * 2 + 0.5,
opacity: Math.random() * 0.5 + 0.3,
drift: Math.random() * 1 - 0.5
})
}
}
const drawSnowflakes = () => {
if (!ctx || !canvas) return
ctx.clearRect(0, 0, canvas.width, canvas.height)
snowflakes.forEach((flake) => {
ctx.beginPath()
ctx.arc(flake.x, flake.y, flake.radius, 0, Math.PI * 2)
ctx.fillStyle = `rgba(255, 255, 255, ${ flake.opacity })`
ctx.fill()
flake.y += flake.speed
flake.x += flake.drift
if (flake.y > canvas.height) {
flake.y = -flake.radius
flake.x = Math.random() * canvas.width
}
if (flake.x > canvas.width) {
flake.x = 0
} else if (flake.x < 0) {
flake.x = canvas.width
}
})
animationFrameId = requestAnimationFrame(drawSnowflakes)
}
resizeCanvas()
createSnowflakes()
drawSnowflakes()
window.addEventListener("resize", () => {
resizeCanvas()
createSnowflakes()
})
return () => {
cancelAnimationFrame(animationFrameId)
window.removeEventListener("resize", resizeCanvas)
}
}, [])
return (
<canvas
ref={canvasRef}
className="fixed inset-0 pointer-events-none z-50"
style={{ background: "transparent" }}
/>
)
}