partycles 0.4.0 → 1.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.
package/dist/index.esm.js CHANGED
@@ -1,4 +1,4 @@
1
- import React, { useState, useRef, useCallback, useEffect } from 'react';
1
+ import React, { useState, useRef, useEffect, useCallback } from 'react';
2
2
  import { createRoot } from 'react-dom/client';
3
3
 
4
4
  const randomInRange = (min, max) => {
@@ -26,9 +26,9 @@ const createParticleStyle = (particle, containerRect) => {
26
26
  };
27
27
  };
28
28
 
29
- const defaultColors$f = ['#f44336', '#e91e63', '#9c27b0', '#673ab7', '#3f51b5', '#2196f3', '#03a9f4', '#00bcd4', '#009688', '#4caf50', '#8bc34a', '#cddc39', '#ffeb3b', '#ffc107', '#ff9800', '#ff5722'];
29
+ const defaultColors$3 = ['#f44336', '#e91e63', '#9c27b0', '#673ab7', '#3f51b5', '#2196f3', '#03a9f4', '#00bcd4', '#009688', '#4caf50', '#8bc34a', '#cddc39', '#ffeb3b', '#ffc107', '#ff9800', '#ff5722'];
30
30
  const createConfettiParticles = (origin, config) => {
31
- const { particleCount = 50, startVelocity = 20, colors = defaultColors$f, elementSize = 20 } = config;
31
+ const { particleCount = 50, startVelocity = 20, colors = defaultColors$3, elementSize = 20 } = config;
32
32
  const particles = [];
33
33
  for (let i = 0; i < particleCount; i++) {
34
34
  const angle = randomInRange(0, 360);
@@ -359,61 +359,6 @@ const renderCoinParticle = (particle) => {
359
359
  } }, "$")));
360
360
  };
361
361
 
362
- const lightningColors = ['#FFFF00', '#FFFFFF', '#00FFFF', '#FF00FF'];
363
- const createLightningParticles = (origin, config) => {
364
- const { particleCount = 20, spread = 360, startVelocity = 50, colors = lightningColors, elementSize = 30 } = config;
365
- const particles = [];
366
- for (let i = 0; i < particleCount; i++) {
367
- const angle = randomInRange(0, spread) * (Math.PI / 180);
368
- const velocity = randomInRange(startVelocity * 0.7, startVelocity * 1.3);
369
- particles.push({
370
- id: generateId(),
371
- x: origin.x,
372
- y: origin.y,
373
- vx: Math.sin(angle) * velocity,
374
- vy: -Math.abs(Math.cos(angle) * velocity * 0.5), // Mostly horizontal movement
375
- life: config.lifetime || 60, // Quick flashes
376
- opacity: 1,
377
- size: randomInRange(elementSize * 0.5, elementSize * 1.5),
378
- rotation: randomInRange(0, 360),
379
- color: colors[Math.floor(Math.random() * colors.length)] || colors[0] || '#ffffff',
380
- });
381
- }
382
- return particles;
383
- };
384
- const renderLightningParticle = (particle) => {
385
- // Create zigzag pattern based on particle life
386
- const zigzagOffset = Math.sin(particle.life * 0.5) * 10;
387
- return (React.createElement("div", { key: particle.id, style: {
388
- width: `${particle.size}px`,
389
- height: `${particle.size * 0.3}px`,
390
- position: 'relative',
391
- filter: `blur(0.5px) brightness(2)`,
392
- transform: `translateX(${zigzagOffset}px) rotate(${particle.rotation}deg)`,
393
- } },
394
- React.createElement("div", { style: {
395
- position: 'absolute',
396
- width: '100%',
397
- height: '100%',
398
- background: particle.color,
399
- clipPath: 'polygon(0% 0%, 60% 0%, 40% 45%, 100% 45%, 0% 100%, 40% 55%, 20% 55%, 35% 100%)',
400
- boxShadow: `
401
- 0 0 10px ${particle.color},
402
- 0 0 20px ${particle.color},
403
- 0 0 30px ${particle.color}
404
- `,
405
- } }),
406
- React.createElement("div", { style: {
407
- position: 'absolute',
408
- top: '-50%',
409
- left: '-50%',
410
- width: '200%',
411
- height: '200%',
412
- background: `radial-gradient(circle, ${particle.color}88 0%, transparent 70%)`,
413
- animation: `electricPulse ${randomInRange(100, 200)}ms infinite`,
414
- } })));
415
- };
416
-
417
362
  const petalColors = ['#FFB6C1', '#FFC0CB', '#FF69B4', '#FF1493', '#FFF0F5'];
418
363
  const createPetalParticles = (origin, config) => {
419
364
  const { particleCount = 40, spread = 100, startVelocity = 8, colors = petalColors, elementSize = 20 } = config;
@@ -679,84 +624,6 @@ const renderPaintParticle = (particle) => {
679
624
  } }))));
680
625
  };
681
626
 
682
- const musicColors = ['#FF006E', '#8338EC', '#3A86FF', '#FB5607', '#FFBE0B'];
683
- const createMusicParticles = (origin, config) => {
684
- const { particleCount = 20, spread = 100, startVelocity = 8, colors = musicColors, elementSize = 25 } = config;
685
- const particles = [];
686
- const notes = ['♪', '♫', '♬', '♩', '♭', '♯'];
687
- for (let i = 0; i < particleCount; i++) {
688
- const angle = randomInRange(-spread / 2, spread / 2) * (Math.PI / 180);
689
- const velocity = randomInRange(startVelocity * 0.5, startVelocity);
690
- particles.push({
691
- id: generateId(),
692
- x: origin.x + randomInRange(-20, 20),
693
- y: origin.y,
694
- vx: Math.sin(angle) * velocity * 0.3,
695
- vy: -Math.abs(Math.cos(angle)) * velocity * 0.5, // Always go up slowly
696
- life: config.lifetime || 200,
697
- opacity: 1,
698
- size: randomInRange(elementSize * 0.8, elementSize * 1.2),
699
- rotation: i % notes.length, // Store which note to use
700
- color: colors[Math.floor(Math.random() * colors.length)] || colors[0] || '#ffffff',
701
- });
702
- }
703
- return particles;
704
- };
705
- const renderMusicParticle = (particle) => {
706
- const notes = ['♪', '♫', '♬', '♩', '♭', '♯'];
707
- const note = notes[Math.floor(particle.rotation)];
708
- // Wave motion as notes float up
709
- const waveX = Math.sin(particle.life * 0.05) * 20;
710
- const wobble = Math.sin(particle.life * 0.1) * 10;
711
- // Simple fade based on particle life
712
- // Assume default lifetime of 300 if not set
713
- const totalLife = 300;
714
- const age = totalLife - particle.life; // How old the particle is
715
- // Fade in during first 20 frames
716
- const fadeIn = age < 20 ? age / 20 : 1;
717
- // Fade out during last 50 frames
718
- const fadeOut = particle.life < 50 ? particle.life / 50 : 1;
719
- const opacity = Math.min(fadeIn, fadeOut) * particle.opacity;
720
- return (React.createElement("div", { key: particle.id, style: {
721
- fontSize: `${particle.size}px`,
722
- fontWeight: 'bold',
723
- position: 'relative',
724
- transform: `
725
- translateX(${waveX}px)
726
- rotate(${wobble}deg)
727
- scale(${0.8 + opacity * 0.2})
728
- `,
729
- color: particle.color,
730
- opacity,
731
- textShadow: `
732
- 0 0 10px ${particle.color}88,
733
- 0 0 20px ${particle.color}44,
734
- 2px 2px 3px rgba(0,0,0,0.3)
735
- `,
736
- transition: 'transform 0.3s ease',
737
- userSelect: 'none',
738
- } },
739
- note,
740
- React.createElement("div", { style: {
741
- position: 'absolute',
742
- top: '40%',
743
- left: '-20%',
744
- width: '140%',
745
- height: '1px',
746
- background: `linear-gradient(90deg, transparent, ${particle.color}33, transparent)`,
747
- opacity: opacity * 0.5,
748
- } }),
749
- React.createElement("div", { style: {
750
- position: 'absolute',
751
- top: '60%',
752
- left: '-20%',
753
- width: '140%',
754
- height: '1px',
755
- background: `linear-gradient(90deg, transparent, ${particle.color}33, transparent)`,
756
- opacity: opacity * 0.3,
757
- } })));
758
- };
759
-
760
627
  const balloonColors = ['#FF006E', '#FB5607', '#FFBE0B', '#8338EC', '#3A86FF', '#06FFB4', '#FF4081'];
761
628
  const createBalloonParticles = (origin, config) => {
762
629
  const { particleCount = 15, spread = 80, startVelocity = 10, colors = balloonColors, elementSize = 35 } = config;
@@ -938,88 +805,6 @@ const renderGalaxyParticle = (particle) => {
938
805
  } }))));
939
806
  };
940
807
 
941
- const matrixChars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZアイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワン';
942
- const createMatrixParticles = (origin, config) => {
943
- const { particleCount = 50, elementSize = 300 } = config;
944
- const particles = [];
945
- const columns = Math.floor(elementSize / 20);
946
- for (let i = 0; i < columns; i++) {
947
- const columnX = origin.x - elementSize / 2 + (i / columns) * elementSize;
948
- const columnParticles = Math.floor(Math.random() * 15) + 10;
949
- for (let j = 0; j < columnParticles; j++) {
950
- const charIndex = Math.floor(Math.random() * matrixChars.length);
951
- const speed = randomInRange(80, 120);
952
- particles.push({
953
- id: generateId(),
954
- x: columnX + randomInRange(-10, 10),
955
- y: origin.y - elementSize / 2 - j * 25 - randomInRange(0, 100),
956
- vx: columnParticles, // Store columnHeight in vx (since we don't use horizontal movement)
957
- vy: speed,
958
- life: config.lifetime || 150,
959
- opacity: 1,
960
- size: 20 + (j * 0.1), // Store fadeDelay as decimal part
961
- rotation: charIndex + (columnParticles * 1000), // Store charIndex and columnHeight
962
- color: '#00ff00',
963
- });
964
- }
965
- }
966
- return particles.slice(0, particleCount);
967
- };
968
- const renderMatrixParticle = (particle) => {
969
- // Extract stored values
970
- const charIndex = Math.floor(particle.rotation % 1000);
971
- const columnHeight = Math.floor(particle.rotation / 1000);
972
- const fadeDelay = (particle.size - Math.floor(particle.size));
973
- const character = matrixChars[charIndex] || matrixChars[0];
974
- const brightness = 1 - (fadeDelay / columnHeight) * 0.5;
975
- return (React.createElement("div", { key: particle.id, style: {
976
- fontSize: '20px',
977
- fontFamily: 'monospace',
978
- fontWeight: 'bold',
979
- color: `rgba(${0}, ${255 * brightness}, ${0}, ${particle.opacity})`,
980
- textShadow: `0 0 10px rgba(0, ${255 * brightness}, 0, ${particle.opacity * 0.8})`,
981
- userSelect: 'none',
982
- } }, character));
983
- };
984
-
985
- const pixelColors = ['#FF0000', '#00FF00', '#0000FF', '#FFFF00', '#FF00FF', '#00FFFF', '#FFA500', '#FF1493'];
986
- const createPixelParticles = (origin, config) => {
987
- const { particleCount = 30, colors = pixelColors, elementSize = 20 } = config;
988
- const particles = [];
989
- const gridSize = Math.floor(Math.sqrt(particleCount));
990
- const spacing = 30;
991
- for (let i = 0; i < gridSize; i++) {
992
- for (let j = 0; j < gridSize; j++) {
993
- const targetX = origin.x + (i - gridSize / 2) * spacing;
994
- const targetY = origin.y + (j - gridSize / 2) * spacing;
995
- const angle = randomInRange(0, 360);
996
- const velocity = randomInRange(150, 300);
997
- particles.push({
998
- id: generateId(),
999
- x: targetX,
1000
- y: targetY,
1001
- vx: Math.cos(degreesToRadians(angle)) * velocity,
1002
- vy: Math.sin(degreesToRadians(angle)) * velocity,
1003
- life: config.lifetime || 150,
1004
- opacity: 1,
1005
- size: randomInRange(elementSize * 0.6, elementSize),
1006
- // Store targetX offset in rotation (from -500 to 500, add 1000 to make positive)
1007
- rotation: ((targetX - origin.x) + 500) + ((targetY - origin.y) + 500) * 1000,
1008
- color: getRandomColor(colors),
1009
- });
1010
- }
1011
- }
1012
- return particles.slice(0, particleCount);
1013
- };
1014
- const renderPixelParticle = (particle) => {
1015
- return (React.createElement("div", { key: particle.id, style: {
1016
- width: `${particle.size}px`,
1017
- height: `${particle.size}px`,
1018
- backgroundColor: particle.color,
1019
- boxShadow: `0 0 ${particle.size / 2}px ${particle.color}`,
1020
- } }));
1021
- };
1022
-
1023
808
  const createGlitchParticles = (origin, config) => {
1024
809
  const { particleCount = 20, elementSize = 200 } = config;
1025
810
  const particles = [];
@@ -1082,119 +867,9 @@ const renderGlitchParticle = (particle) => {
1082
867
  } }));
1083
868
  };
1084
869
 
1085
- const getDiceFace = (value) => {
1086
- const faces = ['⚀', '⚁', '⚂', '⚃', '⚄', '⚅'];
1087
- return faces[value - 1] || '⚀';
1088
- };
1089
- const createDiceParticles = (origin, config) => {
1090
- const { particleCount = 8, startVelocity = 25, elementSize = 40 } = config;
1091
- const particles = [];
1092
- for (let i = 0; i < particleCount; i++) {
1093
- const angle = randomInRange(0, 360);
1094
- const velocity = randomInRange(startVelocity * 0.5, startVelocity);
1095
- const diceValue = Math.floor(randomInRange(1, 7));
1096
- particles.push({
1097
- id: generateId(),
1098
- x: origin.x,
1099
- y: origin.y,
1100
- vx: Math.cos(degreesToRadians(angle)) * velocity,
1101
- vy: Math.sin(degreesToRadians(angle)) * velocity - 30,
1102
- life: config.lifetime || 150,
1103
- opacity: 1,
1104
- size: randomInRange(elementSize * 0.8, elementSize * 1.2),
1105
- // Store dice value (1-6) in lower bits, and rotation angles in upper bits
1106
- rotation: diceValue + (Math.floor(randomInRange(0, 360)) * 10),
1107
- color: '#ffffff',
1108
- });
1109
- }
1110
- return particles;
1111
- };
1112
- const renderDiceParticle = (particle) => {
1113
- const diceValue = particle.rotation % 10;
1114
- const rotationAngle = Math.floor(particle.rotation / 10);
1115
- return (React.createElement("div", { key: particle.id, style: {
1116
- width: `${particle.size}px`,
1117
- height: `${particle.size}px`,
1118
- fontSize: `${particle.size * 0.8}px`,
1119
- display: 'flex',
1120
- alignItems: 'center',
1121
- justifyContent: 'center',
1122
- backgroundColor: '#fff',
1123
- color: '#000',
1124
- borderRadius: `${particle.size * 0.2}px`,
1125
- transform: `rotate(${rotationAngle}deg)`,
1126
- transformStyle: 'preserve-3d',
1127
- boxShadow: '0 4px 8px rgba(0,0,0,0.3), inset 0 1px 0 rgba(255,255,255,0.5)',
1128
- border: '2px solid #333',
1129
- fontWeight: 'bold',
1130
- } }, getDiceFace(diceValue)));
1131
- };
1132
-
1133
- const levelUpTexts = ['+1', '+10', '+100', 'XP', '+XP', 'LEVEL UP!', 'CRITICAL!', 'COMBO!', 'PERFECT!'];
1134
- const levelUpColors = [
1135
- { text: '#FFD700', glow: '#FFA500' }, // Gold
1136
- { text: '#00FF00', glow: '#00AA00' }, // Green
1137
- { text: '#00FFFF', glow: '#0088FF' }, // Cyan
1138
- { text: '#FF00FF', glow: '#AA00AA' }, // Magenta
1139
- { text: '#FFFF00', glow: '#FFAA00' }, // Yellow
1140
- ];
1141
- const createLevelUpParticles = (origin, config) => {
1142
- const { particleCount = 10, startVelocity = 20, elementSize = 30 } = config;
1143
- const particles = [];
1144
- for (let i = 0; i < particleCount; i++) {
1145
- const angle = randomInRange(-45, 45);
1146
- const velocity = randomInRange(startVelocity * 0.5, startVelocity * 1.5);
1147
- const colorSetIndex = Math.floor(Math.random() * levelUpColors.length);
1148
- const colorSet = levelUpColors[colorSetIndex] || levelUpColors[0];
1149
- const textIndex = Math.floor(Math.random() * levelUpTexts.length);
1150
- const text = levelUpTexts[textIndex];
1151
- const isMainText = text === 'LEVEL UP!' || text === 'CRITICAL!';
1152
- const fontSize = isMainText ? randomInRange(elementSize * 1.3, elementSize * 2) : randomInRange(elementSize * 0.7, elementSize * 1.2);
1153
- particles.push({
1154
- id: generateId(),
1155
- x: origin.x,
1156
- y: origin.y,
1157
- vx: Math.sin(degreesToRadians(angle)) * velocity,
1158
- vy: -Math.cos(degreesToRadians(angle)) * velocity - 10,
1159
- life: config.lifetime || 150,
1160
- opacity: 1,
1161
- size: fontSize, // Store fontSize in size
1162
- // Store textIndex (0-8) + colorSetIndex (0-4) * 10 + delay (0-50) * 100
1163
- rotation: textIndex + (colorSetIndex * 10) + (Math.floor(i * 5) * 100),
1164
- color: colorSet.text,
1165
- });
1166
- }
1167
- return particles;
1168
- };
1169
- const renderLevelUpParticle = (particle) => {
1170
- // Extract encoded values
1171
- const textIndex = particle.rotation % 10;
1172
- const colorSetIndex = Math.floor((particle.rotation % 100) / 10);
1173
- const delay = Math.floor(particle.rotation / 100) / 100;
1174
- const text = levelUpTexts[textIndex] || levelUpTexts[0];
1175
- const colorSet = levelUpColors[colorSetIndex] || levelUpColors[0];
1176
- return (React.createElement("div", { key: particle.id, style: {
1177
- fontSize: `${particle.size}px`,
1178
- fontWeight: 'bold',
1179
- fontFamily: 'Arial, sans-serif',
1180
- color: particle.color,
1181
- textShadow: `
1182
- 0 0 10px ${colorSet.glow},
1183
- 0 0 20px ${colorSet.glow},
1184
- 0 0 30px ${particle.color},
1185
- 0 2px 4px rgba(0,0,0,0.5)
1186
- `,
1187
- letterSpacing: '2px',
1188
- textTransform: 'uppercase',
1189
- whiteSpace: 'nowrap',
1190
- userSelect: 'none',
1191
- animationDelay: `${delay}s`,
1192
- } }, text));
1193
- };
1194
-
1195
- const defaultColors$e = ['#9C27B0', '#E91E63', '#FF00FF', '#00FFFF', '#FFD700', '#FF69B4', '#DA70D6', '#9370DB'];
870
+ const defaultColors$2 = ['#9C27B0', '#E91E63', '#FF00FF', '#00FFFF', '#FFD700', '#FF69B4', '#DA70D6', '#9370DB'];
1196
871
  const createMagicDustParticles = (origin, config) => {
1197
- const { particleCount = 40, startVelocity = 8, colors = defaultColors$e, elementSize = 12 } = config;
872
+ const { particleCount = 40, startVelocity = 8, colors = defaultColors$2, elementSize = 12 } = config;
1198
873
  const particles = [];
1199
874
  for (let i = 0; i < particleCount; i++) {
1200
875
  // Create particles in a circular pattern with some randomness
@@ -1265,97 +940,9 @@ const renderMagicDustParticle = (particle) => {
1265
940
  } }))));
1266
941
  };
1267
942
 
1268
- const defaultColors$d = ['#FFFFFF', '#F0F0F0', '#E8E8E8', '#F5F5F5', '#FAFAFA'];
1269
- const createGhostParticles = (origin, config) => {
1270
- const { particleCount = 8, startVelocity = 5, colors = defaultColors$d, elementSize = 30 } = config;
1271
- const particles = [];
1272
- for (let i = 0; i < particleCount; i++) {
1273
- // Ghosts float upward with slight horizontal movement
1274
- const angle = randomInRange(250, 290); // Mostly upward
1275
- const velocity = randomInRange(startVelocity * 0.5, startVelocity);
1276
- const color = getRandomColor(colors);
1277
- particles.push({
1278
- id: generateId(),
1279
- x: origin.x + randomInRange(-50, 50),
1280
- y: origin.y + randomInRange(-20, 20),
1281
- vx: Math.cos(degreesToRadians(angle)) * velocity,
1282
- vy: Math.sin(degreesToRadians(angle)) * velocity,
1283
- life: config.lifetime || 200,
1284
- opacity: randomInRange(0.5, 0.8),
1285
- size: randomInRange(elementSize * 0.8, elementSize * 1.2),
1286
- rotation: randomInRange(-15, 15),
1287
- color,
1288
- });
1289
- }
1290
- return particles;
1291
- };
1292
- const renderGhostParticle = (particle) => {
1293
- // Calculate wave effect for tail
1294
- const waveOffset = Math.sin(Date.now() * 0.003 + particle.x * 0.01) * 3;
1295
- return (React.createElement("div", { key: particle.id, style: {
1296
- width: `${particle.size}px`,
1297
- height: `${particle.size * 1.4}px`,
1298
- position: 'relative',
1299
- transform: `rotate(${particle.rotation}deg)`,
1300
- } },
1301
- React.createElement("div", { style: {
1302
- position: 'absolute',
1303
- width: '100%',
1304
- height: '70%',
1305
- backgroundColor: particle.color,
1306
- borderRadius: '50% 50% 0 0',
1307
- boxShadow: `0 0 20px rgba(255, 255, 255, 0.5), inset 0 0 10px rgba(0, 0, 0, 0.1)`,
1308
- } }),
1309
- React.createElement("div", { style: {
1310
- position: 'absolute',
1311
- bottom: 0,
1312
- width: '100%',
1313
- height: '40%',
1314
- backgroundColor: particle.color,
1315
- clipPath: `polygon(
1316
- 0 0,
1317
- 100% 0,
1318
- 100% 60%,
1319
- ${75 + waveOffset}% 80%,
1320
- ${50 - waveOffset}% 100%,
1321
- ${25 + waveOffset}% 80%,
1322
- 0 60%
1323
- )`,
1324
- } }),
1325
- React.createElement("div", { style: {
1326
- position: 'absolute',
1327
- top: '30%',
1328
- left: '25%',
1329
- width: '15%',
1330
- height: '15%',
1331
- backgroundColor: '#000',
1332
- borderRadius: '50%',
1333
- } }),
1334
- React.createElement("div", { style: {
1335
- position: 'absolute',
1336
- top: '30%',
1337
- right: '25%',
1338
- width: '15%',
1339
- height: '15%',
1340
- backgroundColor: '#000',
1341
- borderRadius: '50%',
1342
- } }),
1343
- React.createElement("div", { style: {
1344
- position: 'absolute',
1345
- top: '45%',
1346
- left: '50%',
1347
- transform: 'translateX(-50%)',
1348
- width: '25%',
1349
- height: '15%',
1350
- border: '2px solid #000',
1351
- borderTop: 'none',
1352
- borderRadius: '0 0 50% 50%',
1353
- } })));
1354
- };
1355
-
1356
- const defaultColors$c = ['#FF1493', '#00CED1', '#FFD700', '#FF69B4', '#7B68EE', '#00FA9A', '#FF6347', '#4169E1'];
943
+ const defaultColors$1 = ['#FF1493', '#00CED1', '#FFD700', '#FF69B4', '#7B68EE', '#00FA9A', '#FF6347', '#4169E1'];
1357
944
  const createCrystalParticles = (origin, config) => {
1358
- const { particleCount = 15, startVelocity = 15, colors = defaultColors$c, elementSize = 25 } = config;
945
+ const { particleCount = 15, startVelocity = 15, colors = defaultColors$1, elementSize = 25 } = config;
1359
946
  const particles = [];
1360
947
  for (let i = 0; i < particleCount; i++) {
1361
948
  // Crystals explode outward and fall with gravity
@@ -1427,762 +1014,61 @@ const renderCrystalParticle = (particle) => {
1427
1014
  } })));
1428
1015
  };
1429
1016
 
1430
- const defaultColors$b = ['#FF4500', '#DC143C', '#B22222', '#8B0000', '#FF6347', '#FF8C00'];
1431
- const createDragonParticles = (origin, config) => {
1432
- const { particleCount = 6, startVelocity = 12, colors = defaultColors$b, elementSize = 40 } = config;
1017
+ const defaultColors = ['#D2691E', '#CD853F', '#8B4513', '#A0522D', '#FF8C00', '#FF6347'];
1018
+ const createLeafParticles = (origin, config) => {
1019
+ const { particleCount = 10, colors = defaultColors, elementSize = 25 } = config;
1433
1020
  const particles = [];
1434
1021
  for (let i = 0; i < particleCount; i++) {
1435
- // Dragons fly in various directions with swooping motion
1436
- const angle = randomInRange(0, 360);
1437
- const velocity = randomInRange(startVelocity * 0.7, startVelocity);
1438
- const color = getRandomColor(colors);
1439
1022
  particles.push({
1440
1023
  id: generateId(),
1441
- x: origin.x + randomInRange(-30, 30),
1442
- y: origin.y + randomInRange(-30, 30),
1443
- vx: Math.cos(degreesToRadians(angle)) * velocity,
1444
- vy: Math.sin(degreesToRadians(angle)) * velocity,
1445
- life: config.lifetime || 180,
1024
+ x: origin.x + randomInRange(-100, 100),
1025
+ y: origin.y + randomInRange(-50, 0),
1026
+ vx: randomInRange(-1, 1),
1027
+ vy: randomInRange(0.5, 2),
1028
+ life: config.lifetime || 300,
1446
1029
  opacity: 1,
1447
- size: randomInRange(elementSize * 0.8, elementSize),
1448
- rotation: angle,
1449
- color,
1030
+ size: randomInRange(elementSize * 0.6, elementSize),
1031
+ // Encode tumble phase (0-360), sway phase (0-360), and sway amount (20-40) + rotation speed (-3 to 3)
1032
+ rotation: Math.floor(randomInRange(0, 360)) +
1033
+ (Math.floor(randomInRange(0, 360)) * 1000) +
1034
+ (Math.floor(randomInRange(20, 40)) * 1000000) +
1035
+ ((Math.floor(randomInRange(-3, 3)) + 3) * 100000000),
1036
+ color: getRandomColor(colors),
1450
1037
  });
1451
1038
  }
1452
1039
  return particles;
1453
1040
  };
1454
- const renderDragonParticle = (particle) => {
1455
- // Calculate wing flap animation
1456
- const wingFlap = Math.sin(Date.now() * 0.01 + particle.x) * 10;
1457
- // Calculate fire breath pulse
1458
- const firePulse = (Math.sin(Date.now() * 0.005 + particle.y) + 1) / 2;
1041
+ const renderLeafParticle = (particle) => {
1042
+ // Extract encoded values
1043
+ const tumblePhase = particle.rotation % 1000;
1044
+ const swayPhase = Math.floor((particle.rotation % 1000000) / 1000);
1045
+ const swayAmount = Math.floor((particle.rotation % 100000000) / 1000000);
1046
+ const rotationSpeed = (Math.floor(particle.rotation / 100000000) - 3);
1047
+ // Calculate tumbling and swaying
1048
+ const tumble = Math.sin((Date.now() * 0.002 + tumblePhase) * Math.PI / 180) * 30;
1049
+ const swayX = Math.sin((Date.now() * 0.001 + swayPhase) * Math.PI / 180) * swayAmount;
1050
+ const rotation = (Date.now() * rotationSpeed * 0.01 + tumble) % 360;
1459
1051
  return (React.createElement("div", { key: particle.id, style: {
1460
1052
  width: `${particle.size}px`,
1461
1053
  height: `${particle.size}px`,
1462
1054
  position: 'relative',
1463
- transform: `rotate(${particle.rotation}deg)`,
1055
+ transform: `translateX(${swayX}px) rotate(${rotation}deg)`,
1464
1056
  } },
1465
- React.createElement("div", { style: {
1466
- position: 'absolute',
1467
- width: '60%',
1468
- height: '40%',
1469
- top: '30%',
1470
- left: '20%',
1471
- backgroundColor: particle.color,
1472
- borderRadius: '50% 40% 40% 50%',
1473
- boxShadow: `0 2px 10px rgba(0, 0, 0, 0.3)`,
1474
- } }),
1475
- React.createElement("div", { style: {
1057
+ React.createElement("svg", { width: particle.size, height: particle.size, viewBox: `-${particle.size / 2} -${particle.size / 2} ${particle.size} ${particle.size}`, style: {
1476
1058
  position: 'absolute',
1477
- width: '35%',
1478
- height: '35%',
1479
- top: '25%',
1480
- right: '15%',
1481
- backgroundColor: particle.color,
1482
- borderRadius: '40% 50% 50% 40%',
1483
- transform: 'rotate(-10deg)',
1059
+ top: 0,
1060
+ left: 0,
1484
1061
  } },
1485
- React.createElement("div", { style: {
1486
- position: 'absolute',
1487
- width: '20%',
1488
- height: '20%',
1489
- top: '30%',
1490
- right: '25%',
1491
- backgroundColor: '#FFD700',
1492
- borderRadius: '50%',
1493
- boxShadow: '0 0 4px #FFD700',
1494
- } })),
1495
- React.createElement("div", { style: {
1496
- position: 'absolute',
1497
- width: '40%',
1498
- height: '50%',
1499
- top: '15%',
1500
- left: '30%',
1501
- backgroundColor: `${particle.color}CC`,
1502
- clipPath: 'polygon(0% 100%, 50% 0%, 100% 30%, 90% 60%, 100% 90%, 50% 100%)',
1503
- transform: `rotateX(${wingFlap}deg)`,
1504
- transformOrigin: 'bottom center',
1505
- } }),
1506
- React.createElement("div", { style: {
1507
- position: 'absolute',
1508
- width: '40%',
1509
- height: '50%',
1510
- top: '35%',
1511
- left: '30%',
1512
- backgroundColor: `${particle.color}AA`,
1513
- clipPath: 'polygon(0% 0%, 50% 0%, 100% 30%, 90% 60%, 100% 90%, 50% 100%)',
1514
- transform: `rotateX(${-wingFlap}deg)`,
1515
- transformOrigin: 'top center',
1516
- } }),
1517
- React.createElement("div", { style: {
1518
- position: 'absolute',
1519
- width: '40%',
1520
- height: '15%',
1521
- top: '35%',
1522
- left: '5%',
1523
- backgroundColor: particle.color,
1524
- clipPath: 'polygon(0% 50%, 70% 0%, 100% 20%, 100% 80%, 70% 100%, 0% 50%)',
1525
- } }),
1526
- React.createElement("div", { style: {
1527
- position: 'absolute',
1528
- width: '30%',
1529
- height: '20%',
1530
- top: '35%',
1531
- right: '5%',
1532
- background: `radial-gradient(ellipse, #FFD700 0%, #FF4500 50%, transparent 100%)`,
1533
- transform: `scale(${0.8 + firePulse * 0.4})`,
1534
- opacity: 0.8 + firePulse * 0.2,
1535
- filter: 'blur(1px)',
1536
- } }),
1537
- React.createElement("div", { style: {
1538
- position: 'absolute',
1539
- width: '25%',
1540
- height: '15%',
1541
- top: '37%',
1542
- right: '0%',
1543
- background: `radial-gradient(ellipse, #FFA500 0%, #FF6347 50%, transparent 100%)`,
1544
- transform: `scale(${0.6 + firePulse * 0.6})`,
1545
- opacity: 0.6 + firePulse * 0.4,
1546
- filter: 'blur(2px)',
1547
- } })));
1548
- };
1549
-
1550
- const defaultColors$a = ['#9C27B0', '#673AB7', '#3F51B5', '#00BCD4', '#4CAF50', '#FFEB3B', '#FF9800'];
1551
- // Ancient rune symbols
1552
- const runeSymbols = [
1553
- 'ᚠ', 'ᚢ', 'ᚦ', 'ᚨ', 'ᚱ', 'ᚲ', 'ᚷ', 'ᚹ', 'ᚺ', 'ᚾ', 'ᛁ', 'ᛃ', 'ᛇ', 'ᛈ', 'ᛉ', 'ᛋ', 'ᛏ', 'ᛒ', 'ᛖ', 'ᛗ', 'ᛚ', 'ᛜ', 'ᛞ', 'ᛟ'
1554
- ];
1555
- const createRuneParticles = (origin, config) => {
1556
- const { particleCount = 12, startVelocity = 6, colors = defaultColors$a, elementSize = 35 } = config;
1557
- const particles = [];
1558
- for (let i = 0; i < particleCount; i++) {
1559
- // Runes float upward in a mystical pattern
1560
- const angle = randomInRange(250, 290); // Mostly upward
1561
- const velocity = randomInRange(startVelocity * 0.3, startVelocity * 0.7);
1562
- const color = getRandomColor(colors);
1563
- // Create circular spawn pattern
1564
- const spawnAngle = (i / particleCount) * 360;
1565
- const spawnRadius = randomInRange(20, 60);
1566
- const runeIndex = Math.floor(Math.random() * runeSymbols.length);
1567
- particles.push({
1568
- id: generateId(),
1569
- x: origin.x + Math.cos(degreesToRadians(spawnAngle)) * spawnRadius,
1570
- y: origin.y + Math.sin(degreesToRadians(spawnAngle)) * spawnRadius,
1571
- vx: Math.cos(degreesToRadians(angle)) * velocity + randomInRange(-1, 1),
1572
- vy: Math.sin(degreesToRadians(angle)) * velocity,
1573
- life: config.lifetime || 160,
1574
- opacity: 0,
1575
- size: randomInRange(elementSize * 0.8, elementSize * 1.2),
1576
- // Store runeIndex in lower bits of rotation, keep rotation angle in upper bits
1577
- rotation: runeIndex + (Math.floor(randomInRange(0, 360)) * 100),
1578
- color,
1579
- });
1580
- }
1581
- return particles;
1582
- };
1583
- const renderRuneParticle = (particle) => {
1584
- // Extract rune index and rotation from encoded value
1585
- const runeIndex = particle.rotation % 100;
1586
- const rotationAngle = Math.floor(particle.rotation / 100);
1587
- const runeSymbol = runeSymbols[runeIndex] || runeSymbols[0];
1588
- // Calculate pulsing glow effect
1589
- const glowIntensity = (Math.sin(Date.now() * 0.003 + particle.x * 0.01) + 1) / 2;
1590
- // Fade in and out based on life
1591
- const fadeProgress = particle.life > 120 ? (160 - particle.life) / 40 : particle.life < 40 ? particle.life / 40 : 1;
1592
- return (React.createElement("div", { key: particle.id, style: {
1593
- width: `${particle.size}px`,
1594
- height: `${particle.size}px`,
1595
- position: 'relative',
1596
- transform: `rotate(${rotationAngle}deg)`,
1597
- opacity: fadeProgress,
1598
- } },
1599
- React.createElement("div", { style: {
1600
- position: 'absolute',
1601
- width: '100%',
1602
- height: '100%',
1603
- borderRadius: '50%',
1604
- background: `radial-gradient(circle, ${particle.color}44 0%, transparent 70%)`,
1605
- transform: `scale(${1.5 + glowIntensity * 0.5})`,
1606
- filter: 'blur(4px)',
1607
- } }),
1608
- React.createElement("div", { style: {
1609
- position: 'absolute',
1610
- width: '100%',
1611
- height: '100%',
1612
- borderRadius: '50%',
1613
- background: `radial-gradient(circle, ${particle.color}88 0%, ${particle.color}44 50%, transparent 100%)`,
1614
- transform: `scale(${1.2 + glowIntensity * 0.3})`,
1615
- } }),
1616
- React.createElement("div", { style: {
1617
- position: 'absolute',
1618
- width: '100%',
1619
- height: '100%',
1620
- display: 'flex',
1621
- alignItems: 'center',
1622
- justifyContent: 'center',
1623
- fontSize: `${particle.size * 0.7}px`,
1624
- fontWeight: 'bold',
1625
- color: '#FFFFFF',
1626
- textShadow: `
1627
- 0 0 10px ${particle.color},
1628
- 0 0 20px ${particle.color},
1629
- 0 0 30px ${particle.color},
1630
- 0 0 40px ${particle.color}
1631
- `,
1632
- filter: `brightness(${1.5 + glowIntensity * 0.5})`,
1633
- } }, runeSymbol),
1634
- [0, 72, 144, 216, 288].map((angle, index) => (React.createElement("div", { key: index, style: {
1635
- position: 'absolute',
1636
- width: '4px',
1637
- height: '4px',
1638
- backgroundColor: '#FFFFFF',
1639
- borderRadius: '50%',
1640
- top: '50%',
1641
- left: '50%',
1642
- transform: `
1643
- translate(-50%, -50%)
1644
- rotate(${angle + rotationAngle}deg)
1645
- translateY(-${particle.size * 0.6}px)
1646
- scale(${0.5 + glowIntensity})
1647
- `,
1648
- boxShadow: '0 0 6px #FFFFFF',
1649
- opacity: glowIntensity,
1650
- } })))));
1651
- };
1652
-
1653
- const defaultColors$9 = ['#FF6B6B', '#4ECDC4', '#45B7D1', '#F7DC6F', '#BB8FCE'];
1654
- const createButterflyParticles = (origin, config) => {
1655
- const { particleCount = 6, startVelocity = 3, colors = defaultColors$9, elementSize = 40 } = config;
1656
- const particles = [];
1657
- for (let i = 0; i < particleCount; i++) {
1658
- const angle = randomInRange(0, 360);
1659
- const velocity = randomInRange(startVelocity * 0.5, startVelocity);
1660
- particles.push({
1661
- id: generateId(),
1662
- x: origin.x + randomInRange(-50, 50),
1663
- y: origin.y + randomInRange(-30, 30),
1664
- vx: Math.cos(angle * Math.PI / 180) * velocity,
1665
- vy: -Math.abs(Math.sin(angle * Math.PI / 180) * velocity * 0.5), // Float upward
1666
- life: config.lifetime || 200,
1667
- opacity: 1,
1668
- size: randomInRange(elementSize * 0.8, elementSize * 1.2),
1669
- // Encode wing phase (0-360), wobble phase (0-360), and rotation speed (-10 to 10)
1670
- rotation: Math.floor(randomInRange(0, 360)) + (Math.floor(randomInRange(0, 360)) * 1000) + ((Math.floor(randomInRange(-10, 10)) + 10) * 1000000),
1671
- color: getRandomColor(colors),
1672
- });
1673
- }
1674
- return particles;
1675
- };
1676
- const renderButterflyParticle = (particle) => {
1677
- // Extract encoded values
1678
- const wingPhase = (particle.rotation % 1000);
1679
- const wobblePhase = Math.floor((particle.rotation % 1000000) / 1000);
1680
- const rotationSpeed = (Math.floor(particle.rotation / 1000000) - 10) / 10;
1681
- // Calculate wing flapping and wobble
1682
- const wingAngle = Math.sin((Date.now() * 0.01 + wingPhase) * Math.PI / 180) * 30;
1683
- const wobbleX = Math.sin((Date.now() * 0.003 + wobblePhase) * Math.PI / 180) * 10;
1684
- const rotation = (Date.now() * rotationSpeed * 0.01) % 360;
1685
- return (React.createElement("div", { key: particle.id, style: {
1686
- width: `${particle.size}px`,
1687
- height: `${particle.size}px`,
1688
- position: 'relative',
1689
- transform: `translateX(${wobbleX}px) rotate(${rotation}deg)`,
1690
- } },
1691
- React.createElement("svg", { width: particle.size, height: particle.size, viewBox: "-25 -25 50 50", style: {
1692
- position: 'absolute',
1693
- top: 0,
1694
- left: 0,
1695
- } },
1696
- React.createElement("ellipse", { cx: "-8", cy: "0", rx: "12", ry: "18", fill: particle.color, opacity: "0.8", transform: `rotate(${-wingAngle} 0 0)` }),
1697
- React.createElement("ellipse", { cx: "-6", cy: "-10", rx: "8", ry: "12", fill: particle.color, opacity: "0.6", transform: `rotate(${-wingAngle - 15} 0 0)` }),
1698
- React.createElement("ellipse", { cx: "8", cy: "0", rx: "12", ry: "18", fill: particle.color, opacity: "0.8", transform: `rotate(${wingAngle} 0 0)` }),
1699
- React.createElement("ellipse", { cx: "6", cy: "-10", rx: "8", ry: "12", fill: particle.color, opacity: "0.6", transform: `rotate(${wingAngle + 15} 0 0)` }),
1700
- React.createElement("ellipse", { cx: "0", cy: "0", rx: "3", ry: "15", fill: "#333" }),
1701
- React.createElement("line", { x1: "0", y1: "-12", x2: "-3", y2: "-18", stroke: "#333", strokeWidth: "1" }),
1702
- React.createElement("line", { x1: "0", y1: "-12", x2: "3", y2: "-18", stroke: "#333", strokeWidth: "1" }),
1703
- React.createElement("circle", { cx: "-8", cy: "0", r: "3", fill: "white", opacity: "0.4" }),
1704
- React.createElement("circle", { cx: "8", cy: "0", r: "3", fill: "white", opacity: "0.4" }),
1705
- React.createElement("circle", { cx: "-6", cy: "-8", r: "2", fill: "black", opacity: "0.3" }),
1706
- React.createElement("circle", { cx: "6", cy: "-8", r: "2", fill: "black", opacity: "0.3" }))));
1707
- };
1708
-
1709
- const defaultColors$8 = ['#D2691E', '#CD853F', '#8B4513', '#A0522D', '#FF8C00', '#FF6347'];
1710
- const createLeafParticles = (origin, config) => {
1711
- const { particleCount = 10, colors = defaultColors$8, elementSize = 25 } = config;
1712
- const particles = [];
1713
- for (let i = 0; i < particleCount; i++) {
1714
- particles.push({
1715
- id: generateId(),
1716
- x: origin.x + randomInRange(-100, 100),
1717
- y: origin.y + randomInRange(-50, 0),
1718
- vx: randomInRange(-1, 1),
1719
- vy: randomInRange(0.5, 2),
1720
- life: config.lifetime || 300,
1721
- opacity: 1,
1722
- size: randomInRange(elementSize * 0.6, elementSize),
1723
- // Encode tumble phase (0-360), sway phase (0-360), and sway amount (20-40) + rotation speed (-3 to 3)
1724
- rotation: Math.floor(randomInRange(0, 360)) +
1725
- (Math.floor(randomInRange(0, 360)) * 1000) +
1726
- (Math.floor(randomInRange(20, 40)) * 1000000) +
1727
- ((Math.floor(randomInRange(-3, 3)) + 3) * 100000000),
1728
- color: getRandomColor(colors),
1729
- });
1730
- }
1731
- return particles;
1732
- };
1733
- const renderLeafParticle = (particle) => {
1734
- // Extract encoded values
1735
- const tumblePhase = particle.rotation % 1000;
1736
- const swayPhase = Math.floor((particle.rotation % 1000000) / 1000);
1737
- const swayAmount = Math.floor((particle.rotation % 100000000) / 1000000);
1738
- const rotationSpeed = (Math.floor(particle.rotation / 100000000) - 3);
1739
- // Calculate tumbling and swaying
1740
- const tumble = Math.sin((Date.now() * 0.002 + tumblePhase) * Math.PI / 180) * 30;
1741
- const swayX = Math.sin((Date.now() * 0.001 + swayPhase) * Math.PI / 180) * swayAmount;
1742
- const rotation = (Date.now() * rotationSpeed * 0.01 + tumble) % 360;
1743
- return (React.createElement("div", { key: particle.id, style: {
1744
- width: `${particle.size}px`,
1745
- height: `${particle.size}px`,
1746
- position: 'relative',
1747
- transform: `translateX(${swayX}px) rotate(${rotation}deg)`,
1748
- } },
1749
- React.createElement("svg", { width: particle.size, height: particle.size, viewBox: `-${particle.size / 2} -${particle.size / 2} ${particle.size} ${particle.size}`, style: {
1750
- position: 'absolute',
1751
- top: 0,
1752
- left: 0,
1753
- } },
1754
- React.createElement("path", { d: `
1755
- M 0,-${particle.size / 2}
1756
- C -${particle.size / 3},-${particle.size / 3} -${particle.size / 3},${particle.size / 3} 0,${particle.size / 2}
1757
- C ${particle.size / 3},${particle.size / 3} ${particle.size / 3},-${particle.size / 3} 0,-${particle.size / 2}
1758
- `, fill: particle.color, opacity: "0.9" }),
1759
- React.createElement("line", { x1: "0", y1: -particle.size / 2, x2: "0", y2: particle.size / 2, stroke: "#8B4513", strokeWidth: "1", opacity: "0.5" }),
1760
- React.createElement("line", { x1: "0", y1: -particle.size / 4, x2: -particle.size / 4, y2: -particle.size / 8, stroke: "#8B4513", strokeWidth: "0.5", opacity: "0.5" }),
1761
- React.createElement("line", { x1: "0", y1: -particle.size / 4, x2: particle.size / 4, y2: -particle.size / 8, stroke: "#8B4513", strokeWidth: "0.5", opacity: "0.5" }),
1762
- React.createElement("line", { x1: "0", y1: particle.size / 4, x2: -particle.size / 4, y2: particle.size / 8, stroke: "#8B4513", strokeWidth: "0.5", opacity: "0.5" }),
1763
- React.createElement("line", { x1: "0", y1: particle.size / 4, x2: particle.size / 4, y2: particle.size / 8, stroke: "#8B4513", strokeWidth: "0.5", opacity: "0.5" }))));
1764
- };
1765
-
1766
- const defaultColors$7 = ['#4A90E2', '#5BA0F2', '#6BB0FF', '#3A80D2'];
1767
- const createRainParticles = (origin, config) => {
1768
- const { particleCount = 50, colors = defaultColors$7, elementSize = 300 } = config;
1769
- const particles = [];
1770
- for (let i = 0; i < particleCount; i++) {
1771
- particles.push({
1772
- id: generateId(),
1773
- x: randomInRange(origin.x - elementSize / 2, origin.x + elementSize / 2),
1774
- y: origin.y - randomInRange(0, elementSize),
1775
- vx: randomInRange(-0.5, 0.5),
1776
- vy: randomInRange(8, 12),
1777
- life: config.lifetime || 150,
1778
- opacity: randomInRange(0.3, 0.7),
1779
- size: randomInRange(2, 4),
1780
- // Encode: length (15-25) + splashing state (0 or 1) * 100 + splash frame (0-15) * 1000
1781
- rotation: Math.floor(randomInRange(15, 25)),
1782
- color: getRandomColor(colors),
1783
- });
1784
- }
1785
- return particles;
1786
- };
1787
- const renderRainParticle = (particle) => {
1788
- // Extract encoded values
1789
- const length = particle.rotation % 100;
1790
- const isSplashing = Math.floor((particle.rotation % 1000) / 100) === 1;
1791
- const splashFrame = Math.floor(particle.rotation / 1000);
1792
- // Render splash effect
1793
- if (isSplashing && splashFrame < 15) {
1794
- const splashRadius = splashFrame * 2;
1795
- const splashOpacity = Math.max(0, 1 - splashFrame / 15) * 0.5;
1796
- return (React.createElement("div", { key: particle.id, style: { position: 'relative' } },
1797
- React.createElement("svg", { width: splashRadius * 4, height: splashRadius * 2, style: {
1798
- position: 'absolute',
1799
- left: -splashRadius * 2,
1800
- top: -splashRadius,
1801
- } },
1802
- React.createElement("ellipse", { cx: splashRadius * 2, cy: splashRadius, rx: splashRadius, ry: splashRadius / 3, fill: "none", stroke: particle.color, strokeWidth: "1", opacity: splashOpacity }),
1803
- [0, 1, 2, 3].map((i) => {
1804
- const angle = (i * 90 + 45) * Math.PI / 180;
1805
- const distance = splashFrame * 1.5;
1806
- const dropX = splashRadius * 2 + Math.cos(angle) * distance;
1807
- const dropY = splashRadius - Math.sin(angle) * distance + splashFrame * 0.5;
1808
- return (React.createElement("circle", { key: i, cx: dropX, cy: dropY, r: "1.5", fill: particle.color, opacity: splashOpacity }));
1809
- }))));
1810
- }
1811
- // Render falling raindrop
1812
- return (React.createElement("div", { key: particle.id, style: { position: 'relative' } },
1813
- React.createElement("svg", { width: particle.size * 2, height: length + particle.size * 3, style: {
1814
- position: 'absolute',
1815
- left: -particle.size,
1816
- top: -length,
1817
- } },
1818
- React.createElement("line", { x1: particle.size, y1: 0, x2: particle.size, y2: length, stroke: particle.color, strokeWidth: particle.size, strokeLinecap: "round", opacity: particle.opacity * 0.3 }),
1819
- React.createElement("ellipse", { cx: particle.size, cy: length + particle.size * 1.5, rx: particle.size, ry: particle.size * 1.5, fill: particle.color, opacity: particle.opacity }))));
1820
- };
1821
-
1822
- const defaultColors$6 = ['#FFFFFF', '#F5F5F5', '#FAFAFA'];
1823
- const createDandelionParticles = (origin, config) => {
1824
- const { particleCount = 15, colors = defaultColors$6, elementSize = 15 } = config;
1825
- const particles = [];
1826
- for (let i = 0; i < particleCount; i++) {
1827
- particles.push({
1828
- id: generateId(),
1829
- x: origin.x + randomInRange(-20, 20),
1830
- y: origin.y + randomInRange(-20, 20),
1831
- vx: randomInRange(-2, 2),
1832
- vy: randomInRange(-1, -0.3),
1833
- life: config.lifetime || 200,
1834
- opacity: 1,
1835
- size: randomInRange(elementSize * 0.8, elementSize * 1.2),
1836
- // Encode: rotation angle + float phase * 1000 + drift phase * 1000000
1837
- rotation: Math.floor(randomInRange(0, 360)) +
1838
- (Math.floor(randomInRange(0, 360)) * 1000) +
1839
- (Math.floor(randomInRange(0, 360)) * 1000000),
1840
- color: getRandomColor(colors),
1841
- });
1842
- }
1843
- return particles;
1844
- };
1845
- const renderDandelionParticle = (particle) => {
1846
- // Extract encoded values
1847
- const rotationAngle = particle.rotation % 1000;
1848
- const floatPhase = Math.floor((particle.rotation % 1000000) / 1000);
1849
- const driftPhase = Math.floor(particle.rotation / 1000000);
1850
- // Calculate floating effects
1851
- const floatY = Math.sin((Date.now() * 0.002 + floatPhase) * Math.PI / 180) * 5;
1852
- const driftX = Math.sin((Date.now() * 0.001 + driftPhase) * Math.PI / 180) * 3;
1853
- const rotation = (Date.now() * 0.01 + rotationAngle) % 360;
1854
- const seedCount = 7;
1855
- return (React.createElement("div", { key: particle.id, style: {
1856
- width: `${particle.size * 2}px`,
1857
- height: `${particle.size * 2}px`,
1858
- position: 'relative',
1859
- transform: `translate(${driftX}px, ${floatY}px) rotate(${rotation}deg)`,
1860
- opacity: particle.opacity,
1861
- } },
1862
- React.createElement("svg", { width: particle.size * 2, height: particle.size * 2, viewBox: `-${particle.size} -${particle.size} ${particle.size * 2} ${particle.size * 2}`, style: {
1863
- position: 'absolute',
1864
- top: 0,
1865
- left: 0,
1866
- } },
1867
- React.createElement("circle", { cx: "0", cy: "0", r: "2", fill: "#F5F5DC" }),
1868
- Array.from({ length: seedCount }).map((_, i) => {
1869
- const angle = (i * 360 / seedCount) * Math.PI / 180;
1870
- const endX = Math.cos(angle) * particle.size;
1871
- const endY = Math.sin(angle) * particle.size;
1872
- const midX = Math.cos(angle) * particle.size * 0.7;
1873
- const midY = Math.sin(angle) * particle.size * 0.7;
1874
- return (React.createElement("g", { key: i },
1875
- React.createElement("line", { x1: "0", y1: "0", x2: endX, y2: endY, stroke: "#E5E5E5", strokeWidth: "0.5", opacity: "0.8" }),
1876
- React.createElement("circle", { cx: endX, cy: endY, r: "3", fill: particle.color, opacity: "0.6" }),
1877
- React.createElement("circle", { cx: midX, cy: midY, r: "2", fill: particle.color, opacity: "0.4" }),
1878
- [0, 1, 2].map((j) => {
1879
- const filamentAngle = angle + (j - 1) * 0.1;
1880
- const filamentX = endX + Math.cos(filamentAngle) * 2;
1881
- const filamentY = endY + Math.sin(filamentAngle) * 2;
1882
- return (React.createElement("line", { key: j, x1: endX, y1: endY, x2: filamentX, y2: filamentY, stroke: "#E5E5E5", strokeWidth: "0.3", opacity: "0.6" }));
1883
- })));
1884
- }))));
1885
- };
1886
-
1887
- const defaultColors$5 = ['#FFD700', '#FFA500'];
1888
- const createBeeParticles = (origin, config) => {
1889
- const { particleCount = 5, colors = defaultColors$5, elementSize = 18 } = config;
1890
- const particles = [];
1891
- for (let i = 0; i < particleCount; i++) {
1892
- particles.push({
1893
- id: generateId(),
1894
- x: origin.x + randomInRange(-50, 50),
1895
- y: origin.y + randomInRange(-50, 50),
1896
- vx: randomInRange(2, 4),
1897
- vy: randomInRange(-1, 1),
1898
- life: config.lifetime || 300,
1899
- opacity: 1,
1900
- size: randomInRange(elementSize * 0.8, elementSize * 1.1),
1901
- // Encode: zigzag phase (0-360) + wing phase (0-360) * 1000 + buzz phase (0-360) * 1000000
1902
- rotation: Math.floor(randomInRange(0, 360)) +
1903
- (Math.floor(randomInRange(0, 360)) * 1000) +
1904
- (Math.floor(randomInRange(0, 360)) * 1000000),
1905
- color: getRandomColor(colors),
1906
- });
1907
- }
1908
- return particles;
1909
- };
1910
- const renderBeeParticle = (particle) => {
1911
- // Extract encoded values
1912
- const zigzagPhase = particle.rotation % 1000;
1913
- const wingPhase = Math.floor((particle.rotation % 1000000) / 1000);
1914
- const buzzPhase = Math.floor(particle.rotation / 1000000);
1915
- // Calculate animations
1916
- const zigzagY = Math.sin((Date.now() * 0.005 + zigzagPhase) * Math.PI / 180) * 15;
1917
- const wingAngle = Math.sin((Date.now() * 0.02 + wingPhase) * Math.PI / 180) * 20;
1918
- const buzzOffset = Math.sin((Date.now() * 0.015 + buzzPhase) * Math.PI / 180) * 2;
1919
- // Calculate rotation based on movement direction
1920
- const movementAngle = Math.atan2(particle.vy, particle.vx) * 180 / Math.PI;
1921
- return (React.createElement("div", { key: particle.id, style: {
1922
- width: `${particle.size * 2}px`,
1923
- height: `${particle.size * 2}px`,
1924
- position: 'relative',
1925
- transform: `translate(${buzzOffset}px, ${zigzagY}px) rotate(${movementAngle}deg)`,
1926
- } },
1927
- React.createElement("svg", { width: particle.size * 2, height: particle.size * 2, viewBox: "-15 -10 30 20", style: {
1928
- position: 'absolute',
1929
- top: 0,
1930
- left: 0,
1931
- } },
1932
- React.createElement("ellipse", { cx: "-2", cy: "-3", rx: "8", ry: "4", fill: "#E5E5E5", opacity: "0.7", transform: `rotate(${-wingAngle - 20} -2 -3)` }),
1933
- React.createElement("ellipse", { cx: "-2", cy: "3", rx: "8", ry: "4", fill: "#E5E5E5", opacity: "0.7", transform: `rotate(${wingAngle + 20} -2 3)` }),
1934
- React.createElement("ellipse", { cx: "0", cy: "0", rx: "8", ry: "6", fill: particle.color }),
1935
- React.createElement("ellipse", { cx: "-6", cy: "0", rx: "6", ry: "5", fill: "#333" }),
1936
- React.createElement("rect", { x: "-3", y: "-6", width: "2", height: "12", fill: "#333" }),
1937
- React.createElement("rect", { x: "1", y: "-6", width: "2", height: "12", fill: "#333" }),
1938
- React.createElement("rect", { x: "5", y: "-5", width: "2", height: "10", fill: "#333" }),
1939
- React.createElement("circle", { cx: "8", cy: "0", r: "4", fill: "#333" }),
1940
- React.createElement("circle", { cx: "9", cy: "-1.5", r: "1", fill: "#FFF" }),
1941
- React.createElement("circle", { cx: "9", cy: "1.5", r: "1", fill: "#FFF" }),
1942
- React.createElement("line", { x1: "10", y1: "-2", x2: "12", y2: "-4", stroke: "#333", strokeWidth: "0.5" }),
1943
- React.createElement("line", { x1: "10", y1: "2", x2: "12", y2: "4", stroke: "#333", strokeWidth: "0.5" }),
1944
- React.createElement("polygon", { points: "-10,0 -12,1 -12,-1", fill: "#333" }))));
1945
- };
1946
-
1947
- const defaultColors$4 = ['#FFE5B4', '#FFF8DC', '#FFFACD', '#FFF5EE'];
1948
- const createPopcornParticles = (origin, config) => {
1949
- const { particleCount = 20, startVelocity = 25, colors = defaultColors$4, elementSize = 20 } = config;
1950
- const particles = [];
1951
- for (let i = 0; i < particleCount; i++) {
1952
- const angle = randomInRange(0, 360);
1953
- const velocity = randomInRange(startVelocity * 0.5, startVelocity);
1954
- particles.push({
1955
- id: generateId(),
1956
- x: origin.x,
1957
- y: origin.y,
1958
- vx: Math.cos(degreesToRadians(angle)) * velocity,
1959
- vy: Math.sin(degreesToRadians(angle)) * velocity - 10, // Initial upward boost
1960
- life: config.lifetime || 150,
1961
- opacity: 1,
1962
- size: randomInRange(elementSize * 0.8, elementSize * 1.2),
1963
- rotation: randomInRange(0, 360),
1964
- color: getRandomColor(colors),
1965
- });
1966
- }
1967
- return particles;
1968
- };
1969
- const renderPopcornParticle = (particle) => {
1970
- // Add some rotation animation
1971
- const rotation = particle.rotation + (150 - particle.life) * 3;
1972
- // Add bounce effect
1973
- const bounceProgress = (150 - particle.life) / 150;
1974
- const scale = 0.5 + Math.sin(bounceProgress * Math.PI) * 0.5;
1975
- return (React.createElement("div", { key: particle.id, style: {
1976
- fontSize: `${particle.size}px`,
1977
- transform: `rotate(${rotation}deg) scale(${scale})`,
1978
- opacity: particle.opacity,
1979
- userSelect: 'none',
1980
- } }, "\uD83C\uDF7F"));
1981
- };
1982
-
1983
- const defaultColors$3 = ['#F0E68C', '#FFD700', '#FFF8DC', '#FFFACD', '#F5DEB3'];
1984
- const createChampagneParticles = (origin, config) => {
1985
- const { particleCount = 40, startVelocity = 30, colors = defaultColors$3, elementSize = 12 } = config;
1986
- const particles = [];
1987
- for (let i = 0; i < particleCount; i++) {
1988
- const isBubble = i < particleCount * 0.7;
1989
- const angle = isBubble
1990
- ? randomInRange(60, 120) // Mostly upward for bubbles
1991
- : randomInRange(0, 360); // Any direction for cork
1992
- const velocity = randomInRange(startVelocity * 0.5, startVelocity);
1993
- particles.push({
1994
- id: generateId(),
1995
- x: origin.x,
1996
- y: origin.y,
1997
- vx: Math.cos(degreesToRadians(angle)) * velocity,
1998
- vy: Math.sin(degreesToRadians(angle)) * velocity * (isBubble ? -1 : 1),
1999
- life: config.lifetime || 200,
2000
- opacity: isBubble ? 0.6 : 1,
2001
- size: isBubble ? randomInRange(4, 8) : elementSize,
2002
- // Encode: isBubble (0 or 1) + wobble phase * 10
2003
- rotation: (isBubble ? 0 : 1) + Math.floor(randomInRange(0, 360)) * 10,
2004
- color: getRandomColor(colors),
2005
- });
2006
- }
2007
- return particles;
2008
- };
2009
- const renderChampagneParticle = (particle) => {
2010
- const isBubble = (particle.rotation % 10) === 0;
2011
- const wobblePhase = Math.floor(particle.rotation / 10);
2012
- if (isBubble) {
2013
- // Render bubble
2014
- const wobble = Math.sin((Date.now() * 0.003 + wobblePhase) * Math.PI / 180) * 10;
2015
- return (React.createElement("div", { key: particle.id, style: {
2016
- width: `${particle.size}px`,
2017
- height: `${particle.size}px`,
2018
- borderRadius: '50%',
2019
- backgroundColor: particle.color,
2020
- opacity: particle.opacity,
2021
- border: `1px solid ${particle.color}`,
2022
- transform: `translateX(${wobble}px)`,
2023
- boxShadow: `inset -2px -2px 4px rgba(0,0,0,0.1), inset 2px 2px 4px rgba(255,255,255,0.5)`,
2024
- } }));
2025
- }
2026
- // Render cork (champagne bottle)
2027
- const rotation = (Date.now() * 0.2 + wobblePhase) % 360;
2028
- return (React.createElement("div", { key: particle.id, style: {
2029
- fontSize: `${particle.size}px`,
2030
- transform: `rotate(${rotation}deg)`,
2031
- opacity: particle.opacity,
2032
- userSelect: 'none',
2033
- } }, "\uD83C\uDF7E"));
2034
- };
2035
-
2036
- const defaultColors$2 = ['#FF69B4', '#FF1493', '#FFB6C1', '#FFC0CB', '#FF6347', '#FFA500', '#FFD700', '#98FB98', '#87CEEB', '#DDA0DD'];
2037
- const candyEmojis = ['🍬', '🍭', '🍮', '🍯'];
2038
- const createCandyParticles = (origin, config) => {
2039
- const { particleCount = 20, startVelocity = 20, colors = defaultColors$2, elementSize = 25 } = config;
2040
- const particles = [];
2041
- for (let i = 0; i < particleCount; i++) {
2042
- const angle = randomInRange(-30, 30); // Mostly falling down
2043
- const velocity = randomInRange(startVelocity * 0.5, startVelocity);
2044
- particles.push({
2045
- id: generateId(),
2046
- x: origin.x,
2047
- y: origin.y - 50, // Start above origin
2048
- vx: Math.sin(angle * Math.PI / 180) * velocity * 0.5,
2049
- vy: randomInRange(3, 6), // Fall down
2050
- life: config.lifetime || 180,
2051
- opacity: 1,
2052
- size: randomInRange(elementSize * 0.7, elementSize * 1.3),
2053
- // Encode candy type (0-3) + initial rotation * 10
2054
- rotation: Math.floor(Math.random() * candyEmojis.length) + (Math.floor(randomInRange(0, 360)) * 10),
2055
- color: getRandomColor(colors),
2056
- });
2057
- }
2058
- return particles;
2059
- };
2060
- const renderCandyParticle = (particle) => {
2061
- const candyIndex = particle.rotation % 10;
2062
- const initialRotation = Math.floor(particle.rotation / 10);
2063
- const candy = candyEmojis[candyIndex] || candyEmojis[0];
2064
- // Add tumbling rotation
2065
- const tumbleSpeed = 5;
2066
- const rotation = initialRotation + (180 - particle.life) * tumbleSpeed;
2067
- // Add sway effect
2068
- const swayAmount = Math.sin((180 - particle.life) * 0.05) * 20;
2069
- return (React.createElement("div", { key: particle.id, style: {
2070
- fontSize: `${particle.size}px`,
2071
- transform: `translateX(${swayAmount}px) rotate(${rotation}deg)`,
2072
- opacity: particle.opacity,
2073
- userSelect: 'none',
2074
- } }, candy));
2075
- };
2076
-
2077
- const defaultColors$1 = ['#FFB6C1', '#FF69B4', '#FFD700', '#FF6347', '#98D8C8', '#F7DC6F', '#BB8FCE', '#85C1E2'];
2078
- const createDonutParticles = (origin, config) => {
2079
- const { particleCount = 15, startVelocity = 25, colors = defaultColors$1, elementSize = 30 } = config;
2080
- const particles = [];
2081
- for (let i = 0; i < particleCount; i++) {
2082
- const angle = randomInRange(0, 360);
2083
- const velocity = randomInRange(startVelocity * 0.5, startVelocity);
2084
- particles.push({
2085
- id: generateId(),
2086
- x: origin.x,
2087
- y: origin.y,
2088
- vx: Math.cos(degreesToRadians(angle)) * velocity,
2089
- vy: Math.sin(degreesToRadians(angle)) * velocity - 10, // Initial upward boost
2090
- life: config.lifetime || 200,
2091
- opacity: 1,
2092
- size: randomInRange(elementSize * 0.6, elementSize * 1.4),
2093
- rotation: randomInRange(0, 360),
2094
- color: getRandomColor(colors),
2095
- });
2096
- }
2097
- return particles;
2098
- };
2099
- const renderDonutParticle = (particle) => {
2100
- // Add spinning animation
2101
- const spinSpeed = 8;
2102
- const rotation = particle.rotation + (200 - particle.life) * spinSpeed;
2103
- // Add bounce effect
2104
- const bounceProgress = (200 - particle.life) / 200;
2105
- const scale = 0.9 + Math.sin(bounceProgress * Math.PI * 2) * 0.1;
2106
- return (React.createElement("div", { key: particle.id, style: {
2107
- fontSize: `${particle.size}px`,
2108
- transform: `rotate(${rotation}deg) scale(${scale})`,
2109
- opacity: particle.opacity,
2110
- userSelect: 'none',
2111
- position: 'relative',
2112
- } },
2113
- "\uD83C\uDF69",
2114
- [0, 120, 240].map((angle, i) => (React.createElement("div", { key: i, style: {
2115
- position: 'absolute',
2116
- width: '3px',
2117
- height: '8px',
2118
- backgroundColor: particle.color,
2119
- borderRadius: '40%',
2120
- top: '50%',
2121
- left: '50%',
2122
- transform: `
2123
- translate(-50%, -50%)
2124
- rotate(${angle + rotation}deg)
2125
- translateY(${particle.size * 0.3}px)
2126
- scale(${scale})
2127
- `,
2128
- opacity: particle.opacity * 0.8,
2129
- } })))));
2130
- };
2131
-
2132
- const defaultColors = ['#FF6347', '#FFD700', '#98D8C8', '#FF8C00', '#DC143C'];
2133
- const createPizzaParticles = (origin, config) => {
2134
- const { particleCount = 12, startVelocity = 30, colors = defaultColors, elementSize = 35 } = config;
2135
- const particles = [];
2136
- for (let i = 0; i < particleCount; i++) {
2137
- const angle = randomInRange(0, 360);
2138
- const velocity = randomInRange(startVelocity * 0.5, startVelocity);
2139
- particles.push({
2140
- id: generateId(),
2141
- x: origin.x,
2142
- y: origin.y,
2143
- vx: Math.cos(degreesToRadians(angle)) * velocity,
2144
- vy: Math.sin(degreesToRadians(angle)) * velocity - 15, // Initial upward boost
2145
- life: config.lifetime || 180,
2146
- opacity: 1,
2147
- size: randomInRange(elementSize * 0.5, elementSize * 1.2),
2148
- rotation: randomInRange(0, 360),
2149
- color: getRandomColor(colors),
2150
- });
2151
- }
2152
- return particles;
2153
- };
2154
- const renderPizzaParticle = (particle) => {
2155
- // Add spinning and flipping animation
2156
- const spinSpeed = 10;
2157
- const rotation = particle.rotation + (180 - particle.life) * spinSpeed;
2158
- const flipAngle = (180 - particle.life) * 2;
2159
- // Add wobble effect
2160
- const wobble = Math.sin((180 - particle.life) * 0.1) * 10;
2161
- // Scale animation
2162
- const progress = (180 - particle.life) / 180;
2163
- const scale = 0.8 + Math.sin(progress * Math.PI) * 0.2;
2164
- return (React.createElement("div", { key: particle.id, style: {
2165
- fontSize: `${particle.size}px`,
2166
- transform: `
2167
- translateX(${wobble}px)
2168
- rotate(${rotation}deg)
2169
- rotateX(${flipAngle}deg)
2170
- scale(${scale})
2171
- `,
2172
- transformStyle: 'preserve-3d',
2173
- opacity: particle.opacity,
2174
- userSelect: 'none',
2175
- position: 'relative',
2176
- } },
2177
- "\uD83C\uDF55",
2178
- Math.random() > 0.5 && (React.createElement("div", { style: {
2179
- position: 'absolute',
2180
- fontSize: `${particle.size * 0.4}px`,
2181
- top: `${10 + Math.random() * 30}%`,
2182
- left: `${10 + Math.random() * 30}%`,
2183
- transform: `rotate(${Math.random() * 360}deg)`,
2184
- opacity: particle.opacity * 0.8,
2185
- } }, ['🍅', '🧀', '🌶️'][Math.floor(Math.random() * 3)]))));
1062
+ React.createElement("path", { d: `
1063
+ M 0,-${particle.size / 2}
1064
+ C -${particle.size / 3},-${particle.size / 3} -${particle.size / 3},${particle.size / 3} 0,${particle.size / 2}
1065
+ C ${particle.size / 3},${particle.size / 3} ${particle.size / 3},-${particle.size / 3} 0,-${particle.size / 2}
1066
+ `, fill: particle.color, opacity: "0.9" }),
1067
+ React.createElement("line", { x1: "0", y1: -particle.size / 2, x2: "0", y2: particle.size / 2, stroke: "#8B4513", strokeWidth: "1", opacity: "0.5" }),
1068
+ React.createElement("line", { x1: "0", y1: -particle.size / 4, x2: -particle.size / 4, y2: -particle.size / 8, stroke: "#8B4513", strokeWidth: "0.5", opacity: "0.5" }),
1069
+ React.createElement("line", { x1: "0", y1: -particle.size / 4, x2: particle.size / 4, y2: -particle.size / 8, stroke: "#8B4513", strokeWidth: "0.5", opacity: "0.5" }),
1070
+ React.createElement("line", { x1: "0", y1: particle.size / 4, x2: -particle.size / 4, y2: particle.size / 8, stroke: "#8B4513", strokeWidth: "0.5", opacity: "0.5" }),
1071
+ React.createElement("line", { x1: "0", y1: particle.size / 4, x2: particle.size / 4, y2: particle.size / 8, stroke: "#8B4513", strokeWidth: "0.5", opacity: "0.5" }))));
2186
1072
  };
2187
1073
 
2188
1074
  const animations = {
@@ -2222,10 +1108,6 @@ const animations = {
2222
1108
  createParticles: createCoinParticles,
2223
1109
  renderParticle: renderCoinParticle,
2224
1110
  },
2225
- lightning: {
2226
- createParticles: createLightningParticles,
2227
- renderParticle: renderLightningParticle,
2228
- },
2229
1111
  petals: {
2230
1112
  createParticles: createPetalParticles,
2231
1113
  renderParticle: renderPetalParticle,
@@ -2242,10 +1124,6 @@ const animations = {
2242
1124
  createParticles: createPaintParticles,
2243
1125
  renderParticle: renderPaintParticle,
2244
1126
  },
2245
- music: {
2246
- createParticles: createMusicParticles,
2247
- renderParticle: renderMusicParticle,
2248
- },
2249
1127
  balloons: {
2250
1128
  createParticles: createBalloonParticles,
2251
1129
  renderParticle: renderBalloonParticle,
@@ -2254,86 +1132,58 @@ const animations = {
2254
1132
  createParticles: createGalaxyParticles,
2255
1133
  renderParticle: renderGalaxyParticle,
2256
1134
  },
2257
- matrix: {
2258
- createParticles: createMatrixParticles,
2259
- renderParticle: renderMatrixParticle,
2260
- },
2261
- pixels: {
2262
- createParticles: createPixelParticles,
2263
- renderParticle: renderPixelParticle,
2264
- },
2265
1135
  glitch: {
2266
1136
  createParticles: createGlitchParticles,
2267
1137
  renderParticle: renderGlitchParticle,
2268
1138
  },
2269
- dice: {
2270
- createParticles: createDiceParticles,
2271
- renderParticle: renderDiceParticle,
2272
- },
2273
- levelup: {
2274
- createParticles: createLevelUpParticles,
2275
- renderParticle: renderLevelUpParticle,
2276
- },
2277
1139
  magicdust: {
2278
1140
  createParticles: createMagicDustParticles,
2279
1141
  renderParticle: renderMagicDustParticle,
2280
1142
  },
2281
- ghosts: {
2282
- createParticles: createGhostParticles,
2283
- renderParticle: renderGhostParticle,
2284
- },
2285
1143
  crystals: {
2286
1144
  createParticles: createCrystalParticles,
2287
1145
  renderParticle: renderCrystalParticle,
2288
1146
  },
2289
- dragons: {
2290
- createParticles: createDragonParticles,
2291
- renderParticle: renderDragonParticle,
2292
- },
2293
- runes: {
2294
- createParticles: createRuneParticles,
2295
- renderParticle: renderRuneParticle,
2296
- },
2297
- butterflies: {
2298
- createParticles: createButterflyParticles,
2299
- renderParticle: renderButterflyParticle,
2300
- },
2301
1147
  leaves: {
2302
1148
  createParticles: createLeafParticles,
2303
1149
  renderParticle: renderLeafParticle,
2304
1150
  },
2305
- rain: {
2306
- createParticles: createRainParticles,
2307
- renderParticle: renderRainParticle,
2308
- },
2309
- dandelion: {
2310
- createParticles: createDandelionParticles,
2311
- renderParticle: renderDandelionParticle,
2312
- },
2313
- bees: {
2314
- createParticles: createBeeParticles,
2315
- renderParticle: renderBeeParticle,
2316
- },
2317
- popcorn: {
2318
- createParticles: createPopcornParticles,
2319
- renderParticle: renderPopcornParticle,
2320
- },
2321
- champagne: {
2322
- createParticles: createChampagneParticles,
2323
- renderParticle: renderChampagneParticle,
2324
- },
2325
- candy: {
2326
- createParticles: createCandyParticles,
2327
- renderParticle: renderCandyParticle,
2328
- },
2329
- donuts: {
2330
- createParticles: createDonutParticles,
2331
- renderParticle: renderDonutParticle,
2332
- },
2333
- pizza: {
2334
- createParticles: createPizzaParticles,
2335
- renderParticle: renderPizzaParticle,
2336
- },
1151
+ };
1152
+
1153
+ const isMobileDevice = () => {
1154
+ if (typeof window === 'undefined')
1155
+ return false;
1156
+ // Check user agent
1157
+ const userAgent = navigator.userAgent || navigator.vendor || window.opera;
1158
+ const isMobileUA = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(userAgent);
1159
+ // Check viewport width
1160
+ const isMobileWidth = window.innerWidth <= 768;
1161
+ // Check touch support
1162
+ const hasTouch = 'ontouchstart' in window || navigator.maxTouchPoints > 0;
1163
+ return isMobileUA || (isMobileWidth && hasTouch);
1164
+ };
1165
+ const optimizeConfigForMobile = (config) => {
1166
+ var _a, _b, _c;
1167
+ if (!isMobileDevice())
1168
+ return config;
1169
+ return Object.assign(Object.assign({}, config), {
1170
+ // Reduce particle count by 40%
1171
+ particleCount: Math.floor((config.particleCount || 50) * 0.6),
1172
+ // Reduce element size by 20%
1173
+ elementSize: Math.floor((config.elementSize || 20) * 0.8),
1174
+ // Reduce lifetime by 20%
1175
+ lifetime: Math.floor((config.lifetime || 150) * 0.8),
1176
+ // Simplify physics
1177
+ physics: Object.assign(Object.assign({}, config.physics), {
1178
+ // Reduce precision for mobile
1179
+ gravity: Math.round((((_a = config.physics) === null || _a === void 0 ? void 0 : _a.gravity) || 0) * 100) / 100, wind: Math.round((((_b = config.physics) === null || _b === void 0 ? void 0 : _b.wind) || 0) * 100) / 100, friction: Math.round((((_c = config.physics) === null || _c === void 0 ? void 0 : _c.friction) || 0.98) * 100) / 100 }) });
1180
+ };
1181
+ // Frame skipping for mobile
1182
+ const shouldSkipFrame = (frameCount) => {
1183
+ if (!isMobileDevice())
1184
+ return false;
1185
+ // Skip every 3rd frame on mobile
1186
+ return frameCount % 3 === 0;
2337
1187
  };
2338
1188
 
2339
1189
  const useReward = (elementId, animationType, config) => {
@@ -2342,6 +1192,15 @@ const useReward = (elementId, animationType, config) => {
2342
1192
  const particlesRef = useRef([]);
2343
1193
  const containerRef = useRef(null);
2344
1194
  const rootRef = useRef(null);
1195
+ const isTabVisible = useRef(true);
1196
+ // Monitor tab visibility
1197
+ useEffect(() => {
1198
+ const handleVisibilityChange = () => {
1199
+ isTabVisible.current = !document.hidden;
1200
+ };
1201
+ document.addEventListener('visibilitychange', handleVisibilityChange);
1202
+ return () => document.removeEventListener('visibilitychange', handleVisibilityChange);
1203
+ }, []);
2345
1204
  const animate = useCallback(() => {
2346
1205
  var _a, _b, _c, _d, _e, _f;
2347
1206
  const element = document.getElementById(elementId);
@@ -2357,8 +1216,10 @@ const useReward = (elementId, animationType, config) => {
2357
1216
  console.error(`Animation type "${animationType}" not found`);
2358
1217
  return;
2359
1218
  }
1219
+ // Apply mobile performance optimizations
1220
+ const optimizedConfig = config ? optimizeConfigForMobile(config) : undefined;
2360
1221
  // Create particles
2361
- particlesRef.current = animationHandler.createParticles(origin, config || {});
1222
+ particlesRef.current = animationHandler.createParticles(origin, optimizedConfig || {});
2362
1223
  // Create container
2363
1224
  const container = document.createElement('div');
2364
1225
  container.style.position = 'fixed';
@@ -2379,8 +1240,13 @@ const useReward = (elementId, animationType, config) => {
2379
1240
  const gravity = (_b = (_a = config === null || config === void 0 ? void 0 : config.physics) === null || _a === void 0 ? void 0 : _a.gravity) !== null && _b !== void 0 ? _b : defaultGravity;
2380
1241
  const friction = (_d = (_c = config === null || config === void 0 ? void 0 : config.physics) === null || _c === void 0 ? void 0 : _c.friction) !== null && _d !== void 0 ? _d : 0.98;
2381
1242
  const wind = (_f = (_e = config === null || config === void 0 ? void 0 : config.physics) === null || _e === void 0 ? void 0 : _e.wind) !== null && _f !== void 0 ? _f : 0;
1243
+ // Track frame count for mobile optimization
1244
+ let frameCount = 0;
2382
1245
  const updateParticles = () => {
2383
1246
  let activeParticles = 0;
1247
+ frameCount++;
1248
+ // Skip frame rendering on mobile to improve performance
1249
+ const skipFrame = shouldSkipFrame(frameCount);
2384
1250
  particlesRef.current = particlesRef.current.map((particle) => {
2385
1251
  if (particle.life <= 0)
2386
1252
  return particle;
@@ -2408,13 +1274,13 @@ const useReward = (elementId, animationType, config) => {
2408
1274
  }
2409
1275
  return particle;
2410
1276
  });
2411
- // Render particles
2412
- if (rootRef.current) {
1277
+ // Render particles (skip rendering on mobile for some frames)
1278
+ if (rootRef.current && !skipFrame) {
2413
1279
  rootRef.current.render(React.createElement(React.Fragment, null, particlesRef.current
2414
1280
  .filter((p) => p.life > 0)
2415
1281
  .map((particle) => (React.createElement("div", { key: particle.id, style: createParticleStyle(particle, containerRect) }, animationHandler.renderParticle(particle))))));
2416
1282
  }
2417
- if (activeParticles > 0) {
1283
+ if (activeParticles > 0 && isTabVisible.current) {
2418
1284
  animationFrameRef.current = requestAnimationFrame(updateParticles);
2419
1285
  }
2420
1286
  else {
@@ -2460,5 +1326,5 @@ const useReward = (elementId, animationType, config) => {
2460
1326
  return { reward, isAnimating };
2461
1327
  };
2462
1328
 
2463
- export { emojiPresets, useReward };
1329
+ export { emojiPresets, isMobileDevice, optimizeConfigForMobile, useReward };
2464
1330
  //# sourceMappingURL=index.esm.js.map