@@ -42,6 +42,8 @@ style.textContent = `
42
42
.balloon {
43
43
position: absolute;
44
44
animation: float 15s ease-in-out infinite;
45
+ cursor: pointer;
46
+ pointer-events: auto;
45
47
}
46
48
.balloon-string {
47
49
width: 1px;
@@ -57,6 +59,7 @@ style.textContent = `
57
59
position: relative;
58
60
margin: 0 auto;
59
61
font-weight: bold;
62
+ animation: inflate 0.5s ease-out;
60
63
}
61
64
.balloon-body::before,
62
65
.balloon-body::after {
@@ -105,6 +108,10 @@ style.textContent = `
105
108
0% { transform: translateY(0) rotate(0deg); }
106
109
100% { transform: translateY(-100vh) rotate(720deg); }
107
110
}
111
+ @keyframes inflate {
112
+ 0% { transform: scale(0); }
113
+ 100% { transform: scale(1); }
114
+ }
108
115
@media (max-width: 600px) {
109
116
.banner-letter {
110
117
font-size: 16px;
@@ -164,6 +171,99 @@ function createBalloon() {
164
171
balloonBody . style . backgroundColor = `hsl(${ hue } , 100%, 70%)` ;
165
172
166
173
effectsContainer . appendChild ( balloon ) ;
174
+
175
+ // Add click event listener to pop the balloon
176
+ balloon . addEventListener ( 'click' , ( ) => {
177
+ popBalloon ( balloon ) ;
178
+ } ) ;
179
+ }
180
+
181
+ // Function to pop a balloon
182
+ function popBalloon ( balloon ) {
183
+ // Play pop sound
184
+ const popSound = new Audio ( `data:audio/mp3;base64,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
229
+ GaN65YQUGbsb/paBxEN98OhE1MRCujuutlB8QqYZUFBY8YgmCBZgQ7QyESqolC+icsmlK34dkFh9
230
+ dwHFKSmxayaA8RcgFHod1etQOR0MQCpEvKOWi5OljCRe2JJDxEwpODqxdBlhkdCIiqgQmFBIg4pA
231
+ sd5sh/w0SLBV/y91EHLanQbDxaaVBFiJaVVZ9bHIzLmAW//7YMTzAA7pKT3smRNh0ZznPYSZ5SAs
232
+ OFBITF0EqeAGChllhdJaLJYZas/7BSoE6GQDTIootUJkS7C7pokJFFpyIibThFFdslR3LBVBOhqG
233
+ 2JiFcjjCl1kcY0Mqu21n6xrLxsj6JkakaDgPHpVf7C87mFGVlh5kbWe1Jo4kVMmVCz8tupMgs19T
234
+ yMaRACSAFoUIlZ0M7x+2Q4YGvlTVlDAwaDlMQU1FMy4xMDBVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
235
+ VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
236
+ VVVVVVVVVVVVVf/7YMT1AA7VQTvssG3p1qRnvaMOXFVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
237
+ VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
238
+ VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
239
+ VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
240
+ VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVf/7QMT2gNBFSzfspG9g
241
+ YodiUPCNHVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
242
+ VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
243
+ VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV` ) ;
244
+ popSound . play ( ) ;
245
+
246
+ // Create pop effect
247
+ const popEffect = document . createElement ( 'div' ) ;
248
+ popEffect . style . position = 'absolute' ;
249
+ popEffect . style . left = balloon . style . left ;
250
+ popEffect . style . top = balloon . style . top ;
251
+ popEffect . style . width = '60px' ;
252
+ popEffect . style . height = '60px' ;
253
+ popEffect . style . borderRadius = '50%' ;
254
+ popEffect . style . backgroundColor = balloon . querySelector ( '.balloon-body' ) . style . backgroundColor ;
255
+ popEffect . style . opacity = '0.7' ;
256
+ popEffect . style . animation = 'pop 0.3s ease-out forwards' ;
257
+
258
+ effectsContainer . appendChild ( popEffect ) ;
259
+
260
+ // Remove the balloon
261
+ balloon . remove ( ) ;
262
+
263
+ // Remove the pop effect after animation
264
+ setTimeout ( ( ) => {
265
+ popEffect . remove ( ) ;
266
+ } , 300 ) ;
167
267
}
168
268
169
269
// Function to create a single confetti
@@ -190,11 +290,12 @@ function createConfettiEffect() {
190
290
}
191
291
}
192
292
193
-
194
293
// Function to stop the effect
195
294
function stopEffect ( ) {
196
295
effectsContainer . remove ( ) ;
197
296
document . removeEventListener ( 'keydown' , escapeKeyHandler ) ;
297
+ clearInterval ( createConfettiEffect ) ;
298
+ clearInterval ( createBalloon ) ;
198
299
}
199
300
200
301
// Event listener for Escape key
@@ -208,10 +309,10 @@ function escapeKeyHandler(event) {
208
309
export function startBirthday ( ) {
209
310
document . addEventListener ( 'keydown' , escapeKeyHandler ) ;
210
311
211
-
212
312
createBirthdayBanner ( ) ;
213
313
for ( let i = 0 ; i < 5 ; i ++ ) {
214
314
createBalloon ( ) ;
215
315
}
216
316
setInterval ( createConfettiEffect , 200 ) ;
317
+ setInterval ( createBalloon , 5000 ) ;
217
318
}
0 commit comments