CodingPenguin44 commited on
Commit
f79f393
·
verified ·
1 Parent(s): ed0a231

Upload folder using huggingface_hub

Browse files
Files changed (1) hide show
  1. index.html +664 -19
index.html CHANGED
@@ -1,19 +1,664 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="de">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Alles Gute zum Geburtstag!</title>
7
+ <!-- Google Fonts -->
8
+ <link href="https://fonts.googleapis.com/css2?family=Fredoka:wght@300;400;600&family=Poppins:wght@300;400;700&display=swap" rel="stylesheet">
9
+ <!-- FontAwesome Icons -->
10
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
11
+
12
+ <style>
13
+ :root {
14
+ --primary-color: #ff6b6b;
15
+ --secondary-color: #4ecdc4;
16
+ --accent-color: #ffe66d;
17
+ --dark-bg: #2c3e50;
18
+ --light-bg: #f7f9fc;
19
+ --text-color: #333;
20
+ --card-bg: rgba(255, 255, 255, 0.9);
21
+ --font-heading: 'Fredoka', sans-serif;
22
+ --font-body: 'Poppins', sans-serif;
23
+ }
24
+
25
+ * {
26
+ margin: 0;
27
+ padding: 0;
28
+ box-sizing: border-box;
29
+ }
30
+
31
+ body {
32
+ font-family: var(--font-body);
33
+ background-color: var(--light-bg);
34
+ color: var(--text-color);
35
+ overflow-x: hidden;
36
+ min-height: 100vh;
37
+ display: flex;
38
+ flex-direction: column;
39
+ }
40
+
41
+ /* Canvas for Confetti */
42
+ #confetti-canvas {
43
+ position: fixed;
44
+ top: 0;
45
+ left: 0;
46
+ width: 100%;
47
+ height: 100%;
48
+ pointer-events: none;
49
+ z-index: 1;
50
+ }
51
+
52
+ /* Header */
53
+ header {
54
+ display: flex;
55
+ justify-content: space-between;
56
+ align-items: center;
57
+ padding: 1.5rem 5%;
58
+ background: rgba(255, 255, 255, 0.8);
59
+ backdrop-filter: blur(10px);
60
+ position: fixed;
61
+ width: 100%;
62
+ top: 0;
63
+ z-index: 100;
64
+ box-shadow: 0 2px 10px rgba(0,0,0,0.05);
65
+ }
66
+
67
+ .logo {
68
+ font-family: var(--font-heading);
69
+ font-size: 1.5rem;
70
+ font-weight: 600;
71
+ color: var(--primary-color);
72
+ text-decoration: none;
73
+ display: flex;
74
+ align-items: center;
75
+ gap: 0.5rem;
76
+ }
77
+
78
+ .logo span {
79
+ color: var(--secondary-color);
80
+ }
81
+
82
+ .anycoder-link {
83
+ font-size: 0.8rem;
84
+ color: #888;
85
+ text-decoration: none;
86
+ transition: color 0.3s;
87
+ }
88
+
89
+ .anycoder-link:hover {
90
+ color: var(--primary-color);
91
+ }
92
+
93
+ /* Main Content */
94
+ main {
95
+ flex: 1;
96
+ display: flex;
97
+ flex-direction: column;
98
+ align-items: center;
99
+ justify-content: center;
100
+ padding: 6rem 1rem 2rem;
101
+ z-index: 2;
102
+ text-align: center;
103
+ }
104
+
105
+ /* Hero Section */
106
+ .hero {
107
+ margin-bottom: 4rem;
108
+ animation: fadeInDown 1s ease-out;
109
+ }
110
+
111
+ .hero h1 {
112
+ font-family: var(--font-heading);
113
+ font-size: 4rem;
114
+ margin-bottom: 1rem;
115
+ background: linear-gradient(45deg, var(--primary-color), var(--secondary-color));
116
+ -webkit-background-clip: text;
117
+ -webkit-text-fill-color: transparent;
118
+ line-height: 1.2;
119
+ }
120
+
121
+ .hero p {
122
+ font-size: 1.2rem;
123
+ color: #666;
124
+ max-width: 600px;
125
+ margin: 0 auto;
126
+ margin-bottom: 2rem;
127
+ }
128
+
129
+ .scroll-down {
130
+ animation: bounce 2s infinite;
131
+ color: var(--primary-color);
132
+ font-size: 2rem;
133
+ cursor: pointer;
134
+ }
135
+
136
+ /* Interactive Cake Section */
137
+ .cake-section {
138
+ background: var(--card-bg);
139
+ padding: 3rem;
140
+ border-radius: 20px;
141
+ box-shadow: 0 10px 30px rgba(0,0,0,0.1);
142
+ margin-bottom: 4rem;
143
+ display: flex;
144
+ flex-direction: column;
145
+ align-items: center;
146
+ max-width: 500px;
147
+ width: 100%;
148
+ transform: translateY(0);
149
+ transition: transform 0.3s;
150
+ }
151
+
152
+ .cake-section:hover {
153
+ transform: translateY(-5px);
154
+ }
155
+
156
+ .cake-container {
157
+ display: flex;
158
+ justify-content: center;
159
+ align-items: flex-end;
160
+ height: 200px;
161
+ position: relative;
162
+ margin-bottom: 2rem;
163
+ }
164
+
165
+ /* CSS Only Cake */
166
+ .plate {
167
+ width: 300px;
168
+ height: 15px;
169
+ background: #ddd;
170
+ border-radius: 10px;
171
+ position: absolute;
172
+ bottom: 0;
173
+ }
174
+
175
+ .layer {
176
+ position: absolute;
177
+ bottom: 15px;
178
+ border-radius: 10px 10px 0 0;
179
+ }
180
+
181
+ .layer-bottom {
182
+ width: 280px;
183
+ height: 80px;
184
+ background: var(--primary-color);
185
+ left: 10px;
186
+ }
187
+
188
+ .layer-middle {
189
+ width: 240px;
190
+ height: 60px;
191
+ background: var(--secondary-color);
192
+ left: 30px;
193
+ }
194
+
195
+ .layer-top {
196
+ width: 200px;
197
+ height: 40px;
198
+ background: var(--accent-color);
199
+ left: 50px;
200
+ }
201
+
202
+ .candle {
203
+ position: absolute;
204
+ bottom: 95px; /* Adjust based on layer height */
205
+ width: 8px;
206
+ height: 40px;
207
+ background: repeating-linear-gradient(
208
+ to bottom,
209
+ #fff,
210
+ #fff 5px,
211
+ var(--secondary-color) 5px,
212
+ var(--secondary-color) 10px
213
+ );
214
+ border-radius: 4px 4px 0 0;
215
+ cursor: pointer;
216
+ transition: transform 0.2s;
217
+ z-index: 5;
218
+ }
219
+
220
+ .candle:hover {
221
+ transform: scale(1.1);
222
+ }
223
+
224
+ .flame {
225
+ position: absolute;
226
+ top: -25px;
227
+ left: 50%;
228
+ transform: translateX(-50%);
229
+ width: 12px;
230
+ height: 25px;
231
+ background: radial-gradient(ellipse at bottom, #ffd700 0%, #ff8c00 100%);
232
+ border-radius: 50% 50% 20% 20%;
233
+ animation: flicker 1s infinite alternate;
234
+ box-shadow: 0 0 10px #ff8c00;
235
+ }
236
+
237
+ .instruction {
238
+ font-size: 1rem;
239
+ color: #777;
240
+ font-weight: 500;
241
+ }
242
+
243
+ .instruction i {
244
+ color: var(--primary-color);
245
+ animation: pulse 1.5s infinite;
246
+ }
247
+
248
+ /* Gift Box Section */
249
+ .gift-section {
250
+ position: relative;
251
+ width: 100%;
252
+ max-width: 600px;
253
+ perspective: 1000px;
254
+ }
255
+
256
+ .gift-box {
257
+ width: 200px;
258
+ height: 200px;
259
+ background: var(--primary-color);
260
+ margin: 0 auto 2rem;
261
+ position: relative;
262
+ cursor: pointer;
263
+ border-radius: 10px;
264
+ box-shadow: 0 15px 35px rgba(255, 107, 107, 0.3);
265
+ transition: transform 0.6s cubic-bezier(0.68, -0.55, 0.265, 1.55);
266
+ display: flex;
267
+ align-items: center;
268
+ justify-content: center;
269
+ }
270
+
271
+ .gift-box.opened {
272
+ transform: rotateY(180deg) scale(0.9);
273
+ opacity: 0;
274
+ pointer-events: none;
275
+ }
276
+
277
+ .gift-lid {
278
+ position: absolute;
279
+ top: -30px;
280
+ left: -20px;
281
+ width: 240px;
282
+ height: 40px;
283
+ background: #ff4757;
284
+ border-radius: 5px;
285
+ z-index: 2;
286
+ transition: transform 0.6s;
287
+ }
288
+
289
+ .gift-box.opened .gift-lid {
290
+ transform: rotateX(-180deg) translateY(-10px);
291
+ }
292
+
293
+ .gift-ribbon-v {
294
+ position: absolute;
295
+ width: 40px;
296
+ height: 100%;
297
+ background: var(--secondary-color);
298
+ }
299
+
300
+ .gift-ribbon-h {
301
+ position: absolute;
302
+ width: 100%;
303
+ height: 40px;
304
+ background: var(--secondary-color);
305
+ top: 0;
306
+ }
307
+
308
+ .gift-handle {
309
+ position: absolute;
310
+ width: 100px;
311
+ height: 20px;
312
+ border: 5px solid var(--secondary-color);
313
+ border-bottom: none;
314
+ border-radius: 10px 10px 0 0;
315
+ top: 20px;
316
+ left: 50%;
317
+ transform: translateX(-50%);
318
+ z-index: 1;
319
+ }
320
+
321
+ .message-card {
322
+ background: white;
323
+ padding: 2rem;
324
+ border-radius: 15px;
325
+ box-shadow: 0 10px 25px rgba(0,0,0,0.1);
326
+ text-align: left;
327
+ transform: rotateY(-90deg); /* Initial hidden state */
328
+ transition: transform 0.6s;
329
+ position: absolute;
330
+ top: 0;
331
+ left: 0;
332
+ width: 100%;
333
+ height: 100%;
334
+ z-index: 10;
335
+ display: flex;
336
+ flex-direction: column;
337
+ justify-content: center;
338
+ align-items: center;
339
+ }
340
+
341
+ .gift-box.opened ~ .message-card {
342
+ transform: rotateY(0deg);
343
+ }
344
+
345
+ .message-card h3 {
346
+ color: var(--primary-color);
347
+ margin-bottom: 1rem;
348
+ }
349
+
350
+ .message-card p {
351
+ font-size: 1rem;
352
+ line-height: 1.6;
353
+ color: #555;
354
+ }
355
+
356
+ /* Toast Notification */
357
+ .toast {
358
+ position: fixed;
359
+ bottom: 30px;
360
+ left: 50%;
361
+ transform: translateX(-50%) translateY(100px);
362
+ background: #333;
363
+ color: #fff;
364
+ padding: 12px 24px;
365
+ border-radius: 30px;
366
+ box-shadow: 0 5px 15px rgba(0,0,0,0.2);
367
+ opacity: 0;
368
+ transition: all 0.4s ease;
369
+ z-index: 200;
370
+ display: flex;
371
+ align-items: center;
372
+ gap: 10px;
373
+ }
374
+
375
+ .toast.show {
376
+ transform: translateX(-50%) translateY(0);
377
+ opacity: 1;
378
+ }
379
+
380
+ /* Footer */
381
+ footer {
382
+ padding: 2rem;
383
+ text-align: center;
384
+ color: #888;
385
+ font-size: 0.9rem;
386
+ background: white;
387
+ width: 100%;
388
+ z-index: 2;
389
+ }
390
+
391
+ /* Animations */
392
+ @keyframes float {
393
+ 0%, 100% { transform: translateY(0); }
394
+ 50% { transform: translateY(-20px); }
395
+ }
396
+
397
+ @keyframes flicker {
398
+ 0% { transform: translateX(-50%) scale(1); opacity: 1; }
399
+ 100% { transform: translateX(-50%) scale(0.9); opacity: 0.8; }
400
+ }
401
+
402
+ @keyframes pulse {
403
+ 0%, 100% { transform: scale(1); }
404
+ 50% { transform: scale(1.2); }
405
+ }
406
+
407
+ @keyframes bounce {
408
+ 0%, 20%, 50%, 80%, 100% { transform: translateY(0); }
409
+ 40% { transform: translateY(-10px); }
410
+ 60% { transform: translateY(-5px); }
411
+ }
412
+
413
+ @keyframes fadeInDown {
414
+ from { opacity: 0; transform: translateY(-30px); }
415
+ to { opacity: 1; transform: translateY(0); }
416
+ }
417
+
418
+ /* Responsive */
419
+ @media (max-width: 768px) {
420
+ .hero h1 {
421
+ font-size: 2.5rem;
422
+ }
423
+ .cake-container {
424
+ height: 150px;
425
+ }
426
+ .layer-bottom { width: 220px; height: 60px; }
427
+ .layer-middle { width: 180px; height: 45px; }
428
+ .layer-top { width: 140px; height: 30px; }
429
+ .candle { bottom: 75px; height: 30px; }
430
+ .plate { width: 220px; }
431
+ }
432
+ </style>
433
+ </head>
434
+ <body>
435
+
436
+ <canvas id="confetti-canvas"></canvas>
437
+
438
+ <header>
439
+ <a href="#" class="logo"><i class="fa-solid fa-cake-candles"></i> Happy <span>Birthday</span></a>
440
+ <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="anycoder-link">Built with anycoder</a>
441
+ </header>
442
+
443
+ <main>
444
+ <section class="hero">
445
+ <h1 id="typing-text"></h1>
446
+ <p>Hier ist ein kleines Stück Glück für dich!</p>
447
+ <div class="scroll-down" onclick="scrollToContent()">
448
+ <i class="fa-solid fa-chevron-down"></i>
449
+ </div>
450
+ </section>
451
+
452
+ <section class="cake-section">
453
+ <div class="cake-container">
454
+ <div class="plate"></div>
455
+ <div class="layer layer-bottom"></div>
456
+ <div class="layer layer-middle"></div>
457
+ <div class="layer layer-top"></div>
458
+
459
+ <!-- Candles that can be blown out -->
460
+ <div class="candle" style="left: 80px;" onclick="blowCandle(this, event)"><div class="flame"></div></div>
461
+ <div class="candle" style="left: 120px;" onclick="blowCandle(this, event)"><div class="flame"></div></div>
462
+ <div class="candle" style="left: 160px;" onclick="blowCandle(this, event)"><div class="flame"></div></div>
463
+ </div>
464
+ <p class="instruction"><i class="fa-regular fa-hand-pointer"></i> Klicke auf die Kerzen, um sie auszublähen!</p>
465
+ </section>
466
+
467
+ <section class="gift-section">
468
+ <div class="gift-box" id="giftBox" onclick="openGift()">
469
+ <div class="gift-lid"></div>
470
+ <div class="gift-ribbon-v"></div>
471
+ <div class="gift-ribbon-h"></div>
472
+ <div class="gift-handle"></div>
473
+ <div class="message-card">
474
+ <h3>Alles Gute zum Geburtstag!</h3>
475
+ <p>Mögest du ein tolles Jahr haben voller Erfolg, Freude und süßen Überraschungen. Genieß den Tag!</p>
476
+ <button onclick="resetGift()" style="margin-top: 1rem; padding: 8px 16px; background: var(--secondary-color); border: none; border-radius: 20px; cursor: pointer; color: white;">Neu öffnen</button>
477
+ </div>
478
+ </div>
479
+ </section>
480
+ </main>
481
+
482
+ <div id="toast" class="toast">
483
+ <i class="fa-regular fa-face-smile"></i> <span id="toast-message">Message</span>
484
+ </div>
485
+
486
+ <footer>
487
+ <p>&copy; 2023 Happy Birthday Website. Made with <i class="fa-solid fa-heart" style="color: var(--primary-color);"></i> by you.</p>
488
+ </footer>
489
+
490
+ <script>
491
+ // --- Typing Effect ---
492
+ const message = "Herzlichen Glückwunsch!";
493
+ const typingElement = document.getElementById('typing-text');
494
+ let charIndex = 0;
495
+
496
+ function typeWriter() {
497
+ if (charIndex < message.length) {
498
+ typingElement.innerHTML += message.charAt(charIndex);
499
+ charIndex++;
500
+ setTimeout(typeWriter, 150);
501
+ } else {
502
+ // Add sparkle effect after typing
503
+ typingElement.style.animation = "float 3s ease-in-out infinite";
504
+ }
505
+ }
506
+
507
+ // Start typing after a short delay
508
+ window.onload = () => {
509
+ setTimeout(typeWriter, 500);
510
+ startConfetti();
511
+ };
512
+
513
+ function scrollToContent() {
514
+ document.querySelector('.cake-section').scrollIntoView({ behavior: 'smooth' });
515
+ }
516
+
517
+ // --- Candle Blowing Logic ---
518
+ function blowCandle(element, event) {
519
+ event.stopPropagation(); // Prevent opening gift box if clicking candle
520
+ const flame = element.querySelector('.flame');
521
+
522
+ if (flame.style.opacity === '0') return; // Already gone
523
+
524
+ // Blow effect
525
+ flame.style.transition = "all 0.5s ease-out";
526
+ flame.style.transform = "translateX(-50%) scale(0.5) translateY(-10px)";
527
+ flame.style.opacity = "0";
528
+
529
+ // Trigger confetti burst
530
+ createConfettiBurst(event.clientX, event.clientY);
531
+
532
+ // Check if all candles are blown
533
+ const allCandles = document.querySelectorAll('.candle');
534
+ const allBlown = Array.from(allCandles).every(c => c.querySelector('.flame').style.opacity === '0');
535
+
536
+ if (allBlown) {
537
+ showToast("Alle Kerzen sind aus! Das ist ein Geburtstag!");
538
+ // Trigger giant confetti
539
+ for(let i=0; i<5; i++) {
540
+ setTimeout(() => {
541
+ createConfettiBurst(window.innerWidth/2, window.innerHeight/2);
542
+ }, i * 300);
543
+ }
544
+ }
545
+ }
546
+
547
+ // --- Gift Box Logic ---
548
+ function openGift() {
549
+ const gift = document.getElementById('giftBox');
550
+ if (!gift.classList.contains('opened')) {
551
+ gift.classList.add('opened');
552
+ showToast("Glückwunsch! Eine Nachricht für dich!");
553
+ createConfettiBurst(window.innerWidth/2, window.innerHeight/2);
554
+ }
555
+ }
556
+
557
+ function resetGift() {
558
+ const gift = document.getElementById('giftBox');
559
+ gift.classList.remove('opened');
560
+ }
561
+
562
+ // --- Toast Notification ---
563
+ function showToast(message) {
564
+ const toast = document.getElementById('toast');
565
+ const toastMsg = document.getElementById('toast-message');
566
+ toastMsg.textContent = message;
567
+ toast.classList.add('show');
568
+ setTimeout(() => {
569
+ toast.classList.remove('show');
570
+ }, 3000);
571
+ }
572
+
573
+ // --- Confetti System ---
574
+ const canvas = document.getElementById('confetti-canvas');
575
+ const ctx = canvas.getContext('2d');
576
+ let particles = [];
577
+
578
+ function resizeCanvas() {
579
+ canvas.width = window.innerWidth;
580
+ canvas.height = window.innerHeight;
581
+ }
582
+ window.addEventListener('resize', resizeCanvas);
583
+ resizeCanvas();
584
+
585
+ class Particle {
586
+ constructor(x, y, isBurst = false) {
587
+ this.x = x || Math.random() * canvas.width;
588
+ this.y = y || Math.random() * canvas.height - canvas.height;
589
+ this.size = Math.random() * 8 + 4;
590
+ this.speedY = Math.random() * 3 + 1;
591
+ this.speedX = (Math.random() - 0.5) * 2;
592
+ this.color = `hsl(${Math.random() * 360}, 70%, 50%)`;
593
+ this.rotation = Math.random() * 360;
594
+ this.rotationSpeed = (Math.random() - 0.5) * 5;
595
+ this.isBurst = isBurst;
596
+
597
+ if (isBurst) {
598
+ this.vx = (Math.random() - 0.5) * 15;
599
+ this.vy = (Math.random() - 0.5) * 15;
600
+ }
601
+ }
602
+
603
+ update() {
604
+ if (this.isBurst) {
605
+ this.x += this.vx;
606
+ this.y += this.vy;
607
+ this.vy += 0.2; // Gravity
608
+ this.vx *= 0.95; // Friction
609
+ } else {
610
+ this.y += this.speedY;
611
+ this.x += Math.sin(this.y / 50) * 0.5; // Sway effect
612
+ }
613
+
614
+ this.rotation += this.rotationSpeed;
615
+
616
+ if (this.y > canvas.height) {
617
+ this.y = -20;
618
+ this.x = Math.random() * canvas.width;
619
+ }
620
+ }
621
+
622
+ draw() {
623
+ ctx.save();
624
+ ctx.translate(this.x, this.y);
625
+ ctx.rotate((this.rotation * Math.PI) / 180);
626
+ ctx.fillStyle = this.color;
627
+ ctx.fillRect(-this.size / 2, -this.size / 2, this.size, this.size);
628
+ ctx.restore();
629
+ }
630
+ }
631
+
632
+ function initParticles(count) {
633
+ for (let i = 0; i < count; i++) {
634
+ particles.push(new Particle());
635
+ }
636
+ }
637
+
638
+ function createConfettiBurst(x, y) {
639
+ for (let i = 0; i < 30; i++) {
640
+ particles.push(new Particle(x, y, true));
641
+ }
642
+ }
643
+
644
+ function startConfetti() {
645
+ initParticles(100);
646
+ animate();
647
+ }
648
+
649
+ function animate() {
650
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
651
+ particles.forEach((p, index) => {
652
+ p.update();
653
+ p.draw();
654
+ // Remove burst particles that are off screen or slow
655
+ if (p.isBurst && (p.x < 0 || p.x > canvas.width || p.y > canvas.height)) {
656
+ // Optional: remove burst particles to save memory,
657
+ // but keeping them creates a nice pile effect
658
+ }
659
+ });
660
+ requestAnimationFrame(animate);
661
+ }
662
+ </script>
663
+ </body>
664
+ </html>