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 +74 -23
- package/dist/falling-animation.cjs +56 -10
- package/dist/falling-animation.esm.js +56 -10
- package/dist/falling-animation.umd.js +56 -10
- package/dist/falling-animation.umd.min.js +2 -3
- package/dist/types/CanvasRenderer.d.ts +5 -1
- package/dist/types/Fireworks.d.ts +3 -1
- package/package.json +2 -2
- package/dist/falling-animation.cjs.js +0 -1019
- package/dist/falling-animation.cjs.js.map +0 -1
- package/dist/falling-animation.cjs.map +0 -1
- package/dist/falling-animation.esm.js.map +0 -1
- package/dist/falling-animation.umd.js.map +0 -1
- package/dist/falling-animation.umd.min.js.map +0 -1
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:
|
|
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:
|
|
285
|
+
// Rocket speed range (default: { min: 7, max: 12 })
|
|
286
286
|
rocketSpeed?: { min: number; max: number };
|
|
287
287
|
|
|
288
|
-
// Explosion particle speed (default: { min:
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
343
|
-
particlesPerExplosion:
|
|
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
|
-
####
|
|
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
|
-
|
|
352
|
-
|
|
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
|
|
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:
|
|
361
|
-
particlesPerExplosion:
|
|
362
|
-
|
|
363
|
-
|
|
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
|
|
367
|
-
fw.burst(
|
|
412
|
+
// Launch a massive burst manually when the timer hits zero!
|
|
413
|
+
// fw.burst(15);
|
|
368
414
|
```
|
|
369
415
|
|
|
370
|
-
####
|
|
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
|
-
|
|
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
|
-
|
|
392
|
-
|
|
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:
|
|
851
|
+
launchRate: 0.5,
|
|
840
852
|
particlesPerExplosion: 50,
|
|
841
|
-
rocketSpeed: { min:
|
|
842
|
-
explosionSpeed: { min:
|
|
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
|
|
902
|
-
if (particle.
|
|
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
|
|
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
|
-
|
|
388
|
-
|
|
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:
|
|
847
|
+
launchRate: 0.5,
|
|
836
848
|
particlesPerExplosion: 50,
|
|
837
|
-
rocketSpeed: { min:
|
|
838
|
-
explosionSpeed: { min:
|
|
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
|
|
898
|
-
if (particle.
|
|
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
|
|
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
|
-
|
|
394
|
-
|
|
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:
|
|
853
|
+
launchRate: 0.5,
|
|
842
854
|
particlesPerExplosion: 50,
|
|
843
|
-
rocketSpeed: { min:
|
|
844
|
-
explosionSpeed: { min:
|
|
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
|
|
904
|
-
if (particle.
|
|
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
|
|
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,
|
|
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
|
-
|
|
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
|
+
"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"
|