falling-animation 0.1.0

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.
@@ -0,0 +1,670 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ /**
6
+ * Utility functions for falling-animation
7
+ */
8
+ /**
9
+ * Generate a random number between min and max
10
+ */
11
+ function randomRange(min, max) {
12
+ return Math.random() * (max - min) + min;
13
+ }
14
+ /**
15
+ * Generate a random number from a RangeValue
16
+ */
17
+ function randomFromRange(range) {
18
+ return randomRange(range.min, range.max);
19
+ }
20
+ /**
21
+ * Pick a random item from an array
22
+ */
23
+ function randomPick(array) {
24
+ return array[Math.floor(Math.random() * array.length)];
25
+ }
26
+ /**
27
+ * Pick a random item based on weights
28
+ */
29
+ function weightedRandomPick(items) {
30
+ var _a;
31
+ const totalWeight = items.reduce((sum, item) => { var _a; return sum + ((_a = item.weight) !== null && _a !== void 0 ? _a : 1); }, 0);
32
+ let random = Math.random() * totalWeight;
33
+ for (const item of items) {
34
+ random -= (_a = item.weight) !== null && _a !== void 0 ? _a : 1;
35
+ if (random <= 0) {
36
+ return item;
37
+ }
38
+ }
39
+ return items[items.length - 1];
40
+ }
41
+ /**
42
+ * Generate a unique ID
43
+ */
44
+ let idCounter = 0;
45
+ function generateId() {
46
+ return ++idCounter;
47
+ }
48
+ /**
49
+ * Resolve container from element or selector
50
+ */
51
+ function resolveContainer(container) {
52
+ if (!container) {
53
+ return document.body;
54
+ }
55
+ if (typeof container === 'string') {
56
+ const element = document.querySelector(container);
57
+ if (!element) {
58
+ console.warn(`[falling-animation] Container "${container}" not found, using document.body`);
59
+ return document.body;
60
+ }
61
+ return element;
62
+ }
63
+ return container;
64
+ }
65
+ /**
66
+ * Throttle function calls
67
+ */
68
+ function throttle(func, limit) {
69
+ let inThrottle = false;
70
+ return function (...args) {
71
+ if (!inThrottle) {
72
+ func.apply(this, args);
73
+ inThrottle = true;
74
+ setTimeout(() => (inThrottle = false), limit);
75
+ }
76
+ };
77
+ }
78
+
79
+ /**
80
+ * Animation functions for falling particles
81
+ * Each animation modifies particle state based on deltaTime and elapsed time
82
+ */
83
+ /**
84
+ * Simple vertical fall - no special effects
85
+ */
86
+ const fall = (particle, deltaTime) => {
87
+ // Skip movement if speed is 0
88
+ if (particle.vy === 0 && particle.vx === 0)
89
+ return;
90
+ particle.y += particle.vy * deltaTime;
91
+ particle.x += particle.vx * deltaTime;
92
+ };
93
+ /**
94
+ * Swing - pendulum-like swinging motion while falling
95
+ */
96
+ const swing = (particle, deltaTime, elapsed) => {
97
+ const swingAmplitude = 30; // pixels
98
+ const swingFrequency = 0.002; // oscillations per ms
99
+ // Slight rotation based on swing - always animate
100
+ particle.rotation = Math.sin((elapsed + particle.phase) * swingFrequency) * 20;
101
+ // Skip movement if speed is 0
102
+ if (particle.vy === 0 && particle.vx === 0)
103
+ return;
104
+ particle.y += particle.vy * deltaTime;
105
+ // Calculate swing offset
106
+ const swingOffset = Math.sin((elapsed + particle.phase) * swingFrequency) * swingAmplitude;
107
+ particle.x += particle.vx * deltaTime + (swingOffset * 0.05);
108
+ };
109
+ /**
110
+ * Rotate - continuous 360° rotation while falling
111
+ */
112
+ const rotate = (particle, deltaTime) => {
113
+ // Continuous rotation - always animate
114
+ particle.rotation += particle.rotationSpeed * deltaTime;
115
+ // Skip movement if speed is 0
116
+ if (particle.vy === 0 && particle.vx === 0)
117
+ return;
118
+ particle.y += particle.vy * deltaTime;
119
+ particle.x += particle.vx * deltaTime;
120
+ };
121
+ /**
122
+ * Flutter - butterfly-like fluttering motion
123
+ */
124
+ const flutter = (particle, deltaTime, elapsed) => {
125
+ const flutterFrequency = 0.005;
126
+ // Tilt based on direction - always animate
127
+ particle.rotation = Math.sin((elapsed + particle.phase) * flutterFrequency) * 30;
128
+ // Skip movement if speed is 0
129
+ if (particle.vy === 0 && particle.vx === 0)
130
+ return;
131
+ const flutterAmplitude = 40;
132
+ const verticalWobble = 0.3;
133
+ // Horizontal flutter
134
+ const flutter = Math.sin((elapsed + particle.phase) * flutterFrequency) * flutterAmplitude;
135
+ particle.x += particle.vx * deltaTime + flutter * 0.03;
136
+ // Vertical movement with slight wobble
137
+ const wobble = Math.sin((elapsed + particle.phase) * flutterFrequency * 2) * verticalWobble;
138
+ particle.y += (particle.vy + wobble) * deltaTime;
139
+ };
140
+ /**
141
+ * Spiral - spiraling down pattern
142
+ */
143
+ const spiral = (particle, deltaTime, elapsed) => {
144
+ const spiralSpeed = 0.003;
145
+ const angle = (elapsed + particle.phase) * spiralSpeed;
146
+ // Rotate with spiral - always animate
147
+ particle.rotation = angle * (180 / Math.PI);
148
+ // Skip movement if speed is 0
149
+ if (particle.vy === 0 && particle.vx === 0)
150
+ return;
151
+ const spiralRadius = 25;
152
+ particle.y += particle.vy * deltaTime;
153
+ // Spiral motion
154
+ particle.x += particle.vx * deltaTime + Math.cos(angle) * spiralRadius * 0.02;
155
+ };
156
+ /**
157
+ * Tumble - chaotic tumbling motion
158
+ */
159
+ const tumble = (particle, deltaTime, elapsed) => {
160
+ var _a, _b, _c;
161
+ const tumbleSpeed = (_a = particle.data.tumbleSpeed) !== null && _a !== void 0 ? _a : 5;
162
+ // Fast tumbling rotation - always animate
163
+ particle.rotation += tumbleSpeed * deltaTime;
164
+ // Skip movement if speed is 0
165
+ if (particle.vy === 0 && particle.vx === 0)
166
+ return;
167
+ const wobbleX = (_b = particle.data.wobbleX) !== null && _b !== void 0 ? _b : 0.02;
168
+ const wobbleY = (_c = particle.data.wobbleY) !== null && _c !== void 0 ? _c : 0.01;
169
+ particle.y += particle.vy * deltaTime;
170
+ // Chaotic horizontal movement
171
+ particle.x += particle.vx * deltaTime +
172
+ Math.sin((elapsed + particle.phase) * wobbleX) * 20 * 0.02;
173
+ // Add some vertical variation
174
+ particle.y += Math.sin((elapsed + particle.phase * 1.5) * wobbleY) * 0.5;
175
+ };
176
+ /**
177
+ * Zigzag - zigzag falling pattern
178
+ */
179
+ const zigzag = (particle, deltaTime, elapsed) => {
180
+ const zigzagFrequency = 0.002;
181
+ const period = 1 / zigzagFrequency;
182
+ const t = ((elapsed + particle.phase) % period) / period;
183
+ const triangleWave = Math.abs(t * 2 - 1) * 2 - 1;
184
+ // Tilt in direction of movement - always animate
185
+ particle.rotation = triangleWave * 25;
186
+ // Skip movement if speed is 0
187
+ if (particle.vy === 0 && particle.vx === 0)
188
+ return;
189
+ const zigzagWidth = 50;
190
+ particle.y += particle.vy * deltaTime;
191
+ // Create zigzag using triangle wave
192
+ particle.x += particle.vx * deltaTime + triangleWave * zigzagWidth * 0.02;
193
+ };
194
+ /**
195
+ * Float - slow floating descent with gentle movement
196
+ */
197
+ const float = (particle, deltaTime, elapsed) => {
198
+ const floatFrequency = 0.001;
199
+ // Gentle rotation - always animate
200
+ particle.rotation = Math.sin((elapsed + particle.phase) * floatFrequency) * 10;
201
+ // Skip movement if speed is 0
202
+ if (particle.vy === 0 && particle.vx === 0)
203
+ return;
204
+ const floatAmplitudeX = 20;
205
+ const floatAmplitudeY = 5;
206
+ // Very slow descent with vertical wobble
207
+ const yWobble = Math.sin((elapsed + particle.phase) * floatFrequency * 2) * floatAmplitudeY;
208
+ particle.y += (particle.vy * 0.5 + yWobble * 0.01) * deltaTime;
209
+ // Gentle horizontal drift
210
+ const xDrift = Math.sin((elapsed + particle.phase) * floatFrequency) * floatAmplitudeX;
211
+ particle.x += particle.vx * deltaTime + xDrift * 0.01;
212
+ };
213
+ /**
214
+ * Animation registry - all available animations
215
+ */
216
+ const animations = {
217
+ fall,
218
+ swing,
219
+ rotate,
220
+ flutter,
221
+ spiral,
222
+ tumble,
223
+ zigzag,
224
+ float
225
+ };
226
+ /**
227
+ * Get animation function by name
228
+ */
229
+ function getAnimation(name) {
230
+ var _a;
231
+ return (_a = animations[name]) !== null && _a !== void 0 ? _a : fall;
232
+ }
233
+
234
+ /**
235
+ * Particle class - manages individual falling objects
236
+ */
237
+ /**
238
+ * Speed multiplier to convert user-friendly speed units to px/ms
239
+ * speed = 1 means ~40 pixels per second (~1cm/s at 96 DPI)
240
+ * speed = 0.1 means ~4 pixels per second (~1mm/s)
241
+ * speed = 0 means standing still
242
+ */
243
+ const SPEED_MULTIPLIER = 0.04; // 40 px/s per unit, divided by 1000 for ms
244
+ class Particle {
245
+ constructor(options) {
246
+ this.options = options;
247
+ this.id = generateId();
248
+ this.age = 0;
249
+ this.phase = Math.random() * Math.PI * 2;
250
+ this.data = {};
251
+ // Get container dimensions
252
+ this.containerWidth = options.container.clientWidth;
253
+ this.containerHeight = options.container.clientHeight;
254
+ // Pick random object
255
+ const object = weightedRandomPick(options.objects);
256
+ // Initialize position at top with random x
257
+ this.x = randomRange(-50, this.containerWidth + 50);
258
+ this.y = -50;
259
+ // Initialize velocity (apply speed multiplier for intuitive units)
260
+ // speed=1 → ~40px/s (1cm/s), speed=0.1 → ~4px/s (1mm/s)
261
+ this.vy = randomFromRange(options.speed) * SPEED_MULTIPLIER;
262
+ this.vx = options.wind * SPEED_MULTIPLIER * randomRange(0.5, 1.5);
263
+ // Initialize size and opacity
264
+ this.size = randomFromRange(options.size);
265
+ this.opacity = randomFromRange(options.opacity);
266
+ // Initialize rotation
267
+ this.rotation = randomRange(0, 360);
268
+ this.rotationSpeed = randomRange(-3, 3);
269
+ // Pick animation
270
+ this.animation = randomPick(options.animation);
271
+ // Initialize animation-specific data
272
+ this.initAnimationData();
273
+ // Create DOM element
274
+ this.element = this.createElement(object);
275
+ }
276
+ /**
277
+ * Initialize animation-specific data
278
+ */
279
+ initAnimationData() {
280
+ if (this.animation === 'tumble') {
281
+ this.data.tumbleSpeed = randomRange(3, 8);
282
+ this.data.wobbleX = randomRange(0.01, 0.03);
283
+ this.data.wobbleY = randomRange(0.005, 0.015);
284
+ }
285
+ }
286
+ /**
287
+ * Create DOM element for the particle
288
+ */
289
+ createElement(object) {
290
+ var _a, _b, _c, _d;
291
+ const element = document.createElement('div');
292
+ element.className = 'falling-particle';
293
+ element.setAttribute('data-particle-id', String(this.id));
294
+ // Set content based on type
295
+ switch (object.type) {
296
+ case 'emoji':
297
+ element.textContent = (_a = object.content) !== null && _a !== void 0 ? _a : '❄️';
298
+ element.style.fontSize = `${this.size}px`;
299
+ element.style.lineHeight = '1';
300
+ break;
301
+ case 'image':
302
+ const img = document.createElement('img');
303
+ img.src = (_c = (_b = object.src) !== null && _b !== void 0 ? _b : object.content) !== null && _c !== void 0 ? _c : '';
304
+ img.alt = '';
305
+ img.style.width = `${this.size}px`;
306
+ img.style.height = `${this.size}px`;
307
+ img.style.objectFit = 'contain';
308
+ img.draggable = false;
309
+ element.appendChild(img);
310
+ break;
311
+ case 'html':
312
+ element.innerHTML = (_d = object.content) !== null && _d !== void 0 ? _d : '';
313
+ break;
314
+ }
315
+ // Apply base styles
316
+ Object.assign(element.style, {
317
+ position: 'absolute',
318
+ pointerEvents: 'none',
319
+ userSelect: 'none',
320
+ willChange: 'transform, opacity',
321
+ opacity: String(this.opacity),
322
+ left: '0',
323
+ top: '0',
324
+ transform: this.getTransform()
325
+ });
326
+ return element;
327
+ }
328
+ /**
329
+ * Get CSS transform string
330
+ */
331
+ getTransform() {
332
+ return `translate3d(${this.x}px, ${this.y}px, 0) rotate(${this.rotation}deg)`;
333
+ }
334
+ /**
335
+ * Update particle state
336
+ */
337
+ update(deltaTime, elapsed) {
338
+ this.age += deltaTime;
339
+ // Apply animation
340
+ const animationFn = getAnimation(this.animation);
341
+ animationFn(this, deltaTime, elapsed);
342
+ // Update DOM
343
+ this.element.style.transform = this.getTransform();
344
+ }
345
+ /**
346
+ * Check if particle is out of bounds
347
+ */
348
+ isOutOfBounds() {
349
+ return (this.y > this.containerHeight + 100 ||
350
+ this.x < -100 ||
351
+ this.x > this.containerWidth + 100);
352
+ }
353
+ /**
354
+ * Update container dimensions (for resize handling)
355
+ */
356
+ updateContainerSize(width, height) {
357
+ this.containerWidth = width;
358
+ this.containerHeight = height;
359
+ }
360
+ /**
361
+ * Remove particle from DOM
362
+ */
363
+ destroy() {
364
+ this.element.remove();
365
+ }
366
+ }
367
+
368
+ /**
369
+ * FallingAnimation - Main class for creating falling object animations
370
+ */
371
+ /** Default configuration values */
372
+ const DEFAULTS = {
373
+ speed: { min: 2, max: 5 },
374
+ spawnRate: 3,
375
+ maxParticles: 50,
376
+ animation: ['fall'],
377
+ size: { min: 20, max: 40 },
378
+ opacity: { min: 0.6, max: 1 },
379
+ wind: 0,
380
+ autoStart: true,
381
+ zIndex: 9999,
382
+ responsive: true
383
+ };
384
+ class FallingAnimation {
385
+ constructor(options) {
386
+ this.particles = [];
387
+ this.wrapper = null;
388
+ this.isRunning = false;
389
+ this.isPaused = false;
390
+ this.animationId = null;
391
+ this.lastSpawnTime = 0;
392
+ this.startTime = 0;
393
+ this.lastFrameTime = 0;
394
+ this.resizeHandler = null;
395
+ /**
396
+ * Main animation loop
397
+ */
398
+ this.animate = (currentTime) => {
399
+ if (!this.isRunning)
400
+ return;
401
+ // Calculate delta time
402
+ if (this.lastFrameTime === 0) {
403
+ this.lastFrameTime = currentTime;
404
+ this.startTime = currentTime;
405
+ }
406
+ const deltaTime = Math.min(currentTime - this.lastFrameTime, 50); // Cap at 50ms
407
+ const elapsed = currentTime - this.startTime;
408
+ this.lastFrameTime = currentTime;
409
+ // Spawn new particles based on rate
410
+ const spawnInterval = 1000 / this.options.spawnRate;
411
+ if (currentTime - this.lastSpawnTime >= spawnInterval) {
412
+ this.spawnParticle();
413
+ this.lastSpawnTime = currentTime;
414
+ }
415
+ // Update particles
416
+ const particlesToRemove = [];
417
+ for (const particle of this.particles) {
418
+ particle.update(deltaTime, elapsed);
419
+ if (particle.isOutOfBounds()) {
420
+ particlesToRemove.push(particle);
421
+ }
422
+ }
423
+ // Remove out-of-bounds particles
424
+ particlesToRemove.forEach(p => this.removeParticle(p));
425
+ // Continue animation loop
426
+ this.animationId = requestAnimationFrame(this.animate);
427
+ };
428
+ // Validate required options
429
+ if (!options.objects || options.objects.length === 0) {
430
+ throw new Error('[falling-animation] "objects" option is required and must not be empty');
431
+ }
432
+ // Resolve options with defaults
433
+ this.options = this.resolveOptions(options);
434
+ // Create wrapper element
435
+ this.createWrapper();
436
+ // Setup resize handler if responsive
437
+ if (this.options.responsive) {
438
+ this.setupResizeHandler();
439
+ }
440
+ // Auto start if enabled
441
+ if (this.options.autoStart) {
442
+ this.start();
443
+ }
444
+ }
445
+ /**
446
+ * Merge user options with defaults
447
+ */
448
+ resolveOptions(options) {
449
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j;
450
+ const container = resolveContainer(options.container);
451
+ // Normalize animation to array
452
+ let animation;
453
+ if (!options.animation) {
454
+ animation = DEFAULTS.animation;
455
+ }
456
+ else if (typeof options.animation === 'string') {
457
+ animation = [options.animation];
458
+ }
459
+ else {
460
+ animation = options.animation;
461
+ }
462
+ return {
463
+ container,
464
+ objects: options.objects,
465
+ speed: (_a = options.speed) !== null && _a !== void 0 ? _a : DEFAULTS.speed,
466
+ spawnRate: (_b = options.spawnRate) !== null && _b !== void 0 ? _b : DEFAULTS.spawnRate,
467
+ maxParticles: (_c = options.maxParticles) !== null && _c !== void 0 ? _c : DEFAULTS.maxParticles,
468
+ animation,
469
+ size: (_d = options.size) !== null && _d !== void 0 ? _d : DEFAULTS.size,
470
+ opacity: (_e = options.opacity) !== null && _e !== void 0 ? _e : DEFAULTS.opacity,
471
+ wind: (_f = options.wind) !== null && _f !== void 0 ? _f : DEFAULTS.wind,
472
+ autoStart: (_g = options.autoStart) !== null && _g !== void 0 ? _g : DEFAULTS.autoStart,
473
+ zIndex: (_h = options.zIndex) !== null && _h !== void 0 ? _h : DEFAULTS.zIndex,
474
+ responsive: (_j = options.responsive) !== null && _j !== void 0 ? _j : DEFAULTS.responsive
475
+ };
476
+ }
477
+ /**
478
+ * Create wrapper element for particles
479
+ */
480
+ createWrapper() {
481
+ this.wrapper = document.createElement('div');
482
+ this.wrapper.className = 'falling-animation-wrapper';
483
+ // Apply styles
484
+ Object.assign(this.wrapper.style, {
485
+ position: this.options.container === document.body ? 'fixed' : 'absolute',
486
+ top: '0',
487
+ left: '0',
488
+ width: '100%',
489
+ height: '100%',
490
+ overflow: 'hidden',
491
+ pointerEvents: 'none',
492
+ zIndex: String(this.options.zIndex)
493
+ });
494
+ // Ensure container has position for absolute wrapper
495
+ if (this.options.container !== document.body) {
496
+ const containerPosition = getComputedStyle(this.options.container).position;
497
+ if (containerPosition === 'static') {
498
+ this.options.container.style.position = 'relative';
499
+ }
500
+ }
501
+ this.options.container.appendChild(this.wrapper);
502
+ }
503
+ /**
504
+ * Setup resize handler for responsive behavior
505
+ */
506
+ setupResizeHandler() {
507
+ this.resizeHandler = throttle(() => {
508
+ const width = this.options.container.clientWidth;
509
+ const height = this.options.container.clientHeight;
510
+ this.particles.forEach(particle => {
511
+ particle.updateContainerSize(width, height);
512
+ });
513
+ }, 200);
514
+ window.addEventListener('resize', this.resizeHandler);
515
+ }
516
+ /**
517
+ * Spawn a new particle
518
+ */
519
+ spawnParticle() {
520
+ if (this.particles.length >= this.options.maxParticles) {
521
+ return;
522
+ }
523
+ const particle = new Particle(this.options);
524
+ this.particles.push(particle);
525
+ if (this.wrapper) {
526
+ this.wrapper.appendChild(particle.element);
527
+ }
528
+ }
529
+ /**
530
+ * Remove a particle
531
+ */
532
+ removeParticle(particle) {
533
+ const index = this.particles.indexOf(particle);
534
+ if (index > -1) {
535
+ this.particles.splice(index, 1);
536
+ particle.destroy();
537
+ }
538
+ }
539
+ /**
540
+ * Start the animation
541
+ */
542
+ start() {
543
+ if (this.isRunning)
544
+ return;
545
+ this.isRunning = true;
546
+ this.isPaused = false;
547
+ this.lastFrameTime = 0;
548
+ this.lastSpawnTime = 0;
549
+ this.animationId = requestAnimationFrame(this.animate);
550
+ }
551
+ /**
552
+ * Stop the animation and clear all particles
553
+ */
554
+ stop() {
555
+ this.isRunning = false;
556
+ this.isPaused = false;
557
+ if (this.animationId !== null) {
558
+ cancelAnimationFrame(this.animationId);
559
+ this.animationId = null;
560
+ }
561
+ // Clear all particles
562
+ this.particles.forEach(p => p.destroy());
563
+ this.particles = [];
564
+ }
565
+ /**
566
+ * Pause the animation (keeps particles in place)
567
+ */
568
+ pause() {
569
+ if (!this.isRunning || this.isPaused)
570
+ return;
571
+ this.isPaused = true;
572
+ this.isRunning = false;
573
+ if (this.animationId !== null) {
574
+ cancelAnimationFrame(this.animationId);
575
+ this.animationId = null;
576
+ }
577
+ }
578
+ /**
579
+ * Resume a paused animation
580
+ */
581
+ resume() {
582
+ if (this.isRunning || !this.isPaused)
583
+ return;
584
+ this.isRunning = true;
585
+ this.isPaused = false;
586
+ this.lastFrameTime = 0;
587
+ this.animationId = requestAnimationFrame(this.animate);
588
+ }
589
+ /**
590
+ * Update options dynamically
591
+ */
592
+ setOptions(newOptions) {
593
+ // Merge new options
594
+ if (newOptions.speed) {
595
+ this.options.speed = newOptions.speed;
596
+ }
597
+ if (newOptions.spawnRate !== undefined) {
598
+ this.options.spawnRate = newOptions.spawnRate;
599
+ }
600
+ if (newOptions.maxParticles !== undefined) {
601
+ this.options.maxParticles = newOptions.maxParticles;
602
+ }
603
+ if (newOptions.animation) {
604
+ this.options.animation = typeof newOptions.animation === 'string'
605
+ ? [newOptions.animation]
606
+ : newOptions.animation;
607
+ }
608
+ if (newOptions.size) {
609
+ this.options.size = newOptions.size;
610
+ }
611
+ if (newOptions.opacity) {
612
+ this.options.opacity = newOptions.opacity;
613
+ }
614
+ if (newOptions.wind !== undefined) {
615
+ this.options.wind = newOptions.wind;
616
+ }
617
+ if (newOptions.objects) {
618
+ this.options.objects = newOptions.objects;
619
+ }
620
+ }
621
+ /**
622
+ * Get current particle count
623
+ */
624
+ getParticleCount() {
625
+ return this.particles.length;
626
+ }
627
+ /**
628
+ * Check if animation is running
629
+ */
630
+ getIsRunning() {
631
+ return this.isRunning;
632
+ }
633
+ /**
634
+ * Check if animation is paused
635
+ */
636
+ getIsPaused() {
637
+ return this.isPaused;
638
+ }
639
+ /**
640
+ * Destroy the animation and clean up
641
+ */
642
+ destroy() {
643
+ this.stop();
644
+ // Remove wrapper
645
+ if (this.wrapper) {
646
+ this.wrapper.remove();
647
+ this.wrapper = null;
648
+ }
649
+ // Remove resize handler
650
+ if (this.resizeHandler) {
651
+ window.removeEventListener('resize', this.resizeHandler);
652
+ this.resizeHandler = null;
653
+ }
654
+ }
655
+ }
656
+
657
+ /**
658
+ * falling-animation
659
+ * A lightweight, customizable falling objects animation library
660
+ *
661
+ * @author phongdh
662
+ * @license MIT
663
+ */
664
+ // Main class
665
+
666
+ exports.FallingAnimation = FallingAnimation;
667
+ exports.animations = animations;
668
+ exports.default = FallingAnimation;
669
+ exports.getAnimation = getAnimation;
670
+ //# sourceMappingURL=falling-animation.cjs.js.map