This commit is contained in:
arthur 2025-12-12 19:58:27 +07:00
parent 899b5f57c2
commit 199317d0c6
5 changed files with 143 additions and 0 deletions

BIN
golden-ticket.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 614 KiB

BIN
result-remade.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 319 KiB

BIN
result.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 MiB

View File

@ -3,6 +3,7 @@ import { useLuckyDraw } from './hooks/useLuckyDraw';
import { SlotMachine } from './components/SlotMachine';
import { HistoryPanel } from './components/HistoryPanel';
import { WinnerModal } from './components/WinnerModal';
import { SparkleEffect } from './components/SparkleEffect';
import { Sparkles } from 'lucide-react';
function App() {
@ -109,6 +110,16 @@ function App() {
onDraw={handleDraw}
poolLength={pool.length}
/>
{/* Left Sparkles - Absolute positioned next to slot machine */}
<div className="absolute left-0 top-1/2 -translate-y-1/2 -translate-x-[calc(100%+20px)] w-[350px] h-[600px] pointer-events-none overflow-visible">
<SparkleEffect />
</div>
{/* Right Sparkles - Absolute positioned next to slot machine */}
<div className="absolute right-0 top-1/2 -translate-y-1/2 translate-x-[calc(100%+20px)] w-[350px] h-[600px] pointer-events-none overflow-visible">
<SparkleEffect />
</div>
</div>
</div>

View File

@ -0,0 +1,132 @@
import { useEffect, useState } from 'react';
/**
* Helper to generate random number within a range
*/
const random = (min, max) => Math.random() * (max - min) + min;
/**
* Component: Star
* Renders a 4-pointed glowing star (lens flare style)
*/
const Star = ({ style }) => {
return (
<div
className="absolute flex items-center justify-center mix-blend-screen pointer-events-none"
style={{
...style,
animationName: 'twinkle',
animationTimingFunction: 'ease-in-out',
animationIterationCount: 'infinite',
}}
>
{/* The Glow Center - Bright Core */}
<div className="absolute w-[10%] h-[10%] bg-white rounded-full shadow-[0_0_20px_5px_rgba(255,255,255,1),0_0_50px_20px_rgba(253,224,71,0.8)] z-20" />
{/* Vertical Ray - Sharp White Core */}
<div className="absolute w-[2px] h-full bg-gradient-to-b from-transparent via-white to-transparent opacity-100 z-10" />
{/* Vertical Ray - Gold Glow */}
<div className="absolute w-[6px] h-full bg-gradient-to-b from-transparent via-yellow-200 to-transparent opacity-60 blur-[2px]" />
{/* Horizontal Ray - Sharp White Core */}
<div className="absolute h-[2px] w-full bg-gradient-to-r from-transparent via-white to-transparent opacity-100 z-10" />
{/* Horizontal Ray - Gold Glow */}
<div className="absolute h-[6px] w-full bg-gradient-to-r from-transparent via-yellow-200 to-transparent opacity-60 blur-[2px]" />
{/* Diffuse Halo */}
<div className="absolute w-[70%] h-[70%] bg-amber-500/40 rounded-full blur-2xl" />
</div>
);
};
/**
* Component: Particle
* Renders tiny background dust/bokeh circles
*/
const Particle = ({ style }) => (
<div
className="absolute rounded-full bg-yellow-50 mix-blend-screen"
style={{
...style,
animationName: 'pulse',
animationTimingFunction: 'ease-in-out',
animationIterationCount: 'infinite',
}}
/>
);
export function SparkleEffect() {
const [stars, setStars] = useState([]);
const [particles, setParticles] = useState([]);
useEffect(() => {
// Generate Stars (The big cross flares) - spread across entire area
const starCount = 40; // Increased from 25
const newStars = Array.from({ length: starCount }).map((_, i) => {
return {
id: i,
style: {
left: `${random(0, 100)}%`,
top: `${random(0, 100)}%`,
width: `${random(50, 150)}px`,
height: `${random(50, 150)}px`,
animationDuration: `${random(1.5, 4)}s`,
animationDelay: `${random(0, 4)}s`,
opacity: random(0.6, 1),
},
};
});
// Generate Particles (The small bokeh dots) - spread across entire area
const particleCount = 100; // Increased from 60
const newParticles = Array.from({ length: particleCount }).map((_, i) => {
return {
id: i,
style: {
left: `${random(0, 100)}%`,
top: `${random(0, 100)}%`,
width: `${random(2, 5)}px`,
height: `${random(2, 5)}px`,
opacity: random(0.3, 0.8),
boxShadow: `0 0 ${random(3, 6)}px rgba(255, 255, 220, 0.8)`,
animationDuration: `${random(1, 3)}s`,
animationDelay: `${random(0, 5)}s`,
},
};
});
setStars(newStars);
setParticles(newParticles);
}, []);
return (
<div className="absolute inset-0 pointer-events-none">
{/* Custom Keyframes */}
<style>{`
@keyframes twinkle {
0% { transform: scale(0.3) rotate(0deg); opacity: 0; }
50% { transform: scale(1.2) rotate(15deg); opacity: 1; filter: brightness(2.5); }
100% { transform: scale(0.3) rotate(0deg); opacity: 0; }
}
@keyframes pulse {
0%, 100% { opacity: 0.2; transform: scale(0.5); }
50% { opacity: 1; transform: scale(1.3); filter: brightness(1.5); }
}
`}</style>
{/* Render Stars (Big cross flares) */}
<div className="absolute inset-0 z-10">
{stars.map((s) => (
<Star key={s.id} style={s.style} />
))}
</div>
{/* Render Particles (Small dots) */}
<div className="absolute inset-0 z-0">
{particles.map((p) => (
<Particle key={p.id} style={p.style} />
))}
</div>
</div>
);
}