falling-animation 0.1.3 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -276,16 +276,16 @@ interface FireworksOptions {
276
276
  // Colors for fireworks (default: 10 festive colors)
277
277
  colors?: string[];
278
278
 
279
- // Rockets per second (default: 1)
279
+ // Rockets per second (default: 0.5)
280
280
  launchRate?: number;
281
281
 
282
282
  // Particles per explosion (default: 50)
283
283
  particlesPerExplosion?: number;
284
284
 
285
- // Rocket speed range (default: { min: 8, max: 15 })
285
+ // Rocket speed range (default: { min: 7, max: 12 })
286
286
  rocketSpeed?: { min: number; max: number };
287
287
 
288
- // Explosion particle speed (default: { min: 2, max: 8 })
288
+ // Explosion particle speed (default: { min: 1, max: 6 })
289
289
  explosionSpeed?: { min: number; max: number };
290
290
 
291
291
  // Particle size in px (default: { min: 2, max: 6 })
@@ -300,6 +300,11 @@ interface FireworksOptions {
300
300
  // Auto start (default: true)
301
301
  autoStart?: boolean;
302
302
 
303
+ // Explosion pattern (default: 'random')
304
+ // Values: 'random' | 'circular' | 'double' | 'embers' | 'heart' | 'star' | 'ring' | 'palm' | 'willow' | 'chrysanthemum'
305
+ // Can also be an array: ['double', 'heart']
306
+ explosionPattern?: ExplosionPattern | ExplosionPattern[];
307
+
303
308
  // Z-index (default: 9999)
304
309
  zIndex?: number;
305
310
  }
@@ -333,49 +338,93 @@ fw.getIsRunning();
333
338
 
334
339
  ### Fireworks Examples
335
340
 
336
- #### Basic Fireworks Show
341
+ ### Explosion Patterns
342
+
343
+ You can choose from 9 different explosion patterns or use a random mix!
344
+
345
+ - `random`: Randomly selects a pattern for each explosion (default)
346
+ - `circular`: Standard circular explosion
347
+ - `double`: **Spectacular 2-stage explosion** (particles explode again!)
348
+ - `waterfall`: **Waterfall effect** (gentle up, heavy rain down)
349
+ - `embers`: Slow-falling, micro-particles (Tàn Lửa)
350
+ - `heart`: ❤️ Heart shape
351
+ - `star`: ⭐ Star shape
352
+ - `ring`: 💍 Ring shape
353
+ - `palm`: 🌴 Palm tree effect
354
+ - `willow`: 🌳 Trailing willow effect
355
+ - `chrysanthemum`: 🌼 Dense spherical burst
356
+
357
+ ### ✨ Recommended Presets
358
+
359
+ Here are some beautiful configurations to get you started:
360
+
361
+ #### 1. The Grand Finale (Spectacular Mix)
362
+ Perfect for big celebrations. Uses double explosions and a mix of random patterns.
337
363
 
338
364
  ```javascript
339
- import { Fireworks } from 'falling-animation';
365
+ const fireworks = new Fireworks({
366
+ launchRate: 2,
367
+ particlesPerExplosion: 50,
368
+ explosionPattern: ['double', 'random'], // Mix of single and double explosions
369
+ rocketSpeed: { min: 12, max: 18 },
370
+ explosionSpeed: { min: 3, max: 9 }
371
+ });
372
+ ```
373
+
374
+ #### 2. Romantic Hearts
375
+ A gentle stream of heart-shaped fireworks, great for weddings or Valentine's.
340
376
 
377
+ ```javascript
341
378
  new Fireworks({
342
- launchRate: 2,
343
- particlesPerExplosion: 60
379
+ launchRate: 1,
380
+ particlesPerExplosion: 40,
381
+ explosionPattern: 'heart',
382
+ colors: ['#ff0000', '#ff69b4', '#ffffff'], // Red, Pink, White
383
+ gravity: 0.05, // Slower fall
384
+ rocketSpeed: { min: 10, max: 12 }
344
385
  });
345
386
  ```
346
387
 
347
- #### Custom Colors
388
+ #### 3. Gentle Embers (Tàn Lửa)
389
+ Soft, floating micro-particles that drift slowly. Very atmospheric.
348
390
 
349
391
  ```javascript
350
392
  new Fireworks({
351
- colors: ['#ff0000', '#00ff00', '#0000ff', '#ffff00'],
352
- launchRate: 1.5
393
+ launchRate: 3,
394
+ explosionPattern: 'embers',
395
+ rocketSpeed: { min: 8, max: 12 },
396
+ particleLifetime: { min: 2000, max: 4000 },
397
+ gravity: 0.05
353
398
  });
354
399
  ```
355
400
 
356
- #### New Year Celebration
401
+ #### 4. New Year Countdown (Intense)
402
+ High density, fast-paced action!
357
403
 
358
404
  ```javascript
359
405
  const fw = new Fireworks({
360
- launchRate: 3,
361
- particlesPerExplosion: 100,
362
- gravity: 0.15,
363
- particleLifetime: { min: 1500, max: 2500 }
406
+ launchRate: 4, // Fast launch
407
+ particlesPerExplosion: 60, // Dense explosions
408
+ explosionPattern: 'random',
409
+ explosionSpeed: { min: 5, max: 10 }
364
410
  });
365
411
 
366
- // Launch a burst at midnight!
367
- fw.burst(10);
412
+ // Launch a massive burst manually when the timer hits zero!
413
+ // fw.burst(15);
368
414
  ```
369
415
 
370
- #### Bounded Container
416
+ #### 5. Single Shot (Manual Control)
417
+ Want to trigger fireworks manually (e.g., on button click)?
371
418
 
372
419
  ```javascript
373
- new Fireworks({
374
- container: '#celebration-box',
375
- launchRate: 0.5,
376
- particlesPerExplosion: 30,
377
- zIndex: 100
420
+ const fw = new Fireworks({
421
+ autoStart: false // 1. Disable auto-start
378
422
  });
423
+
424
+ // 2. Trigger manually whenever you want (e.g. onClick)
425
+ fw.launch(); // Launches 1 rocket
426
+ // or
427
+ fw.burst(5); // Launches 5 rockets at once
379
428
  ```
380
429
 
381
430
  ---
@@ -387,3 +436,5 @@ MIT © [phongdh](https://github.com/phongdh)
387
436
  ## 🙏 Contributing
388
437
 
389
438
  Contributions are welcome! Please feel free to submit a Pull Request.
439
+
440
+ > I'm currently figuring out how to implement the **Waterfall** or **Weeping Willow** effect properly. If you have experience with these physics/visuals, I'd love your help! Please feel free to open a Pull Request.
@@ -388,8 +388,20 @@ class CanvasRenderer {
388
388
  /**
389
389
  * Clear the entire canvas
390
390
  */
391
- clear() {
392
- this.ctx.clearRect(0, 0, this.width, this.height);
391
+ /**
392
+ * Clear the entire canvas
393
+ * @param trail If true, fades out existing content instead of hard clear
394
+ */
395
+ clear(trail = false) {
396
+ if (trail) {
397
+ // "Trail trick": fill with semi-transparent black to create trails
398
+ // Adjust opacity for longer/shorter trails (current: very long trails)
399
+ this.ctx.fillStyle = 'rgba(0, 0, 0, 0.2)';
400
+ this.ctx.fillRect(0, 0, this.width, this.height);
401
+ }
402
+ else {
403
+ this.ctx.clearRect(0, 0, this.width, this.height);
404
+ }
393
405
  }
394
406
  /**
395
407
  * Pre-cache an emoji as a sprite for fast drawing (HiDPI aware)
@@ -836,10 +848,10 @@ const DEFAULT_COLORS = [
836
848
  ];
837
849
  const DEFAULTS = {
838
850
  colors: DEFAULT_COLORS,
839
- launchRate: 1,
851
+ launchRate: 0.5,
840
852
  particlesPerExplosion: 50,
841
- rocketSpeed: { min: 8, max: 15 },
842
- explosionSpeed: { min: 2, max: 8 },
853
+ rocketSpeed: { min: 7, max: 12 },
854
+ explosionSpeed: { min: 1, max: 6 },
843
855
  particleSize: { min: 2, max: 6 },
844
856
  particleLifetime: { min: 1000, max: 2000 },
845
857
  gravity: 0.1,
@@ -898,8 +910,8 @@ class Fireworks {
898
910
  const drag = particle.size < 2 ? 0.96 : 0.99;
899
911
  particle.vx *= drag;
900
912
  particle.vy *= drag;
901
- // Double pattern secondary explosion: trigger at 50% lifetime for stage 1 particles
902
- if (particle.stage === 1 && lifeProgress >= 0.5 && particle.targetY !== -1) {
913
+ // Double pattern secondary explosion: trigger at 50% lifetime if flag is set
914
+ if (particle.hasSecondaryExplosion && lifeProgress >= 0.5 && particle.targetY !== -1) {
903
915
  // Mark as already triggered secondary explosion
904
916
  particle.targetY = -1;
905
917
  particlesToExplode.push(particle);
@@ -1029,7 +1041,7 @@ class Fireworks {
1029
1041
  pattern = patternOption[Math.floor(Math.random() * patternOption.length)];
1030
1042
  }
1031
1043
  else if (patternOption === 'random') {
1032
- const patterns = ['circular', 'ring', 'heart', 'star', 'willow', 'palm', 'chrysanthemum', 'embers', 'double'];
1044
+ const patterns = ['circular', 'ring', 'heart', 'star', 'willow', 'palm', 'chrysanthemum', 'embers', 'double', 'waterfall'];
1033
1045
  pattern = patterns[Math.floor(Math.random() * patterns.length)];
1034
1046
  }
1035
1047
  else {
@@ -1059,6 +1071,8 @@ class Fireworks {
1059
1071
  return this.createEmbersExplosion(rocket, count, color);
1060
1072
  case 'double':
1061
1073
  return this.createDoubleExplosion(rocket, count, color);
1074
+ case 'waterfall':
1075
+ return this.createWaterfallExplosion(rocket, count, color);
1062
1076
  case 'circular':
1063
1077
  default:
1064
1078
  return this.createCircularExplosion(rocket, count, color);
@@ -1210,7 +1224,40 @@ class Fireworks {
1210
1224
  maxAge: randomFromRange(this.options.particleLifetime),
1211
1225
  phase: 'explosion',
1212
1226
  gravity: this.options.gravity,
1213
- stage: 1 // Mark as stage 1 - will trigger secondary explosion
1227
+ stage: 1,
1228
+ hasSecondaryExplosion: true, // Explicitly enable secondary explosion
1229
+ };
1230
+ particles.push(particle);
1231
+ }
1232
+ return particles;
1233
+ }
1234
+ /** Waterfall - Gentle explosion up, then heavy fall like water */
1235
+ createWaterfallExplosion(rocket, count, color) {
1236
+ const particles = [];
1237
+ // Waterfall needs VERY high density to look like streams
1238
+ const waterfallCount = Math.floor(count * 2.5);
1239
+ for (let i = 0; i < waterfallCount; i++) {
1240
+ // Semicircle upwards (0 to PI, negative for canvas Y)
1241
+ // But we want a slight spread, mostly up
1242
+ const angle = Math.PI + Math.random() * Math.PI; // Full arc upwards (-PI to 0 effectively)
1243
+ // Low initial speed - "nổ nhẹ nhàng"
1244
+ const speed = randomFromRange(this.options.explosionSpeed) * 0.4;
1245
+ const particle = {
1246
+ id: generateId(),
1247
+ x: rocket.x,
1248
+ y: rocket.y,
1249
+ vx: Math.cos(angle) * speed,
1250
+ vy: Math.sin(angle) * speed * 0.5, // Flatten the vertical explosion a bit
1251
+ size: randomFromRange(this.options.particleSize),
1252
+ opacity: 1,
1253
+ color: this.getVariantColor(color),
1254
+ age: 0,
1255
+ // Long lifetime to fall down screen
1256
+ maxAge: randomFromRange(this.options.particleLifetime) * 2.5,
1257
+ phase: 'explosion',
1258
+ // High gravity for waterfall effect
1259
+ gravity: 0.25,
1260
+ stage: 0
1214
1261
  };
1215
1262
  particles.push(particle);
1216
1263
  }
@@ -1378,4 +1425,3 @@ exports.animations = animations;
1378
1425
  exports.default = FallingAnimation;
1379
1426
  exports.getAnimation = getAnimation;
1380
1427
  exports.isBrowser = isBrowser;
1381
- //# sourceMappingURL=falling-animation.cjs.map
@@ -384,8 +384,20 @@ class CanvasRenderer {
384
384
  /**
385
385
  * Clear the entire canvas
386
386
  */
387
- clear() {
388
- this.ctx.clearRect(0, 0, this.width, this.height);
387
+ /**
388
+ * Clear the entire canvas
389
+ * @param trail If true, fades out existing content instead of hard clear
390
+ */
391
+ clear(trail = false) {
392
+ if (trail) {
393
+ // "Trail trick": fill with semi-transparent black to create trails
394
+ // Adjust opacity for longer/shorter trails (current: very long trails)
395
+ this.ctx.fillStyle = 'rgba(0, 0, 0, 0.2)';
396
+ this.ctx.fillRect(0, 0, this.width, this.height);
397
+ }
398
+ else {
399
+ this.ctx.clearRect(0, 0, this.width, this.height);
400
+ }
389
401
  }
390
402
  /**
391
403
  * Pre-cache an emoji as a sprite for fast drawing (HiDPI aware)
@@ -832,10 +844,10 @@ const DEFAULT_COLORS = [
832
844
  ];
833
845
  const DEFAULTS = {
834
846
  colors: DEFAULT_COLORS,
835
- launchRate: 1,
847
+ launchRate: 0.5,
836
848
  particlesPerExplosion: 50,
837
- rocketSpeed: { min: 8, max: 15 },
838
- explosionSpeed: { min: 2, max: 8 },
849
+ rocketSpeed: { min: 7, max: 12 },
850
+ explosionSpeed: { min: 1, max: 6 },
839
851
  particleSize: { min: 2, max: 6 },
840
852
  particleLifetime: { min: 1000, max: 2000 },
841
853
  gravity: 0.1,
@@ -894,8 +906,8 @@ class Fireworks {
894
906
  const drag = particle.size < 2 ? 0.96 : 0.99;
895
907
  particle.vx *= drag;
896
908
  particle.vy *= drag;
897
- // Double pattern secondary explosion: trigger at 50% lifetime for stage 1 particles
898
- if (particle.stage === 1 && lifeProgress >= 0.5 && particle.targetY !== -1) {
909
+ // Double pattern secondary explosion: trigger at 50% lifetime if flag is set
910
+ if (particle.hasSecondaryExplosion && lifeProgress >= 0.5 && particle.targetY !== -1) {
899
911
  // Mark as already triggered secondary explosion
900
912
  particle.targetY = -1;
901
913
  particlesToExplode.push(particle);
@@ -1025,7 +1037,7 @@ class Fireworks {
1025
1037
  pattern = patternOption[Math.floor(Math.random() * patternOption.length)];
1026
1038
  }
1027
1039
  else if (patternOption === 'random') {
1028
- const patterns = ['circular', 'ring', 'heart', 'star', 'willow', 'palm', 'chrysanthemum', 'embers', 'double'];
1040
+ const patterns = ['circular', 'ring', 'heart', 'star', 'willow', 'palm', 'chrysanthemum', 'embers', 'double', 'waterfall'];
1029
1041
  pattern = patterns[Math.floor(Math.random() * patterns.length)];
1030
1042
  }
1031
1043
  else {
@@ -1055,6 +1067,8 @@ class Fireworks {
1055
1067
  return this.createEmbersExplosion(rocket, count, color);
1056
1068
  case 'double':
1057
1069
  return this.createDoubleExplosion(rocket, count, color);
1070
+ case 'waterfall':
1071
+ return this.createWaterfallExplosion(rocket, count, color);
1058
1072
  case 'circular':
1059
1073
  default:
1060
1074
  return this.createCircularExplosion(rocket, count, color);
@@ -1206,7 +1220,40 @@ class Fireworks {
1206
1220
  maxAge: randomFromRange(this.options.particleLifetime),
1207
1221
  phase: 'explosion',
1208
1222
  gravity: this.options.gravity,
1209
- stage: 1 // Mark as stage 1 - will trigger secondary explosion
1223
+ stage: 1,
1224
+ hasSecondaryExplosion: true, // Explicitly enable secondary explosion
1225
+ };
1226
+ particles.push(particle);
1227
+ }
1228
+ return particles;
1229
+ }
1230
+ /** Waterfall - Gentle explosion up, then heavy fall like water */
1231
+ createWaterfallExplosion(rocket, count, color) {
1232
+ const particles = [];
1233
+ // Waterfall needs VERY high density to look like streams
1234
+ const waterfallCount = Math.floor(count * 2.5);
1235
+ for (let i = 0; i < waterfallCount; i++) {
1236
+ // Semicircle upwards (0 to PI, negative for canvas Y)
1237
+ // But we want a slight spread, mostly up
1238
+ const angle = Math.PI + Math.random() * Math.PI; // Full arc upwards (-PI to 0 effectively)
1239
+ // Low initial speed - "nổ nhẹ nhàng"
1240
+ const speed = randomFromRange(this.options.explosionSpeed) * 0.4;
1241
+ const particle = {
1242
+ id: generateId(),
1243
+ x: rocket.x,
1244
+ y: rocket.y,
1245
+ vx: Math.cos(angle) * speed,
1246
+ vy: Math.sin(angle) * speed * 0.5, // Flatten the vertical explosion a bit
1247
+ size: randomFromRange(this.options.particleSize),
1248
+ opacity: 1,
1249
+ color: this.getVariantColor(color),
1250
+ age: 0,
1251
+ // Long lifetime to fall down screen
1252
+ maxAge: randomFromRange(this.options.particleLifetime) * 2.5,
1253
+ phase: 'explosion',
1254
+ // High gravity for waterfall effect
1255
+ gravity: 0.25,
1256
+ stage: 0
1210
1257
  };
1211
1258
  particles.push(particle);
1212
1259
  }
@@ -1369,4 +1416,3 @@ class Fireworks {
1369
1416
  // Main classes
1370
1417
 
1371
1418
  export { FallingAnimation, Fireworks, animations, FallingAnimation as default, getAnimation, isBrowser };
1372
- //# sourceMappingURL=falling-animation.esm.js.map
@@ -390,8 +390,20 @@
390
390
  /**
391
391
  * Clear the entire canvas
392
392
  */
393
- clear() {
394
- this.ctx.clearRect(0, 0, this.width, this.height);
393
+ /**
394
+ * Clear the entire canvas
395
+ * @param trail If true, fades out existing content instead of hard clear
396
+ */
397
+ clear(trail = false) {
398
+ if (trail) {
399
+ // "Trail trick": fill with semi-transparent black to create trails
400
+ // Adjust opacity for longer/shorter trails (current: very long trails)
401
+ this.ctx.fillStyle = 'rgba(0, 0, 0, 0.2)';
402
+ this.ctx.fillRect(0, 0, this.width, this.height);
403
+ }
404
+ else {
405
+ this.ctx.clearRect(0, 0, this.width, this.height);
406
+ }
395
407
  }
396
408
  /**
397
409
  * Pre-cache an emoji as a sprite for fast drawing (HiDPI aware)
@@ -838,10 +850,10 @@
838
850
  ];
839
851
  const DEFAULTS = {
840
852
  colors: DEFAULT_COLORS,
841
- launchRate: 1,
853
+ launchRate: 0.5,
842
854
  particlesPerExplosion: 50,
843
- rocketSpeed: { min: 8, max: 15 },
844
- explosionSpeed: { min: 2, max: 8 },
855
+ rocketSpeed: { min: 7, max: 12 },
856
+ explosionSpeed: { min: 1, max: 6 },
845
857
  particleSize: { min: 2, max: 6 },
846
858
  particleLifetime: { min: 1000, max: 2000 },
847
859
  gravity: 0.1,
@@ -900,8 +912,8 @@
900
912
  const drag = particle.size < 2 ? 0.96 : 0.99;
901
913
  particle.vx *= drag;
902
914
  particle.vy *= drag;
903
- // Double pattern secondary explosion: trigger at 50% lifetime for stage 1 particles
904
- if (particle.stage === 1 && lifeProgress >= 0.5 && particle.targetY !== -1) {
915
+ // Double pattern secondary explosion: trigger at 50% lifetime if flag is set
916
+ if (particle.hasSecondaryExplosion && lifeProgress >= 0.5 && particle.targetY !== -1) {
905
917
  // Mark as already triggered secondary explosion
906
918
  particle.targetY = -1;
907
919
  particlesToExplode.push(particle);
@@ -1031,7 +1043,7 @@
1031
1043
  pattern = patternOption[Math.floor(Math.random() * patternOption.length)];
1032
1044
  }
1033
1045
  else if (patternOption === 'random') {
1034
- const patterns = ['circular', 'ring', 'heart', 'star', 'willow', 'palm', 'chrysanthemum', 'embers', 'double'];
1046
+ const patterns = ['circular', 'ring', 'heart', 'star', 'willow', 'palm', 'chrysanthemum', 'embers', 'double', 'waterfall'];
1035
1047
  pattern = patterns[Math.floor(Math.random() * patterns.length)];
1036
1048
  }
1037
1049
  else {
@@ -1061,6 +1073,8 @@
1061
1073
  return this.createEmbersExplosion(rocket, count, color);
1062
1074
  case 'double':
1063
1075
  return this.createDoubleExplosion(rocket, count, color);
1076
+ case 'waterfall':
1077
+ return this.createWaterfallExplosion(rocket, count, color);
1064
1078
  case 'circular':
1065
1079
  default:
1066
1080
  return this.createCircularExplosion(rocket, count, color);
@@ -1212,7 +1226,40 @@
1212
1226
  maxAge: randomFromRange(this.options.particleLifetime),
1213
1227
  phase: 'explosion',
1214
1228
  gravity: this.options.gravity,
1215
- stage: 1 // Mark as stage 1 - will trigger secondary explosion
1229
+ stage: 1,
1230
+ hasSecondaryExplosion: true, // Explicitly enable secondary explosion
1231
+ };
1232
+ particles.push(particle);
1233
+ }
1234
+ return particles;
1235
+ }
1236
+ /** Waterfall - Gentle explosion up, then heavy fall like water */
1237
+ createWaterfallExplosion(rocket, count, color) {
1238
+ const particles = [];
1239
+ // Waterfall needs VERY high density to look like streams
1240
+ const waterfallCount = Math.floor(count * 2.5);
1241
+ for (let i = 0; i < waterfallCount; i++) {
1242
+ // Semicircle upwards (0 to PI, negative for canvas Y)
1243
+ // But we want a slight spread, mostly up
1244
+ const angle = Math.PI + Math.random() * Math.PI; // Full arc upwards (-PI to 0 effectively)
1245
+ // Low initial speed - "nổ nhẹ nhàng"
1246
+ const speed = randomFromRange(this.options.explosionSpeed) * 0.4;
1247
+ const particle = {
1248
+ id: generateId(),
1249
+ x: rocket.x,
1250
+ y: rocket.y,
1251
+ vx: Math.cos(angle) * speed,
1252
+ vy: Math.sin(angle) * speed * 0.5, // Flatten the vertical explosion a bit
1253
+ size: randomFromRange(this.options.particleSize),
1254
+ opacity: 1,
1255
+ color: this.getVariantColor(color),
1256
+ age: 0,
1257
+ // Long lifetime to fall down screen
1258
+ maxAge: randomFromRange(this.options.particleLifetime) * 2.5,
1259
+ phase: 'explosion',
1260
+ // High gravity for waterfall effect
1261
+ gravity: 0.25,
1262
+ stage: 0
1216
1263
  };
1217
1264
  particles.push(particle);
1218
1265
  }
@@ -1384,4 +1431,3 @@
1384
1431
  Object.defineProperty(exports, '__esModule', { value: true });
1385
1432
 
1386
1433
  }));
1387
- //# sourceMappingURL=falling-animation.umd.js.map
@@ -1,4 +1,4 @@
1
- !function(t,i){"object"==typeof exports&&"undefined"!=typeof module?i(exports):"function"==typeof define&&define.amd?define(["exports"],i):i((t="undefined"!=typeof globalThis?globalThis:t||self).FallingAnimation={})}(this,function(t){"use strict";function i(t,i){return Math.random()*(i-t)+t}function e(t){return i(t.min,t.max)}let s=0;function n(){return++s}function o(){return"undefined"!=typeof window&&"undefined"!=typeof document}function a(t,i){let e=!1;return function(...s){e||(t.apply(this,s),e=!0,setTimeout(()=>e=!1,i))}}const r=(t,i)=>{0===t.vy&&0===t.vx||(t.y+=t.vy*i,t.x+=t.vx*i)},h={fall:r,swing:(t,i,e)=>{if(t.rotation=20*Math.sin(.002*(e+t.phase)),0===t.vy&&0===t.vx)return;t.y+=t.vy*i;const s=30*Math.sin(.002*(e+t.phase));t.x+=t.vx*i+.05*s},rotate:(t,i)=>{t.rotation+=t.rotationSpeed*i,0===t.vy&&0===t.vx||(t.y+=t.vy*i,t.x+=t.vx*i)},flutter:(t,i,e)=>{const s=.005;if(t.rotation=30*Math.sin((e+t.phase)*s),0===t.vy&&0===t.vx)return;const n=40*Math.sin((e+t.phase)*s);t.x+=t.vx*i+.03*n;const o=.3*Math.sin((e+t.phase)*s*2);t.y+=(t.vy+o)*i},spiral:(t,i,e)=>{const s=.003*(e+t.phase);if(t.rotation=s*(180/Math.PI),0===t.vy&&0===t.vx)return;t.y+=t.vy*i,t.x+=t.vx*i+25*Math.cos(s)*.02},tumble:(t,i,e)=>{var s,n,o;const a=null!==(s=t.data.tumbleSpeed)&&void 0!==s?s:5;if(t.rotation+=a*i,0===t.vy&&0===t.vx)return;const r=null!==(n=t.data.wobbleX)&&void 0!==n?n:.02,h=null!==(o=t.data.wobbleY)&&void 0!==o?o:.01;t.y+=t.vy*i,t.x+=t.vx*i+20*Math.sin((e+t.phase)*r)*.02,t.y+=.5*Math.sin((e+1.5*t.phase)*h)},zigzag:(t,i,e)=>{const s=(e+t.phase)%500/500,n=2*Math.abs(2*s-1)-1;if(t.rotation=25*n,0===t.vy&&0===t.vx)return;t.y+=t.vy*i,t.x+=t.vx*i+50*n*.02},float:(t,i,e)=>{const s=.001;if(t.rotation=10*Math.sin((e+t.phase)*s),0===t.vy&&0===t.vx)return;const n=5*Math.sin((e+t.phase)*s*2);t.y+=(.5*t.vy+.01*n)*i;const o=20*Math.sin((e+t.phase)*s);t.x+=t.vx*i+.01*o}};function l(t){var i;return null!==(i=h[t])&&void 0!==i?i:r}class c{constructor(t,s,o){this.id=n(),this.age=0,this.phase=Math.random()*Math.PI*2,this.data={},this.containerWidth=s,this.containerHeight=o;const a=function(t){var i;const e=t.reduce((t,i)=>{var e;return t+(null!==(e=i.weight)&&void 0!==e?e:1)},0);let s=Math.random()*e;for(const e of t)if(s-=null!==(i=e.weight)&&void 0!==i?i:1,s<=0)return e;return t[t.length-1]}(t.objects);var r;this.content=a.src||a.content||"❄️",this.objectType=a.type,this.x=i(-50,this.containerWidth+50),this.y=-50,this.vy=.04*e(t.speed),this.vx=.04*t.wind*i(.5,1.5),this.size=e(t.size),this.opacity=e(t.opacity),this.rotation=i(0,360),this.rotationSpeed=i(-3,3),this.animation=(r=t.animation)[Math.floor(Math.random()*r.length)],this.initAnimationData()}initAnimationData(){"tumble"===this.animation&&(this.data.tumbleSpeed=i(3,8),this.data.wobbleX=i(.01,.03),this.data.wobbleY=i(.005,.015))}update(t,i){this.age+=t;l(this.animation)(this,t,i)}isOutOfBounds(){return this.y>this.containerHeight+100||this.x<-100||this.x>this.containerWidth+100}updateContainerSize(t,i){this.containerWidth=t,this.containerHeight=i}}class p{constructor(t,i=9999){if(this.spriteCache=new Map,this.imageCache=new Map,this.width=0,this.height=0,!o())throw new Error("[falling-animation] CanvasRenderer requires a browser environment.");this.canvas=document.createElement("canvas"),this.canvas.className="falling-animation-canvas";const e=this.canvas.getContext("2d");if(!e)throw new Error("[falling-animation] Could not get 2D context from canvas.");this.ctx=e;const s=t===document.body;if(Object.assign(this.canvas.style,{position:s?"fixed":"absolute",top:"0",left:"0",width:"100%",height:"100%",pointerEvents:"none",zIndex:String(i)}),!s){"static"===getComputedStyle(t).position&&(t.style.position="relative")}t.appendChild(this.canvas),this.resize()}resize(){var t;const i=null===(t=this.canvas.parentElement)||void 0===t?void 0:t.getBoundingClientRect();if(i){const t=window.devicePixelRatio||1;this.width=i.width,this.height=i.height,this.canvas.width=this.width*t,this.canvas.height=this.height*t,this.ctx.scale(t,t)}}getSize(){return{width:this.width,height:this.height}}clear(){this.ctx.clearRect(0,0,this.width,this.height)}cacheEmoji(t,i){const e=window.devicePixelRatio||1,s=`emoji:${t}:${i}:${e}`;if(this.spriteCache.has(s))return this.spriteCache.get(s);const n=document.createElement("canvas"),o=i+2*(.3*i),a=o*e;n.width=a,n.height=a;const r=n.getContext("2d");r.scale(e,e),r.imageSmoothingEnabled=!0,r.imageSmoothingQuality="high";const h=1*i;r.font=`${h}px "Segoe UI Emoji", "Apple Color Emoji", "Noto Color Emoji", "Android Emoji", "EmojiOne", sans-serif`,r.textAlign="center",r.textBaseline="middle",r.fillText(t,o/2,o/2);const l={canvas:n,width:o,height:o};return this.spriteCache.set(s,l),l}preloadImage(t){return this.imageCache.has(t)?Promise.resolve(this.imageCache.get(t)):new Promise((i,e)=>{const s=new Image;s.crossOrigin="anonymous",s.onload=()=>{this.imageCache.set(t,s),i(s)},s.onerror=e,s.src=t})}drawParticle(t){const{x:i,y:e,rotation:s,size:n,opacity:o,content:a,type:r}=t;if(this.ctx.save(),this.ctx.globalAlpha=o,this.ctx.translate(i,e),this.ctx.rotate(s*Math.PI/180),"emoji"===r){const t=this.cacheEmoji(a,Math.round(n));this.ctx.drawImage(t.canvas,-t.width/2,-t.height/2,t.width,t.height)}else if("image"===r){const t=this.imageCache.get(a);t&&this.ctx.drawImage(t,-n/2,-n/2,n,n)}this.ctx.restore()}drawParticles(t){for(const i of t)this.drawParticle(i)}drawFireworkParticle(t){const{x:i,y:e,size:s,opacity:n,color:o}=t;this.ctx.save(),this.ctx.globalAlpha=n,this.ctx.shadowColor=o,this.ctx.shadowBlur=2*s,this.ctx.fillStyle=o,this.ctx.beginPath(),this.ctx.arc(i,e,s,0,2*Math.PI),this.ctx.fill(),this.ctx.restore()}drawFireworkParticles(t){for(const i of t)this.drawFireworkParticle(i)}destroy(){this.canvas.remove(),this.spriteCache.clear(),this.imageCache.clear()}}const d={speed:{min:2,max:5},spawnRate:3,maxParticles:50,animation:["fall"],size:{min:20,max:40},opacity:{min:.6,max:1},wind:0,autoStart:!0,zIndex:9999,responsive:!0};class u{constructor(t){if(this.particles=[],this.renderer=null,this.isRunning=!1,this.isPaused=!1,this.animationId=null,this.lastSpawnTime=0,this.startTime=0,this.lastFrameTime=0,this.resizeHandler=null,this.animate=t=>{if(!this.isRunning||!this.renderer)return;0===this.lastFrameTime&&(this.lastFrameTime=t,this.startTime=t);const i=Math.min(t-this.lastFrameTime,50),e=t-this.startTime;this.lastFrameTime=t;const s=1e3/this.options.spawnRate;t-this.lastSpawnTime>=s&&(this.spawnParticle(),this.lastSpawnTime=t);const n=[];for(const t of this.particles)t.update(i,e),t.isOutOfBounds()&&n.push(t);n.forEach(t=>this.removeParticle(t)),this.renderer.clear();const o=this.particles.map(t=>({x:t.x,y:t.y,rotation:t.rotation,size:t.size,opacity:t.opacity,content:t.content,type:"image"===t.objectType?"image":"emoji"}));this.renderer.drawParticles(o),this.animationId=requestAnimationFrame(this.animate)},!t.objects||0===t.objects.length)throw new Error('[falling-animation] "objects" option is required and must not be empty');this.options=this.resolveOptions(t),this.renderer=new p(this.options.container,this.options.zIndex),this.preloadImages(),this.options.responsive&&this.setupResizeHandler(),this.options.autoStart&&this.start()}async preloadImages(){if(this.renderer)for(const t of this.options.objects)if("image"===t.type){const i=t.src||t.content;if(i)try{await this.renderer.preloadImage(i)}catch(t){console.warn(`[falling-animation] Failed to preload image: ${i}`)}}}resolveOptions(t){var i,e,s,n,a,r,h,l,c;const p=function(t){if(!o())throw new Error("[falling-animation] This library requires a browser environment. If using SSR (Next.js, Nuxt, etc.), make sure to only initialize on the client side.");if(!t)return document.body;if("string"==typeof t){return document.querySelector(t)||(console.warn(`[falling-animation] Container "${t}" not found, using document.body`),document.body)}return t}(t.container);let u;return u=t.animation?"string"==typeof t.animation?[t.animation]:t.animation:d.animation,{container:p,objects:t.objects,speed:null!==(i=t.speed)&&void 0!==i?i:d.speed,spawnRate:null!==(e=t.spawnRate)&&void 0!==e?e:d.spawnRate,maxParticles:null!==(s=t.maxParticles)&&void 0!==s?s:d.maxParticles,animation:u,size:null!==(n=t.size)&&void 0!==n?n:d.size,opacity:null!==(a=t.opacity)&&void 0!==a?a:d.opacity,wind:null!==(r=t.wind)&&void 0!==r?r:d.wind,autoStart:null!==(h=t.autoStart)&&void 0!==h?h:d.autoStart,zIndex:null!==(l=t.zIndex)&&void 0!==l?l:d.zIndex,responsive:null!==(c=t.responsive)&&void 0!==c?c:d.responsive}}setupResizeHandler(){this.resizeHandler=a(()=>{if(this.renderer){this.renderer.resize();const{width:t,height:i}=this.renderer.getSize();this.particles.forEach(e=>{e.updateContainerSize(t,i)})}},200),window.addEventListener("resize",this.resizeHandler)}spawnParticle(){if(this.particles.length>=this.options.maxParticles)return;if(!this.renderer)return;const{width:t,height:i}=this.renderer.getSize(),e=new c(this.options,t,i);this.particles.push(e)}removeParticle(t){const i=this.particles.indexOf(t);i>-1&&this.particles.splice(i,1)}start(){this.isRunning||(this.isRunning=!0,this.isPaused=!1,this.lastFrameTime=0,this.lastSpawnTime=0,this.animationId=requestAnimationFrame(this.animate))}stop(){this.isRunning=!1,this.isPaused=!1,null!==this.animationId&&(cancelAnimationFrame(this.animationId),this.animationId=null),this.particles=[],this.renderer&&this.renderer.clear()}pause(){this.isRunning&&!this.isPaused&&(this.isPaused=!0,this.isRunning=!1,null!==this.animationId&&(cancelAnimationFrame(this.animationId),this.animationId=null))}resume(){!this.isRunning&&this.isPaused&&(this.isRunning=!0,this.isPaused=!1,this.lastFrameTime=0,this.animationId=requestAnimationFrame(this.animate))}setOptions(t){t.speed&&(this.options.speed=t.speed),void 0!==t.spawnRate&&(this.options.spawnRate=t.spawnRate),void 0!==t.maxParticles&&(this.options.maxParticles=t.maxParticles),t.animation&&(this.options.animation="string"==typeof t.animation?[t.animation]:t.animation),t.size&&(this.options.size=t.size),t.opacity&&(this.options.opacity=t.opacity),void 0!==t.wind&&(this.options.wind=t.wind),t.objects&&(this.options.objects=t.objects,this.preloadImages())}getParticleCount(){return this.particles.length}getIsRunning(){return this.isRunning}getIsPaused(){return this.isPaused}destroy(){this.stop(),this.renderer&&(this.renderer.destroy(),this.renderer=null),this.resizeHandler&&(window.removeEventListener("resize",this.resizeHandler),this.resizeHandler=null)}}const m={colors:["#ff0000","#ff6b00","#ffff00","#00ff00","#00ffff","#0066ff","#ff00ff","#ffffff","#ff69b4","#ffd700"],launchRate:1,particlesPerExplosion:50,rocketSpeed:{min:8,max:15},explosionSpeed:{min:2,max:8},particleSize:{min:2,max:6},particleLifetime:{min:1e3,max:2e3},gravity:.1,trail:!0,explosionPattern:"circular",autoStart:!0,zIndex:9999};
1
+ !function(t,i){"object"==typeof exports&&"undefined"!=typeof module?i(exports):"function"==typeof define&&define.amd?define(["exports"],i):i((t="undefined"!=typeof globalThis?globalThis:t||self).FallingAnimation={})}(this,function(t){"use strict";function i(t,i){return Math.random()*(i-t)+t}function e(t){return i(t.min,t.max)}let s=0;function n(){return++s}function o(){return"undefined"!=typeof window&&"undefined"!=typeof document}function a(t,i){let e=!1;return function(...s){e||(t.apply(this,s),e=!0,setTimeout(()=>e=!1,i))}}const r=(t,i)=>{0===t.vy&&0===t.vx||(t.y+=t.vy*i,t.x+=t.vx*i)},h={fall:r,swing:(t,i,e)=>{if(t.rotation=20*Math.sin(.002*(e+t.phase)),0===t.vy&&0===t.vx)return;t.y+=t.vy*i;const s=30*Math.sin(.002*(e+t.phase));t.x+=t.vx*i+.05*s},rotate:(t,i)=>{t.rotation+=t.rotationSpeed*i,0===t.vy&&0===t.vx||(t.y+=t.vy*i,t.x+=t.vx*i)},flutter:(t,i,e)=>{const s=.005;if(t.rotation=30*Math.sin((e+t.phase)*s),0===t.vy&&0===t.vx)return;const n=40*Math.sin((e+t.phase)*s);t.x+=t.vx*i+.03*n;const o=.3*Math.sin((e+t.phase)*s*2);t.y+=(t.vy+o)*i},spiral:(t,i,e)=>{const s=.003*(e+t.phase);if(t.rotation=s*(180/Math.PI),0===t.vy&&0===t.vx)return;t.y+=t.vy*i,t.x+=t.vx*i+25*Math.cos(s)*.02},tumble:(t,i,e)=>{var s,n,o;const a=null!==(s=t.data.tumbleSpeed)&&void 0!==s?s:5;if(t.rotation+=a*i,0===t.vy&&0===t.vx)return;const r=null!==(n=t.data.wobbleX)&&void 0!==n?n:.02,h=null!==(o=t.data.wobbleY)&&void 0!==o?o:.01;t.y+=t.vy*i,t.x+=t.vx*i+20*Math.sin((e+t.phase)*r)*.02,t.y+=.5*Math.sin((e+1.5*t.phase)*h)},zigzag:(t,i,e)=>{const s=(e+t.phase)%500/500,n=2*Math.abs(2*s-1)-1;if(t.rotation=25*n,0===t.vy&&0===t.vx)return;t.y+=t.vy*i,t.x+=t.vx*i+50*n*.02},float:(t,i,e)=>{const s=.001;if(t.rotation=10*Math.sin((e+t.phase)*s),0===t.vy&&0===t.vx)return;const n=5*Math.sin((e+t.phase)*s*2);t.y+=(.5*t.vy+.01*n)*i;const o=20*Math.sin((e+t.phase)*s);t.x+=t.vx*i+.01*o}};function l(t){var i;return null!==(i=h[t])&&void 0!==i?i:r}class c{constructor(t,s,o){this.id=n(),this.age=0,this.phase=Math.random()*Math.PI*2,this.data={},this.containerWidth=s,this.containerHeight=o;const a=function(t){var i;const e=t.reduce((t,i)=>{var e;return t+(null!==(e=i.weight)&&void 0!==e?e:1)},0);let s=Math.random()*e;for(const e of t)if(s-=null!==(i=e.weight)&&void 0!==i?i:1,s<=0)return e;return t[t.length-1]}(t.objects);var r;this.content=a.src||a.content||"❄️",this.objectType=a.type,this.x=i(-50,this.containerWidth+50),this.y=-50,this.vy=.04*e(t.speed),this.vx=.04*t.wind*i(.5,1.5),this.size=e(t.size),this.opacity=e(t.opacity),this.rotation=i(0,360),this.rotationSpeed=i(-3,3),this.animation=(r=t.animation)[Math.floor(Math.random()*r.length)],this.initAnimationData()}initAnimationData(){"tumble"===this.animation&&(this.data.tumbleSpeed=i(3,8),this.data.wobbleX=i(.01,.03),this.data.wobbleY=i(.005,.015))}update(t,i){this.age+=t;l(this.animation)(this,t,i)}isOutOfBounds(){return this.y>this.containerHeight+100||this.x<-100||this.x>this.containerWidth+100}updateContainerSize(t,i){this.containerWidth=t,this.containerHeight=i}}class p{constructor(t,i=9999){if(this.spriteCache=new Map,this.imageCache=new Map,this.width=0,this.height=0,!o())throw new Error("[falling-animation] CanvasRenderer requires a browser environment.");this.canvas=document.createElement("canvas"),this.canvas.className="falling-animation-canvas";const e=this.canvas.getContext("2d");if(!e)throw new Error("[falling-animation] Could not get 2D context from canvas.");this.ctx=e;const s=t===document.body;if(Object.assign(this.canvas.style,{position:s?"fixed":"absolute",top:"0",left:"0",width:"100%",height:"100%",pointerEvents:"none",zIndex:String(i)}),!s){"static"===getComputedStyle(t).position&&(t.style.position="relative")}t.appendChild(this.canvas),this.resize()}resize(){var t;const i=null===(t=this.canvas.parentElement)||void 0===t?void 0:t.getBoundingClientRect();if(i){const t=window.devicePixelRatio||1;this.width=i.width,this.height=i.height,this.canvas.width=this.width*t,this.canvas.height=this.height*t,this.ctx.scale(t,t)}}getSize(){return{width:this.width,height:this.height}}clear(t=!1){t?(this.ctx.fillStyle="rgba(0, 0, 0, 0.2)",this.ctx.fillRect(0,0,this.width,this.height)):this.ctx.clearRect(0,0,this.width,this.height)}cacheEmoji(t,i){const e=window.devicePixelRatio||1,s=`emoji:${t}:${i}:${e}`;if(this.spriteCache.has(s))return this.spriteCache.get(s);const n=document.createElement("canvas"),o=i+2*(.3*i),a=o*e;n.width=a,n.height=a;const r=n.getContext("2d");r.scale(e,e),r.imageSmoothingEnabled=!0,r.imageSmoothingQuality="high";const h=1*i;r.font=`${h}px "Segoe UI Emoji", "Apple Color Emoji", "Noto Color Emoji", "Android Emoji", "EmojiOne", sans-serif`,r.textAlign="center",r.textBaseline="middle",r.fillText(t,o/2,o/2);const l={canvas:n,width:o,height:o};return this.spriteCache.set(s,l),l}preloadImage(t){return this.imageCache.has(t)?Promise.resolve(this.imageCache.get(t)):new Promise((i,e)=>{const s=new Image;s.crossOrigin="anonymous",s.onload=()=>{this.imageCache.set(t,s),i(s)},s.onerror=e,s.src=t})}drawParticle(t){const{x:i,y:e,rotation:s,size:n,opacity:o,content:a,type:r}=t;if(this.ctx.save(),this.ctx.globalAlpha=o,this.ctx.translate(i,e),this.ctx.rotate(s*Math.PI/180),"emoji"===r){const t=this.cacheEmoji(a,Math.round(n));this.ctx.drawImage(t.canvas,-t.width/2,-t.height/2,t.width,t.height)}else if("image"===r){const t=this.imageCache.get(a);t&&this.ctx.drawImage(t,-n/2,-n/2,n,n)}this.ctx.restore()}drawParticles(t){for(const i of t)this.drawParticle(i)}drawFireworkParticle(t){const{x:i,y:e,size:s,opacity:n,color:o}=t;this.ctx.save(),this.ctx.globalAlpha=n,this.ctx.shadowColor=o,this.ctx.shadowBlur=2*s,this.ctx.fillStyle=o,this.ctx.beginPath(),this.ctx.arc(i,e,s,0,2*Math.PI),this.ctx.fill(),this.ctx.restore()}drawFireworkParticles(t){for(const i of t)this.drawFireworkParticle(i)}destroy(){this.canvas.remove(),this.spriteCache.clear(),this.imageCache.clear()}}const d={speed:{min:2,max:5},spawnRate:3,maxParticles:50,animation:["fall"],size:{min:20,max:40},opacity:{min:.6,max:1},wind:0,autoStart:!0,zIndex:9999,responsive:!0};class u{constructor(t){if(this.particles=[],this.renderer=null,this.isRunning=!1,this.isPaused=!1,this.animationId=null,this.lastSpawnTime=0,this.startTime=0,this.lastFrameTime=0,this.resizeHandler=null,this.animate=t=>{if(!this.isRunning||!this.renderer)return;0===this.lastFrameTime&&(this.lastFrameTime=t,this.startTime=t);const i=Math.min(t-this.lastFrameTime,50),e=t-this.startTime;this.lastFrameTime=t;const s=1e3/this.options.spawnRate;t-this.lastSpawnTime>=s&&(this.spawnParticle(),this.lastSpawnTime=t);const n=[];for(const t of this.particles)t.update(i,e),t.isOutOfBounds()&&n.push(t);n.forEach(t=>this.removeParticle(t)),this.renderer.clear();const o=this.particles.map(t=>({x:t.x,y:t.y,rotation:t.rotation,size:t.size,opacity:t.opacity,content:t.content,type:"image"===t.objectType?"image":"emoji"}));this.renderer.drawParticles(o),this.animationId=requestAnimationFrame(this.animate)},!t.objects||0===t.objects.length)throw new Error('[falling-animation] "objects" option is required and must not be empty');this.options=this.resolveOptions(t),this.renderer=new p(this.options.container,this.options.zIndex),this.preloadImages(),this.options.responsive&&this.setupResizeHandler(),this.options.autoStart&&this.start()}async preloadImages(){if(this.renderer)for(const t of this.options.objects)if("image"===t.type){const i=t.src||t.content;if(i)try{await this.renderer.preloadImage(i)}catch(t){console.warn(`[falling-animation] Failed to preload image: ${i}`)}}}resolveOptions(t){var i,e,s,n,a,r,h,l,c;const p=function(t){if(!o())throw new Error("[falling-animation] This library requires a browser environment. If using SSR (Next.js, Nuxt, etc.), make sure to only initialize on the client side.");if(!t)return document.body;if("string"==typeof t){return document.querySelector(t)||(console.warn(`[falling-animation] Container "${t}" not found, using document.body`),document.body)}return t}(t.container);let u;return u=t.animation?"string"==typeof t.animation?[t.animation]:t.animation:d.animation,{container:p,objects:t.objects,speed:null!==(i=t.speed)&&void 0!==i?i:d.speed,spawnRate:null!==(e=t.spawnRate)&&void 0!==e?e:d.spawnRate,maxParticles:null!==(s=t.maxParticles)&&void 0!==s?s:d.maxParticles,animation:u,size:null!==(n=t.size)&&void 0!==n?n:d.size,opacity:null!==(a=t.opacity)&&void 0!==a?a:d.opacity,wind:null!==(r=t.wind)&&void 0!==r?r:d.wind,autoStart:null!==(h=t.autoStart)&&void 0!==h?h:d.autoStart,zIndex:null!==(l=t.zIndex)&&void 0!==l?l:d.zIndex,responsive:null!==(c=t.responsive)&&void 0!==c?c:d.responsive}}setupResizeHandler(){this.resizeHandler=a(()=>{if(this.renderer){this.renderer.resize();const{width:t,height:i}=this.renderer.getSize();this.particles.forEach(e=>{e.updateContainerSize(t,i)})}},200),window.addEventListener("resize",this.resizeHandler)}spawnParticle(){if(this.particles.length>=this.options.maxParticles)return;if(!this.renderer)return;const{width:t,height:i}=this.renderer.getSize(),e=new c(this.options,t,i);this.particles.push(e)}removeParticle(t){const i=this.particles.indexOf(t);i>-1&&this.particles.splice(i,1)}start(){this.isRunning||(this.isRunning=!0,this.isPaused=!1,this.lastFrameTime=0,this.lastSpawnTime=0,this.animationId=requestAnimationFrame(this.animate))}stop(){this.isRunning=!1,this.isPaused=!1,null!==this.animationId&&(cancelAnimationFrame(this.animationId),this.animationId=null),this.particles=[],this.renderer&&this.renderer.clear()}pause(){this.isRunning&&!this.isPaused&&(this.isPaused=!0,this.isRunning=!1,null!==this.animationId&&(cancelAnimationFrame(this.animationId),this.animationId=null))}resume(){!this.isRunning&&this.isPaused&&(this.isRunning=!0,this.isPaused=!1,this.lastFrameTime=0,this.animationId=requestAnimationFrame(this.animate))}setOptions(t){t.speed&&(this.options.speed=t.speed),void 0!==t.spawnRate&&(this.options.spawnRate=t.spawnRate),void 0!==t.maxParticles&&(this.options.maxParticles=t.maxParticles),t.animation&&(this.options.animation="string"==typeof t.animation?[t.animation]:t.animation),t.size&&(this.options.size=t.size),t.opacity&&(this.options.opacity=t.opacity),void 0!==t.wind&&(this.options.wind=t.wind),t.objects&&(this.options.objects=t.objects,this.preloadImages())}getParticleCount(){return this.particles.length}getIsRunning(){return this.isRunning}getIsPaused(){return this.isPaused}destroy(){this.stop(),this.renderer&&(this.renderer.destroy(),this.renderer=null),this.resizeHandler&&(window.removeEventListener("resize",this.resizeHandler),this.resizeHandler=null)}}const m={colors:["#ff0000","#ff6b00","#ffff00","#00ff00","#00ffff","#0066ff","#ff00ff","#ffffff","#ff69b4","#ffd700"],launchRate:.5,particlesPerExplosion:50,rocketSpeed:{min:7,max:12},explosionSpeed:{min:1,max:6},particleSize:{min:2,max:6},particleLifetime:{min:1e3,max:2e3},gravity:.1,trail:!0,explosionPattern:"circular",autoStart:!0,zIndex:9999};
2
2
  /**
3
3
  * falling-animation
4
4
  * A lightweight, customizable falling objects animation library
@@ -6,5 +6,4 @@
6
6
  * @author phongdh
7
7
  * @license MIT
8
8
  */
9
- t.FallingAnimation=u,t.Fireworks=class{constructor(t={}){var i,e,s,n,a,r,h,l,c,d,u,x;if(this.particles=[],this.renderer=null,this.isRunning=!1,this.animationId=null,this.lastLaunchTime=0,this.lastFrameTime=0,this.resizeHandler=null,this.animate=t=>{var i;if(!this.isRunning||!this.renderer)return;0===this.lastFrameTime&&(this.lastFrameTime=t);const e=Math.min(t-this.lastFrameTime,50);this.lastFrameTime=t;const s=1e3/this.options.launchRate;t-this.lastLaunchTime>=s&&(this.launchRocket(),this.lastLaunchTime=t);const n=[],o=[];for(const t of this.particles)if(t.age+=e,"rocket"===t.phase){t.x+=t.vx*e*.1,t.y+=t.vy*e*.1,t.vy+=.01*e;const s=null!==(i=t.targetY)&&void 0!==i?i:0;(t.y<=s||t.age>=t.maxAge||t.vy>=0)&&o.push(t),t.opacity=Math.max(.5,1-t.age/t.maxAge*.3)}else{t.x+=t.vx*e*.1,t.y+=t.vy*e*.1,t.vy+=t.gravity*e*.01;const i=t.age/t.maxAge;t.opacity=Math.max(0,1-i);const s=t.size<2?.96:.99;t.vx*=s,t.vy*=s,1===t.stage&&i>=.5&&-1!==t.targetY&&(t.targetY=-1,o.push(t)),t.age>=t.maxAge&&n.push(t)}for(const t of o)1===t.stage?this.explodeSecondary(t):this.explode(t),n.push(t);for(const t of n)this.removeParticle(t);this.renderer.clear();const a=this.particles.map(t=>({x:t.x,y:t.y,size:t.size,opacity:t.opacity,color:t.color}));this.renderer.drawFireworkParticles(a),this.animationId=requestAnimationFrame(this.animate)},!o())throw new Error("[falling-animation] Fireworks requires a browser environment. If using SSR (Next.js, Nuxt, etc.), make sure to only initialize on the client side.");let f;f=t.container?"string"==typeof t.container?document.querySelector(t.container)||document.body:t.container:document.body,this.options={container:f,colors:null!==(i=t.colors)&&void 0!==i?i:m.colors,launchRate:null!==(e=t.launchRate)&&void 0!==e?e:m.launchRate,particlesPerExplosion:null!==(s=t.particlesPerExplosion)&&void 0!==s?s:m.particlesPerExplosion,rocketSpeed:null!==(n=t.rocketSpeed)&&void 0!==n?n:m.rocketSpeed,explosionSpeed:null!==(a=t.explosionSpeed)&&void 0!==a?a:m.explosionSpeed,particleSize:null!==(r=t.particleSize)&&void 0!==r?r:m.particleSize,particleLifetime:null!==(h=t.particleLifetime)&&void 0!==h?h:m.particleLifetime,gravity:null!==(l=t.gravity)&&void 0!==l?l:m.gravity,trail:null!==(c=t.trail)&&void 0!==c?c:m.trail,explosionPattern:null!==(d=t.explosionPattern)&&void 0!==d?d:m.explosionPattern,autoStart:null!==(u=t.autoStart)&&void 0!==u?u:m.autoStart,zIndex:null!==(x=t.zIndex)&&void 0!==x?x:m.zIndex},this.renderer=new p(f,this.options.zIndex),this.setupResizeHandler(),this.options.autoStart&&this.start()}setupResizeHandler(){this.resizeHandler=a(()=>{this.renderer&&this.renderer.resize()},200),window.addEventListener("resize",this.resizeHandler)}getContainerSize(){return this.renderer?this.renderer.getSize():{width:0,height:0}}launchRocket(){const{width:t,height:s}=this.getContainerSize();if(0===t||0===s)return;const o=this.options.colors[Math.floor(Math.random()*this.options.colors.length)],a=i(.1*s,.9*s),r=s-a,h=e(this.options.rocketSpeed),l=r/(.1*h),c={id:n(),x:i(.1*t,.9*t),y:s+10,vx:i(-.5,.5),vy:-h,size:4,opacity:1,color:o,age:0,maxAge:Math.min(l,3e3),phase:"rocket",gravity:0,targetY:a,stage:0};this.particles.push(c)}explode(t){const i=this.options.particlesPerExplosion,e=t.color;let s;const n=this.options.explosionPattern;if(Array.isArray(n))s=n[Math.floor(Math.random()*n.length)];else if("random"===n){const t=["circular","ring","heart","star","willow","palm","chrysanthemum","embers","double"];s=t[Math.floor(Math.random()*t.length)]}else s=n;const o=this.generatePatternParticles(s,t,i,e);for(const t of o)this.particles.push(t)}generatePatternParticles(t,i,e,s){switch(t){case"ring":return this.createRingExplosion(i,e,s);case"heart":return this.createHeartExplosion(i,e,s);case"star":return this.createStarExplosion(i,e,s);case"willow":return this.createWillowExplosion(i,e,s);case"palm":return this.createPalmExplosion(i,e,s);case"chrysanthemum":return this.createChrysanthemumExplosion(i,e,s);case"embers":return this.createEmbersExplosion(i,e,s);case"double":return this.createDoubleExplosion(i,e,s);default:return this.createCircularExplosion(i,e,s)}}createCircularExplosion(t,s,n){const o=[];for(let a=0;a<s;a++){const r=a/s*Math.PI*2+i(-.1,.1),h=e(this.options.explosionSpeed);o.push(this.createExplosionParticle(t,n,r,h))}return o}createRingExplosion(t,e,s){const n=[],o=(this.options.explosionSpeed.min+this.options.explosionSpeed.max)/2;for(let a=0;a<e;a++){const r=a/e*Math.PI*2,h=o+i(-.5,.5);n.push(this.createExplosionParticle(t,s,r,h,.05))}return n}createHeartExplosion(t,i,s){const n=[];for(let o=0;o<i;o++){const a=o/i*Math.PI*2,r=16*Math.pow(Math.sin(a),3),h=-(13*Math.cos(a)-5*Math.cos(2*a)-2*Math.cos(3*a)-Math.cos(4*a)),l=Math.atan2(h,r),c=Math.sqrt(r*r+h*h)/16*e(this.options.explosionSpeed);n.push(this.createExplosionParticle(t,s,l,.8*c,.08))}return n}createStarExplosion(t,s,n){const o=[];for(let a=0;a<s;a++){const r=Math.floor(a/(s/5))/5*Math.PI*2+i(-.15,.15),h=e(this.options.explosionSpeed)*(.8+.4*Math.random());o.push(this.createExplosionParticle(t,n,r,h))}return o}createWillowExplosion(t,s,n){const o=[];for(let n=0;n<s;n++){const a=n/s*Math.PI*2+i(-.1,.1),r=.6*e(this.options.explosionSpeed),h=this.createExplosionParticle(t,"#ffd700",a,r,.25);h.maxAge*=1.5,o.push(h)}return o}createPalmExplosion(t,s,n){const o=[];for(let a=0;a<s;a++){const s=i(.8*-Math.PI,.8*Math.PI)-Math.PI/2,a=e(this.options.explosionSpeed)*(.7+.6*Math.random()),r=this.createExplosionParticle(t,n,s,a,.18);r.maxAge*=1.2,o.push(r)}return o}createChrysanthemumExplosion(t,e,s){const n=[];for(let o=0;o<3;o++){const a=Math.floor(e/3),r=this.options.explosionSpeed.min+(this.options.explosionSpeed.max-this.options.explosionSpeed.min)*(o/3);for(let e=0;e<a;e++){const h=e/a*Math.PI*2+.3*o,l=r+i(-.5,.5);n.push(this.createExplosionParticle(t,s,h,l,.08))}}return n}createEmbersExplosion(t,s,o){const a=[],r=Math.floor(1.5*s);for(let s=0;s<r;s++){const h=s/r*Math.PI*2+i(-.3,.3),l=.3*e(this.options.explosionSpeed),c={id:n(),x:t.x+i(-5,5),y:t.y+i(-5,5),vx:Math.cos(h)*l,vy:Math.sin(h)*l,size:i(.5,1.5),opacity:1,color:this.getEmberColor(o),age:0,maxAge:2*e(this.options.particleLifetime),phase:"explosion",gravity:.02,stage:t.stage+1};a.push(c)}return a}createDoubleExplosion(t,s,o){const a=[],r=Math.floor(.6*s);for(let s=0;s<r;s++){const h=s/r*Math.PI*2+i(-.1,.1),l=.8*e(this.options.explosionSpeed),c={id:n(),x:t.x,y:t.y,vx:Math.cos(h)*l,vy:Math.sin(h)*l,size:e(this.options.particleSize),opacity:1,color:this.getVariantColor(o),age:0,maxAge:e(this.options.particleLifetime),phase:"explosion",gravity:this.options.gravity,stage:1};a.push(c)}return a}explodeSecondary(t){const s=Math.floor(.3*this.options.particlesPerExplosion);for(let o=0;o<s;o++){const a=o/s*Math.PI*2+i(-.2,.2),r=.5*e(this.options.explosionSpeed),h={id:n(),x:t.x,y:t.y,vx:Math.cos(a)*r,vy:Math.sin(a)*r,size:Math.max(1,e(this.options.particleSize)/i(2,3)),opacity:.9,color:this.getVariantColor(t.color),age:0,maxAge:.7*e(this.options.particleLifetime),phase:"explosion",gravity:.5*this.options.gravity,stage:2};this.particles.push(h)}}getEmberColor(t){const i=["#ffaa00","#ff6600","#ff4400","#ffcc00","#ff8800","#ffffff"];return Math.random()<.7?i[Math.floor(Math.random()*i.length)]:t}createExplosionParticle(t,i,s,o,a){return{id:n(),x:t.x,y:t.y,vx:Math.cos(s)*o,vy:Math.sin(s)*o,size:e(this.options.particleSize),opacity:1,color:this.getVariantColor(i),age:0,maxAge:e(this.options.particleLifetime),phase:"explosion",gravity:null!=a?a:this.options.gravity,stage:t.stage+1}}getVariantColor(t){return Math.random()<.2?"#ffffff":Math.random()<.3?this.options.colors[Math.floor(Math.random()*this.options.colors.length)]:t}removeParticle(t){const i=this.particles.indexOf(t);i>-1&&this.particles.splice(i,1)}start(){this.isRunning||(this.isRunning=!0,this.lastFrameTime=0,this.lastLaunchTime=0,this.animationId=requestAnimationFrame(this.animate))}stop(){this.isRunning=!1,null!==this.animationId&&(cancelAnimationFrame(this.animationId),this.animationId=null)}clear(){this.particles=[],this.renderer&&this.renderer.clear()}launch(){this.launchRocket()}burst(t=5){for(let i=0;i<t;i++)setTimeout(()=>this.launchRocket(),100*i)}setOptions(t){t.colors&&(this.options.colors=t.colors),void 0!==t.launchRate&&(this.options.launchRate=t.launchRate),void 0!==t.particlesPerExplosion&&(this.options.particlesPerExplosion=t.particlesPerExplosion),t.rocketSpeed&&(this.options.rocketSpeed=t.rocketSpeed),t.explosionSpeed&&(this.options.explosionSpeed=t.explosionSpeed),t.particleSize&&(this.options.particleSize=t.particleSize),t.particleLifetime&&(this.options.particleLifetime=t.particleLifetime),void 0!==t.gravity&&(this.options.gravity=t.gravity),t.explosionPattern&&(this.options.explosionPattern=t.explosionPattern)}getParticleCount(){return this.particles.length}getIsRunning(){return this.isRunning}destroy(){this.stop(),this.clear(),this.renderer&&(this.renderer.destroy(),this.renderer=null),this.resizeHandler&&(window.removeEventListener("resize",this.resizeHandler),this.resizeHandler=null)}},t.animations=h,t.default=u,t.getAnimation=l,t.isBrowser=o,Object.defineProperty(t,"__esModule",{value:!0})});
10
- //# sourceMappingURL=falling-animation.umd.min.js.map
9
+ t.FallingAnimation=u,t.Fireworks=class{constructor(t={}){var i,e,s,n,a,r,h,l,c,d,u,x;if(this.particles=[],this.renderer=null,this.isRunning=!1,this.animationId=null,this.lastLaunchTime=0,this.lastFrameTime=0,this.resizeHandler=null,this.animate=t=>{var i;if(!this.isRunning||!this.renderer)return;0===this.lastFrameTime&&(this.lastFrameTime=t);const e=Math.min(t-this.lastFrameTime,50);this.lastFrameTime=t;const s=1e3/this.options.launchRate;t-this.lastLaunchTime>=s&&(this.launchRocket(),this.lastLaunchTime=t);const n=[],o=[];for(const t of this.particles)if(t.age+=e,"rocket"===t.phase){t.x+=t.vx*e*.1,t.y+=t.vy*e*.1,t.vy+=.01*e;const s=null!==(i=t.targetY)&&void 0!==i?i:0;(t.y<=s||t.age>=t.maxAge||t.vy>=0)&&o.push(t),t.opacity=Math.max(.5,1-t.age/t.maxAge*.3)}else{t.x+=t.vx*e*.1,t.y+=t.vy*e*.1,t.vy+=t.gravity*e*.01;const i=t.age/t.maxAge;t.opacity=Math.max(0,1-i);const s=t.size<2?.96:.99;t.vx*=s,t.vy*=s,t.hasSecondaryExplosion&&i>=.5&&-1!==t.targetY&&(t.targetY=-1,o.push(t)),t.age>=t.maxAge&&n.push(t)}for(const t of o)1===t.stage?this.explodeSecondary(t):this.explode(t),n.push(t);for(const t of n)this.removeParticle(t);this.renderer.clear();const a=this.particles.map(t=>({x:t.x,y:t.y,size:t.size,opacity:t.opacity,color:t.color}));this.renderer.drawFireworkParticles(a),this.animationId=requestAnimationFrame(this.animate)},!o())throw new Error("[falling-animation] Fireworks requires a browser environment. If using SSR (Next.js, Nuxt, etc.), make sure to only initialize on the client side.");let f;f=t.container?"string"==typeof t.container?document.querySelector(t.container)||document.body:t.container:document.body,this.options={container:f,colors:null!==(i=t.colors)&&void 0!==i?i:m.colors,launchRate:null!==(e=t.launchRate)&&void 0!==e?e:m.launchRate,particlesPerExplosion:null!==(s=t.particlesPerExplosion)&&void 0!==s?s:m.particlesPerExplosion,rocketSpeed:null!==(n=t.rocketSpeed)&&void 0!==n?n:m.rocketSpeed,explosionSpeed:null!==(a=t.explosionSpeed)&&void 0!==a?a:m.explosionSpeed,particleSize:null!==(r=t.particleSize)&&void 0!==r?r:m.particleSize,particleLifetime:null!==(h=t.particleLifetime)&&void 0!==h?h:m.particleLifetime,gravity:null!==(l=t.gravity)&&void 0!==l?l:m.gravity,trail:null!==(c=t.trail)&&void 0!==c?c:m.trail,explosionPattern:null!==(d=t.explosionPattern)&&void 0!==d?d:m.explosionPattern,autoStart:null!==(u=t.autoStart)&&void 0!==u?u:m.autoStart,zIndex:null!==(x=t.zIndex)&&void 0!==x?x:m.zIndex},this.renderer=new p(f,this.options.zIndex),this.setupResizeHandler(),this.options.autoStart&&this.start()}setupResizeHandler(){this.resizeHandler=a(()=>{this.renderer&&this.renderer.resize()},200),window.addEventListener("resize",this.resizeHandler)}getContainerSize(){return this.renderer?this.renderer.getSize():{width:0,height:0}}launchRocket(){const{width:t,height:s}=this.getContainerSize();if(0===t||0===s)return;const o=this.options.colors[Math.floor(Math.random()*this.options.colors.length)],a=i(.1*s,.9*s),r=s-a,h=e(this.options.rocketSpeed),l=r/(.1*h),c={id:n(),x:i(.1*t,.9*t),y:s+10,vx:i(-.5,.5),vy:-h,size:4,opacity:1,color:o,age:0,maxAge:Math.min(l,3e3),phase:"rocket",gravity:0,targetY:a,stage:0};this.particles.push(c)}explode(t){const i=this.options.particlesPerExplosion,e=t.color;let s;const n=this.options.explosionPattern;if(Array.isArray(n))s=n[Math.floor(Math.random()*n.length)];else if("random"===n){const t=["circular","ring","heart","star","willow","palm","chrysanthemum","embers","double","waterfall"];s=t[Math.floor(Math.random()*t.length)]}else s=n;const o=this.generatePatternParticles(s,t,i,e);for(const t of o)this.particles.push(t)}generatePatternParticles(t,i,e,s){switch(t){case"ring":return this.createRingExplosion(i,e,s);case"heart":return this.createHeartExplosion(i,e,s);case"star":return this.createStarExplosion(i,e,s);case"willow":return this.createWillowExplosion(i,e,s);case"palm":return this.createPalmExplosion(i,e,s);case"chrysanthemum":return this.createChrysanthemumExplosion(i,e,s);case"embers":return this.createEmbersExplosion(i,e,s);case"double":return this.createDoubleExplosion(i,e,s);case"waterfall":return this.createWaterfallExplosion(i,e,s);default:return this.createCircularExplosion(i,e,s)}}createCircularExplosion(t,s,n){const o=[];for(let a=0;a<s;a++){const r=a/s*Math.PI*2+i(-.1,.1),h=e(this.options.explosionSpeed);o.push(this.createExplosionParticle(t,n,r,h))}return o}createRingExplosion(t,e,s){const n=[],o=(this.options.explosionSpeed.min+this.options.explosionSpeed.max)/2;for(let a=0;a<e;a++){const r=a/e*Math.PI*2,h=o+i(-.5,.5);n.push(this.createExplosionParticle(t,s,r,h,.05))}return n}createHeartExplosion(t,i,s){const n=[];for(let o=0;o<i;o++){const a=o/i*Math.PI*2,r=16*Math.pow(Math.sin(a),3),h=-(13*Math.cos(a)-5*Math.cos(2*a)-2*Math.cos(3*a)-Math.cos(4*a)),l=Math.atan2(h,r),c=Math.sqrt(r*r+h*h)/16*e(this.options.explosionSpeed);n.push(this.createExplosionParticle(t,s,l,.8*c,.08))}return n}createStarExplosion(t,s,n){const o=[];for(let a=0;a<s;a++){const r=Math.floor(a/(s/5))/5*Math.PI*2+i(-.15,.15),h=e(this.options.explosionSpeed)*(.8+.4*Math.random());o.push(this.createExplosionParticle(t,n,r,h))}return o}createWillowExplosion(t,s,n){const o=[];for(let n=0;n<s;n++){const a=n/s*Math.PI*2+i(-.1,.1),r=.6*e(this.options.explosionSpeed),h=this.createExplosionParticle(t,"#ffd700",a,r,.25);h.maxAge*=1.5,o.push(h)}return o}createPalmExplosion(t,s,n){const o=[];for(let a=0;a<s;a++){const s=i(.8*-Math.PI,.8*Math.PI)-Math.PI/2,a=e(this.options.explosionSpeed)*(.7+.6*Math.random()),r=this.createExplosionParticle(t,n,s,a,.18);r.maxAge*=1.2,o.push(r)}return o}createChrysanthemumExplosion(t,e,s){const n=[];for(let o=0;o<3;o++){const a=Math.floor(e/3),r=this.options.explosionSpeed.min+(this.options.explosionSpeed.max-this.options.explosionSpeed.min)*(o/3);for(let e=0;e<a;e++){const h=e/a*Math.PI*2+.3*o,l=r+i(-.5,.5);n.push(this.createExplosionParticle(t,s,h,l,.08))}}return n}createEmbersExplosion(t,s,o){const a=[],r=Math.floor(1.5*s);for(let s=0;s<r;s++){const h=s/r*Math.PI*2+i(-.3,.3),l=.3*e(this.options.explosionSpeed),c={id:n(),x:t.x+i(-5,5),y:t.y+i(-5,5),vx:Math.cos(h)*l,vy:Math.sin(h)*l,size:i(.5,1.5),opacity:1,color:this.getEmberColor(o),age:0,maxAge:2*e(this.options.particleLifetime),phase:"explosion",gravity:.02,stage:t.stage+1};a.push(c)}return a}createDoubleExplosion(t,s,o){const a=[],r=Math.floor(.6*s);for(let s=0;s<r;s++){const h=s/r*Math.PI*2+i(-.1,.1),l=.8*e(this.options.explosionSpeed),c={id:n(),x:t.x,y:t.y,vx:Math.cos(h)*l,vy:Math.sin(h)*l,size:e(this.options.particleSize),opacity:1,color:this.getVariantColor(o),age:0,maxAge:e(this.options.particleLifetime),phase:"explosion",gravity:this.options.gravity,stage:1,hasSecondaryExplosion:!0};a.push(c)}return a}createWaterfallExplosion(t,i,s){const o=[],a=Math.floor(2.5*i);for(let i=0;i<a;i++){const i=Math.PI+Math.random()*Math.PI,a=.4*e(this.options.explosionSpeed),r={id:n(),x:t.x,y:t.y,vx:Math.cos(i)*a,vy:Math.sin(i)*a*.5,size:e(this.options.particleSize),opacity:1,color:this.getVariantColor(s),age:0,maxAge:2.5*e(this.options.particleLifetime),phase:"explosion",gravity:.25,stage:0};o.push(r)}return o}explodeSecondary(t){const s=Math.floor(.3*this.options.particlesPerExplosion);for(let o=0;o<s;o++){const a=o/s*Math.PI*2+i(-.2,.2),r=.5*e(this.options.explosionSpeed),h={id:n(),x:t.x,y:t.y,vx:Math.cos(a)*r,vy:Math.sin(a)*r,size:Math.max(1,e(this.options.particleSize)/i(2,3)),opacity:.9,color:this.getVariantColor(t.color),age:0,maxAge:.7*e(this.options.particleLifetime),phase:"explosion",gravity:.5*this.options.gravity,stage:2};this.particles.push(h)}}getEmberColor(t){const i=["#ffaa00","#ff6600","#ff4400","#ffcc00","#ff8800","#ffffff"];return Math.random()<.7?i[Math.floor(Math.random()*i.length)]:t}createExplosionParticle(t,i,s,o,a){return{id:n(),x:t.x,y:t.y,vx:Math.cos(s)*o,vy:Math.sin(s)*o,size:e(this.options.particleSize),opacity:1,color:this.getVariantColor(i),age:0,maxAge:e(this.options.particleLifetime),phase:"explosion",gravity:null!=a?a:this.options.gravity,stage:t.stage+1}}getVariantColor(t){return Math.random()<.2?"#ffffff":Math.random()<.3?this.options.colors[Math.floor(Math.random()*this.options.colors.length)]:t}removeParticle(t){const i=this.particles.indexOf(t);i>-1&&this.particles.splice(i,1)}start(){this.isRunning||(this.isRunning=!0,this.lastFrameTime=0,this.lastLaunchTime=0,this.animationId=requestAnimationFrame(this.animate))}stop(){this.isRunning=!1,null!==this.animationId&&(cancelAnimationFrame(this.animationId),this.animationId=null)}clear(){this.particles=[],this.renderer&&this.renderer.clear()}launch(){this.launchRocket()}burst(t=5){for(let i=0;i<t;i++)setTimeout(()=>this.launchRocket(),100*i)}setOptions(t){t.colors&&(this.options.colors=t.colors),void 0!==t.launchRate&&(this.options.launchRate=t.launchRate),void 0!==t.particlesPerExplosion&&(this.options.particlesPerExplosion=t.particlesPerExplosion),t.rocketSpeed&&(this.options.rocketSpeed=t.rocketSpeed),t.explosionSpeed&&(this.options.explosionSpeed=t.explosionSpeed),t.particleSize&&(this.options.particleSize=t.particleSize),t.particleLifetime&&(this.options.particleLifetime=t.particleLifetime),void 0!==t.gravity&&(this.options.gravity=t.gravity),t.explosionPattern&&(this.options.explosionPattern=t.explosionPattern)}getParticleCount(){return this.particles.length}getIsRunning(){return this.isRunning}destroy(){this.stop(),this.clear(),this.renderer&&(this.renderer.destroy(),this.renderer=null),this.resizeHandler&&(window.removeEventListener("resize",this.resizeHandler),this.resizeHandler=null)}},t.animations=h,t.default=u,t.getAnimation=l,t.isBrowser=o,Object.defineProperty(t,"__esModule",{value:!0})});
@@ -42,7 +42,11 @@ export declare class CanvasRenderer {
42
42
  /**
43
43
  * Clear the entire canvas
44
44
  */
45
- clear(): void;
45
+ /**
46
+ * Clear the entire canvas
47
+ * @param trail If true, fades out existing content instead of hard clear
48
+ */
49
+ clear(trail?: boolean): void;
46
50
  /**
47
51
  * Pre-cache an emoji as a sprite for fast drawing (HiDPI aware)
48
52
  */
@@ -10,7 +10,7 @@
10
10
  */
11
11
  import { RangeValue } from './types';
12
12
  /** Available explosion patterns */
13
- export type ExplosionPattern = 'circular' | 'ring' | 'heart' | 'star' | 'willow' | 'palm' | 'chrysanthemum' | 'embers' | 'double' | 'random';
13
+ export type ExplosionPattern = 'circular' | 'ring' | 'heart' | 'star' | 'willow' | 'palm' | 'chrysanthemum' | 'embers' | 'double' | 'waterfall' | 'random';
14
14
  /** Fireworks configuration options */
15
15
  export interface FireworksOptions {
16
16
  /** Container element or selector (default: document.body) */
@@ -77,6 +77,8 @@ export declare class Fireworks {
77
77
  * Secondary particles are 2-3x smaller than primary
78
78
  */
79
79
  private createDoubleExplosion;
80
+ /** Waterfall - Gentle explosion up, then heavy fall like water */
81
+ private createWaterfallExplosion;
80
82
  /** Create secondary explosion particles (called when stage 1 particles reach 50% lifetime)
81
83
  * These particles are 2-3x smaller than the originals
82
84
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "falling-animation",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "type": "module",
5
5
  "description": "A lightweight, customizable falling objects animation library for web",
6
6
  "main": "dist/falling-animation.cjs",
@@ -19,7 +19,7 @@
19
19
  "dist"
20
20
  ],
21
21
  "scripts": {
22
- "build": "rollup -c",
22
+ "build": "rm -rf dist && rollup -c",
23
23
  "dev": "rollup -c -w",
24
24
  "demo": "npx serve . -l 3000",
25
25
  "prepublishOnly": "npm run build"