1+ <!DOCTYPE html>
2+ < html >
3+ < head >
4+ < title > Happy Mothers' Day</ title >
5+ < style >
6+ body {
7+ margin : 0 ;
8+ padding : 0 ;
9+ font-family : consolas;
10+ background-color : black;
11+ overflow : hidden;
12+ }
13+ # fireworkCanvas {
14+ position : fixed;
15+ top : 0 ;
16+ left : 0 ;
17+ z-index : 1 ;
18+ }
19+ .message {
20+ position : fixed;
21+ top : 50% ;
22+ left : 50% ;
23+ transform : translate (-50% , -50% );
24+ color : white;
25+ font-size : 3em ;
26+ text-align : center;
27+ z-index : 2 ;
28+ pointer-events : none;
29+ opacity : 1 ;
30+ transition : opacity 3s ;
31+ }
32+ # fpsCounter {
33+ position : fixed;
34+ bottom : 10px ;
35+ right : 10px ;
36+ color : white;
37+ z-index : 3 ;
38+ }
39+ </ style >
40+ </ head >
41+ < body >
42+ < canvas id ="fireworkCanvas "> </ canvas >
43+ < div class ="message " id ="message "> Happy Mothers' Day!</ div >
44+ < div id ="fpsCounter "> </ div >
45+
46+ < script >
47+ ( function ( ) {
48+ // Performance monitoring
49+ let frameCount = 0 ;
50+ let lastFpsUpdate = 0 ;
51+ const fpsElement = document . getElementById ( 'fpsCounter' ) ;
52+
53+ // Canvas setup
54+ const canvas = document . getElementById ( 'fireworkCanvas' ) ;
55+ const ctx = canvas . getContext ( '2d' ) ;
56+
57+ // Particle system constants
58+ const MAX_PARTICLES = 2000 ;
59+ const FPS = 60 ;
60+ const frameInterval = 1000 / FPS ;
61+ let lastTime = 0 ;
62+
63+ // Initialize canvas
64+ function initCanvas ( ) {
65+ canvas . width = window . innerWidth ;
66+ canvas . height = window . innerHeight ;
67+ }
68+ initCanvas ( ) ;
69+ window . addEventListener ( 'resize' , initCanvas ) ;
70+
71+ // Particle pool
72+ const particlePool = [ ] ;
73+ let particles = [ ] ;
74+ let autoFireworkInterval ;
75+ let isStopping = false ;
76+ let currentInterval = 300 ; // Initial firework interval
77+
78+ // Get random color
79+ function getRandomColor ( ) {
80+ const r = Math . floor ( Math . random ( ) * 205 ) + 50 ;
81+ const g = Math . floor ( Math . random ( ) * 205 ) + 50 ;
82+ const b = Math . floor ( Math . random ( ) * 205 ) + 50 ;
83+ return `rgb(${ r } , ${ g } , ${ b } )` ;
84+ }
85+
86+ // Get particle from pool or create new
87+ function getParticle ( ) {
88+ if ( particlePool . length > 0 ) {
89+ return particlePool . pop ( ) ;
90+ }
91+ return {
92+ x : 0 , y : 0 , radius : 0 , color : '' ,
93+ velocity : { x : 0 , y : 0 } ,
94+ alpha : 1 , decay : 0 , gravity : 0
95+ } ;
96+ }
97+
98+ // Recycle particle
99+ function recycleParticle ( p ) {
100+ if ( particlePool . length < MAX_PARTICLES ) {
101+ particlePool . push ( p ) ;
102+ }
103+ }
104+
105+ // Create firework with more random position
106+ function createFirework ( x , y , isRandom = true ) {
107+ if ( particles . length > MAX_PARTICLES * 0.9 ) return ;
108+
109+ // If position not specified, generate random position
110+ if ( isRandom ) {
111+ x = Math . random ( ) * ( canvas . width * 0.7 ) + canvas . width * 0.15 ;
112+ y = Math . random ( ) * ( canvas . height * 0.7 ) + canvas . height * 0.15 ;
113+ } else {
114+ x = Math . random ( ) * canvas . width ;
115+ y = Math . random ( ) * canvas . height ;
116+ }
117+
118+ const radius = Math . random ( ) * 30 + 30 ;
119+ const particleCount = Math . min ( Math . floor ( radius * 8 ) , MAX_PARTICLES - particles . length ) ;
120+ const color = getRandomColor ( ) ;
121+
122+ for ( let i = 0 ; i < particleCount ; i ++ ) {
123+ const angle = Math . random ( ) * Math . PI * 2 ;
124+ const speed = Math . random ( ) * 5 + 2 ;
125+
126+ const p = getParticle ( ) ;
127+ p . x = x ;
128+ p . y = y ;
129+ p . radius = Math . random ( ) * 3 + 1 ;
130+ p . color = color ;
131+ p . velocity . x = Math . cos ( angle ) * speed ;
132+ p . velocity . y = Math . sin ( angle ) * speed ;
133+ p . alpha = 1 ;
134+ p . decay = Math . random ( ) * 0.01 + 0.005 ;
135+ p . gravity = 0.02 + Math . random ( ) * 0.03 ;
136+
137+ particles . push ( p ) ;
138+ }
139+ }
140+
141+ // Start auto fireworks with better distribution
142+ function startAutoFireworks ( ) {
143+ clearInterval ( autoFireworkInterval ) ;
144+
145+ autoFireworkInterval = setInterval ( ( ) => {
146+ if ( isStopping ) {
147+ // Gradually increase interval to slow down firework generation
148+ currentInterval += 50 ;
149+ if ( currentInterval > 2000 ) {
150+ clearInterval ( autoFireworkInterval ) ;
151+ return ;
152+ }
153+ clearInterval ( autoFireworkInterval ) ;
154+ startAutoFireworks ( ) ;
155+ }
156+
157+ // Create firework at random position
158+ createFirework ( ) ;
159+
160+ } , currentInterval ) ;
161+
162+ // Initial fireworks in different positions
163+ createFirework ( canvas . width * 0.2 , canvas . height * 0.3 , false ) ;
164+ createFirework ( canvas . width * 0.5 , canvas . height * 0.4 , false ) ;
165+ createFirework ( canvas . width * 0.8 , canvas . height * 0.35 , false ) ;
166+ createFirework ( canvas . width * 0.3 , canvas . height * 0.25 , false ) ;
167+ createFirework ( canvas . width * 0.7 , canvas . height * 0.3 , false ) ;
168+ }
169+
170+ // Smoothly stop fireworks
171+ function stopFireworksSmoothly ( ) {
172+ isStopping = true ;
173+
174+ // After a while, increase particle decay to make them fade faster
175+ setTimeout ( ( ) => {
176+ particles . forEach ( p => {
177+ p . decay += 0.01 ;
178+ } ) ;
179+ } , 2000 ) ;
180+ }
181+
182+ // Update particles
183+ function updateParticles ( ) {
184+ for ( let i = particles . length - 1 ; i >= 0 ; i -- ) {
185+ const p = particles [ i ] ;
186+
187+ p . x += p . velocity . x ;
188+ p . y += p . velocity . y ;
189+ p . velocity . y += p . gravity ;
190+ p . velocity . y *= 0.99 ;
191+ p . alpha -= p . decay ;
192+
193+ if ( p . alpha <= 0 ) {
194+ recycleParticle ( particles [ i ] ) ;
195+ particles . splice ( i , 1 ) ;
196+ }
197+ }
198+ }
199+
200+ // Draw particles
201+ function drawParticles ( ) {
202+ ctx . save ( ) ;
203+ for ( let i = 0 , len = particles . length ; i < len ; i ++ ) {
204+ const p = particles [ i ] ;
205+
206+ ctx . beginPath ( ) ;
207+ ctx . arc ( p . x , p . y , p . radius , 0 , Math . PI * 2 ) ;
208+
209+ const gradient = ctx . createRadialGradient (
210+ p . x , p . y , 0 ,
211+ p . x , p . y , p . radius
212+ ) ;
213+ gradient . addColorStop ( 0 , p . color . replace ( ')' , `, ${ p . alpha } )` ) . replace ( 'rgb' , 'rgba' ) ) ;
214+ gradient . addColorStop ( 1 , 'rgba(0,0,0,0)' ) ;
215+
216+ ctx . fillStyle = gradient ;
217+ ctx . fill ( ) ;
218+ }
219+ ctx . restore ( ) ;
220+ }
221+
222+ // Animation loop
223+ function animate ( timestamp ) {
224+ // Throttle FPS
225+ if ( timestamp - lastTime < frameInterval ) {
226+ requestAnimationFrame ( animate ) ;
227+ return ;
228+ }
229+ lastTime = timestamp ;
230+
231+ // Update FPS counter
232+ frameCount ++ ;
233+ if ( timestamp - lastFpsUpdate >= 1000 ) {
234+ fpsElement . textContent = `FPS: ${ frameCount } ` ;
235+ frameCount = 0 ;
236+ lastFpsUpdate = timestamp ;
237+ }
238+
239+ // Clear with trail effect
240+ ctx . fillStyle = 'rgba(0, 0, 0, 0.1)' ;
241+ ctx . fillRect ( 0 , 0 , canvas . width , canvas . height ) ;
242+
243+ updateParticles ( ) ;
244+ drawParticles ( ) ;
245+
246+ requestAnimationFrame ( animate ) ;
247+ }
248+
249+ // Start animation
250+ startAutoFireworks ( ) ;
251+ animate ( 0 ) ;
252+
253+ // Message fade out and start smooth stopping
254+ setTimeout ( ( ) => {
255+ document . getElementById ( 'message' ) . style . opacity = 0 ;
256+ setTimeout ( stopFireworksSmoothly , 1000 ) ;
257+ } , 3000 ) ;
258+
259+ setTimeout ( ( ) => {
260+ document . getElementById ( 'message' ) . style . display = 'none' ;
261+ } , 6000 ) ;
262+
263+ // Click to create firework at mouse position
264+ canvas . addEventListener ( 'click' , ( e ) => {
265+ createFirework ( e . clientX , e . clientY , false ) ;
266+ } ) ;
267+
268+ } ) ( ) ;
269+ </ script >
270+ </ body >
271+ </ html >
0 commit comments