This commit is contained in:
ryan
2026-07-13 15:08:40 +08:00
parent 08e8eea932
commit 1eff7878a1
441 changed files with 26491 additions and 18405 deletions
+100 -89
View File
@@ -1,58 +1,69 @@
"use client"
'use client';
import {useEffect, useRef} from "react"
import {useEffect, useRef} from 'react';
interface Particle {
x: number
y: number
vx: number
vy: number
alpha: number
color: string
decay: number
x: number;
y: number;
vx: number;
vy: number;
alpha: number;
color: string;
decay: number;
}
interface Firework {
x: number
y: number
targetY: number
vx: number
vy: number
color: string
particles: Particle[]
exploded: boolean
dead: boolean
x: number;
y: number;
targetY: number;
vx: number;
vy: number;
color: string;
particles: Particle[];
exploded: boolean;
dead: boolean;
}
export function FireworksEffect() {
const canvasRef = useRef<HTMLCanvasElement>(null)
const canvasRef = useRef<HTMLCanvasElement>(null);
useEffect(() => {
const canvas = canvasRef.current
if (!canvas) return
const canvas = canvasRef.current;
if (!canvas) return;
const ctx = canvas.getContext("2d")
if (!ctx) return
const ctx = canvas.getContext('2d');
if (!ctx) return;
let animationFrameId: number
let fireworks: Firework[] = []
let animationFrameId: number;
let fireworks: Firework[] = [];
const colors = [
"#ff0000", "#ff7f00", "#ffff00", "#00ff00", "#0000ff", "#4b0082", "#8f00ff",
"#ff1493", "#00bfff", "#ffd700", "#ff4500", "#32cd32"
]
'#ff0000',
'#ff7f00',
'#ffff00',
'#00ff00',
'#0000ff',
'#4b0082',
'#8f00ff',
'#ff1493',
'#00bfff',
'#ffd700',
'#ff4500',
'#32cd32',
];
const resizeCanvas = () => {
canvas.width = window.innerWidth
canvas.height = window.innerHeight
}
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
};
const createFirework = () => {
const x = Math.random() * canvas.width
const y = canvas.height
const x = Math.random() * canvas.width;
const y = canvas.height;
const targetY = canvas.height * 0.2 + Math.random() * (canvas.height * 0.5)
const speed = 8 + Math.random() * 5
const targetY =
canvas.height * 0.2 + Math.random() * (canvas.height * 0.5);
const speed = 8 + Math.random() * 5;
fireworks.push({
x,
@@ -63,17 +74,17 @@ export function FireworksEffect() {
color: colors[Math.floor(Math.random() * colors.length)],
particles: [],
exploded: false,
dead: false
})
}
dead: false,
});
};
const createParticles = (x: number, y: number, color: string) => {
const particleCount = 50 + Math.random() * 50
const particles: Particle[] = []
const particleCount = 50 + Math.random() * 50;
const particles: Particle[] = [];
for (let i = 0; i < particleCount; i++) {
const angle = Math.random() * Math.PI * 2
const speed = Math.random() * 5 + 1
const angle = Math.random() * Math.PI * 2;
const speed = Math.random() * 5 + 1;
particles.push({
x,
y,
@@ -81,90 +92,90 @@ export function FireworksEffect() {
vy: Math.sin(angle) * speed,
alpha: 1,
color: color,
decay: Math.random() * 0.01 + 0.005
})
decay: Math.random() * 0.01 + 0.005,
});
}
return particles
}
return particles;
};
const updateAndDraw = () => {
if (!ctx || !canvas) return
if (!ctx || !canvas) return;
ctx.globalCompositeOperation = "destination-out"
ctx.fillStyle = "rgba(0, 0, 0, 0.1)"
ctx.fillRect(0, 0, canvas.width, canvas.height)
ctx.globalCompositeOperation = 'destination-out';
ctx.fillStyle = 'rgba(0, 0, 0, 0.1)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.globalCompositeOperation = "lighter"
ctx.globalCompositeOperation = 'lighter';
if (Math.random() < 0.04) {
createFirework()
createFirework();
}
fireworks.forEach((fw) => {
if (fw.dead) return
if (fw.dead) return;
if (!fw.exploded) {
ctx.beginPath()
ctx.arc(fw.x, fw.y, 2, 0, Math.PI * 2)
ctx.fillStyle = fw.color
ctx.fill()
ctx.beginPath();
ctx.arc(fw.x, fw.y, 2, 0, Math.PI * 2);
ctx.fillStyle = fw.color;
ctx.fill();
fw.x += fw.vx
fw.y += fw.vy
fw.vy += 0.15
fw.x += fw.vx;
fw.y += fw.vy;
fw.vy += 0.15;
if (fw.vy >= 0 || fw.y <= fw.targetY) {
fw.exploded = true
fw.particles = createParticles(fw.x, fw.y, fw.color)
fw.exploded = true;
fw.particles = createParticles(fw.x, fw.y, fw.color);
}
} else {
for (let i = fw.particles.length - 1; i >= 0; i--) {
const p = fw.particles[i]
const p = fw.particles[i];
ctx.beginPath()
ctx.arc(p.x, p.y, 1.5, 0, Math.PI * 2)
ctx.fillStyle = p.color
ctx.globalAlpha = p.alpha
ctx.fill()
ctx.globalAlpha = 1
ctx.beginPath();
ctx.arc(p.x, p.y, 1.5, 0, Math.PI * 2);
ctx.fillStyle = p.color;
ctx.globalAlpha = p.alpha;
ctx.fill();
ctx.globalAlpha = 1;
p.x += p.vx
p.y += p.vy
p.vy += 0.1
p.alpha -= p.decay
p.x += p.vx;
p.y += p.vy;
p.vy += 0.1;
p.alpha -= p.decay;
if (p.alpha <= 0) {
fw.particles.splice(i, 1)
fw.particles.splice(i, 1);
}
}
if (fw.particles.length === 0) {
fw.dead = true
fw.dead = true;
}
}
})
});
fireworks = fireworks.filter(fw => !fw.dead)
fireworks = fireworks.filter((fw) => !fw.dead);
animationFrameId = requestAnimationFrame(updateAndDraw)
}
animationFrameId = requestAnimationFrame(updateAndDraw);
};
resizeCanvas()
updateAndDraw()
resizeCanvas();
updateAndDraw();
window.addEventListener("resize", resizeCanvas)
window.addEventListener('resize', resizeCanvas);
return () => {
cancelAnimationFrame(animationFrameId)
window.removeEventListener("resize", resizeCanvas)
}
}, [])
cancelAnimationFrame(animationFrameId);
window.removeEventListener('resize', resizeCanvas);
};
}, []);
return (
<canvas
ref={canvasRef}
className="fixed inset-0 pointer-events-none z-50"
style={{ background: "transparent" }}
className='fixed inset-0 pointer-events-none z-50'
style={{ background: 'transparent' }}
/>
)
);
}