particle-network-bg 1.0.1 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -89,6 +89,8 @@ function App() {
89
89
  ```
90
90
 
91
91
  > **Note:** Config is applied on mount. The canvas resizes to the window. When gradients are enabled, a background `<div>` is automatically created behind the canvas for smooth CSS-based gradient rendering.
92
+ >
93
+ > Pulse animations are automatically phase-randomized so particles don't pulse in sync.
92
94
 
93
95
  ## Configuration
94
96
 
@@ -160,6 +162,7 @@ new ParticleNetwork(canvas, {
160
162
  secondaryHighlightPosition: "bottom-right",
161
163
  minRadius: 20, // min size for liquid glass particles (overrides root minRadius)
162
164
  maxRadius: 40, // max size for liquid glass particles (overrides root maxRadius)
165
+ blobSpeed: 1, // blob deformation speed multiplier (0 = frozen, 2 = double speed)
163
166
  },
164
167
  });
165
168
  ```
@@ -344,7 +347,7 @@ function App() {
344
347
  </ChildParticle>
345
348
 
346
349
  {/* Liquid glass blob particle */}
347
- <GlassChildParticle id="clock" x={600} y={400} radius={60}>
350
+ <GlassChildParticle id="clock" x={600} y={400} radius={60} glassColor="#ff6600" glassOpacity={0.8}>
348
351
  <span>🕐</span>
349
352
  </GlassChildParticle>
350
353
 
@@ -371,6 +374,9 @@ function App() {
371
374
  | `overflow` | string | `"hidden"` | CSS overflow for the child content container |
372
375
  | `anchorForce` | number | `0.05` | Spring force pulling back to anchor (0–1). Lower = more floaty |
373
376
  | `mouseInfluence` | number | `0.1` | Mouse influence multiplier (0–1). 0 = ignores mouse |
377
+ | `glassOpacity` | number | — | Opacity (0–1) for liquid glass. Overrides global `liquidGlass.opacity` |
378
+ | `glassColor` | string | — | Color (hex) for liquid glass. Overrides global `liquidGlass.color` |
379
+ | `liquidGlassConfig` | `Partial<LiquidGlassConfig>` | — | Full liquid glass config override, merged over global `liquidGlass` |
374
380
  | `children` | ReactNode | — | Content to render inside the particle |
375
381
  | `style` | CSSProperties | — | Style applied to the inner wrapper div |
376
382
  | `className` | string | — | Class applied to the inner wrapper div |
@@ -391,7 +397,9 @@ network.addChildParticle({
391
397
  radius: 50,
392
398
  anchorForce: 0.05,
393
399
  mouseInfluence: 0.1,
394
- liquidGlass: false,
400
+ liquidGlass: true,
401
+ glassOpacity: 0.7, // override global liquidGlass.opacity
402
+ glassColor: "#ff6600", // override global liquidGlass.color
395
403
  });
396
404
 
397
405
  // Update its anchor (e.g. on scroll/resize)
@@ -436,6 +444,9 @@ interface ChildParticleConfig {
436
444
  anchorForce?: number;
437
445
  mouseInfluence?: number;
438
446
  liquidGlass?: boolean;
447
+ glassOpacity?: number; // overrides global liquidGlass.opacity
448
+ glassColor?: string; // overrides global liquidGlass.color
449
+ liquidGlassConfig?: Partial<LiquidGlassConfig>; // merged over global liquidGlass
439
450
  }
440
451
 
441
452
  interface ChildParticlePosition {
@@ -43,6 +43,7 @@ var DEFAULT_LIQUID_GLASS = {
43
43
  shadowStrength: 0.4,
44
44
  secondaryReflection: 0.25,
45
45
  secondaryHighlightPosition: "bottom-right",
46
+ blobSpeed: 1,
46
47
  minRadius: 20,
47
48
  maxRadius: 40
48
49
  };
@@ -158,7 +159,7 @@ var ParticleNetwork = class {
158
159
  const h = isRect ? particle.height : (particle.currentRadius ?? particle.radius) * 2;
159
160
  el.style.width = w + "px";
160
161
  el.style.height = h + "px";
161
- el.style.transform = `translate(${px - w / 2}px, ${py - h / 2}px)`;
162
+ el.style.transform = `translate3d(${Math.round(px - w / 2)}px, ${Math.round(py - h / 2)}px, 0)`;
162
163
  if (isRect) {
163
164
  const br = particle.borderRadius ?? Math.min(w, h) / 2;
164
165
  el.style.borderRadius = br + "px";
@@ -314,7 +315,14 @@ var ParticleNetwork = class {
314
315
  if (!Array.isArray(config.particleTypes)) {
315
316
  throw new Error("particleTypes must be an array");
316
317
  }
317
- if (!config.assets || typeof config.assets !== "object") {
318
+ let hasAssetEntries = false;
319
+ for (const entry of config.particleTypes) {
320
+ if (entry?.type === "asset") {
321
+ hasAssetEntries = true;
322
+ break;
323
+ }
324
+ }
325
+ if (hasAssetEntries && (!config.assets || typeof config.assets !== "object")) {
318
326
  throw new Error("assets map is required when using particleTypes with asset entries");
319
327
  }
320
328
  for (let i = 0; i < config.particleTypes.length; i++) {
@@ -334,8 +342,9 @@ var ParticleNetwork = class {
334
342
  if (typeof pt.asset !== "string" || !pt.asset) {
335
343
  throw new Error(`particleTypes[${i}].asset must be a non-empty string`);
336
344
  }
337
- if (!config.assets[pt.asset]) {
338
- throw new Error(`particleTypes[${i}]: asset "${pt.asset}" not found in assets`);
345
+ const assetKey = pt.asset;
346
+ if (!config.assets?.[assetKey]) {
347
+ throw new Error(`particleTypes[${i}]: asset "${assetKey}" not found in assets`);
339
348
  }
340
349
  }
341
350
  }
@@ -433,6 +442,7 @@ var ParticleNetwork = class {
433
442
  delete p.assetId;
434
443
  delete p.liquidGlass;
435
444
  delete p.typeConfig;
445
+ delete p.pulsePhase;
436
446
  });
437
447
  if (particleTypes?.length) {
438
448
  const counts = [];
@@ -501,6 +511,14 @@ var ParticleNetwork = class {
501
511
  if (!particleTypes?.length && (this.config.liquidGlassPercentage != null || this.config.liquidGlassCount != null)) {
502
512
  this.assignLiquidGlass();
503
513
  }
514
+ this.particles.forEach((p) => {
515
+ if (p.isChild) return;
516
+ const tc = p.typeConfig;
517
+ const doPulse = tc?.pulse ?? this.config.pulseEnabled;
518
+ if (doPulse) {
519
+ p.pulsePhase = Math.random() * Math.PI * 2;
520
+ }
521
+ });
504
522
  this.assignMouseBehavior();
505
523
  }
506
524
  assignLiquidGlass() {
@@ -606,7 +624,8 @@ var ParticleNetwork = class {
606
624
  if (doPulse) {
607
625
  const speed = tc?.pulseSpeed ?? this.config.pulseSpeed;
608
626
  this.pulseAngle += speed;
609
- const pulseScale = Math.sin(this.pulseAngle) * 0.5 + 1;
627
+ const phase = particle.pulsePhase ?? 0;
628
+ const pulseScale = Math.sin(this.pulseAngle + phase) * 0.5 + 1;
610
629
  particle.currentRadius = particle.radius * pulseScale;
611
630
  } else {
612
631
  particle.currentRadius = particle.radius;
@@ -807,7 +826,20 @@ var ParticleNetwork = class {
807
826
  }
808
827
  draw3DFluidSphere(particle) {
809
828
  const tc = particle.typeConfig;
810
- const perTypeLg = tc && "liquidGlass" in tc && typeof tc.liquidGlass === "object" ? tc.liquidGlass : void 0;
829
+ let perTypeLg = tc && "liquidGlass" in tc && typeof tc.liquidGlass === "object" ? tc.liquidGlass : void 0;
830
+ if (particle.isChild && particle.childId) {
831
+ const childConfig = this.childParticleConfigs.get(particle.childId);
832
+ if (childConfig) {
833
+ const overrides = {
834
+ ...childConfig.liquidGlassConfig ?? {},
835
+ ...childConfig.glassOpacity != null && { opacity: childConfig.glassOpacity },
836
+ ...childConfig.glassColor != null && { color: childConfig.glassColor }
837
+ };
838
+ if (Object.keys(overrides).length > 0) {
839
+ perTypeLg = { ...perTypeLg ?? {}, ...overrides };
840
+ }
841
+ }
842
+ }
811
843
  const lg = perTypeLg ? { ...this.getLiquidGlassConfig(), ...perTypeLg } : this.getLiquidGlassConfig();
812
844
  const r = particle.currentRadius ?? particle.radius;
813
845
  if (r <= 0) return;
@@ -816,46 +848,60 @@ var ParticleNetwork = class {
816
848
  const cx = particle.x;
817
849
  const cy = particle.y;
818
850
  const isRect = particle.width != null && particle.height != null;
819
- let opacity = (lg.opacity ?? DEFAULT_LIQUID_GLASS.opacity) * this.config.particleOpacity;
820
- if (this.config.depthEffectEnabled) {
821
- opacity *= 0.6 + 0.4 * particle.z;
851
+ const hasExplicitChildOpacity = particle.isChild && particle.childId && (() => {
852
+ const cfg = this.childParticleConfigs.get(particle.childId);
853
+ return cfg?.glassOpacity != null || cfg?.liquidGlassConfig?.opacity != null;
854
+ })();
855
+ let opacity = lg.opacity ?? DEFAULT_LIQUID_GLASS.opacity;
856
+ if (!hasExplicitChildOpacity) {
857
+ opacity *= this.config.particleOpacity;
858
+ if (this.config.depthEffectEnabled) {
859
+ opacity *= 0.6 + 0.4 * particle.z;
860
+ }
822
861
  }
823
862
  if (opacity <= 0) return;
824
- blob.rotation += blob.rotSpeed;
863
+ const blobSpeed = lg.blobSpeed ?? 1;
864
+ blob.rotation += blob.rotSpeed * blobSpeed;
825
865
  for (let m = 0; m < blob.phases.length; m++) {
826
- blob.phases[m] += blob.phaseSpeeds[m];
866
+ blob.phases[m] += blob.phaseSpeeds[m] * blobSpeed;
827
867
  }
828
- blob.hlAngle += blob.hlAngleSpeed;
868
+ blob.hlAngle += blob.hlAngleSpeed * blobSpeed;
829
869
  this.updateBlobMouse(particle);
830
870
  const color = lg.color ?? DEFAULT_LIQUID_GLASS.color;
831
871
  const baseR = parseInt(color.slice(1, 3), 16);
832
872
  const baseG = parseInt(color.slice(3, 5), 16);
833
873
  const baseB = parseInt(color.slice(5, 7), 16);
834
874
  const shadowStr = lg.shadowStrength ?? DEFAULT_LIQUID_GLASS.shadowStrength;
875
+ const refl = (lg.reflectionStrength ?? DEFAULT_LIQUID_GLASS.reflectionStrength) / DEFAULT_LIQUID_GLASS.reflectionStrength;
835
876
  const gradR = isRect ? Math.max(particle.width, particle.height) / 2 : r;
836
877
  const hlDist = gradR * 0.35;
837
878
  const hlX = cx + Math.cos(blob.hlAngle) * hlDist;
838
879
  const hlY = cy + Math.sin(blob.hlAngle) * hlDist;
839
- const lr = Math.min(255, baseR + Math.round((255 - baseR) * 0.55));
840
- const lgr = Math.min(255, baseG + Math.round((255 - baseG) * 0.55));
841
- const lb = Math.min(255, baseB + Math.round((255 - baseB) * 0.55));
880
+ const lr = Math.min(255, baseR + Math.round((255 - baseR) * 0.55 * refl));
881
+ const lgr = Math.min(255, baseG + Math.round((255 - baseG) * 0.55 * refl));
882
+ const lb = Math.min(255, baseB + Math.round((255 - baseB) * 0.55 * refl));
842
883
  const dr = Math.max(0, Math.round(baseR * (1 - shadowStr * 0.35)));
843
884
  const dg = Math.max(0, Math.round(baseG * (1 - shadowStr * 0.35)));
844
885
  const db = Math.max(0, Math.round(baseB * (1 - shadowStr * 0.35)));
845
886
  this.ctx.save();
846
- const grad = this.ctx.createRadialGradient(
847
- hlX,
848
- hlY,
849
- gradR * 0.05,
850
- cx,
851
- cy,
852
- gradR * 1.05
853
- );
854
- grad.addColorStop(0, `rgba(${lr},${lgr},${lb}, ${opacity * 0.95})`);
855
- grad.addColorStop(0.4, `rgba(${baseR},${baseG},${baseB}, ${opacity * 0.8})`);
856
- grad.addColorStop(0.8, `rgba(${dr},${dg},${db}, ${opacity * 0.6})`);
857
- grad.addColorStop(1, `rgba(${dr},${dg},${db}, ${opacity * 0.25})`);
858
- this.ctx.fillStyle = grad;
887
+ this.ctx.globalAlpha = 1;
888
+ if (hasExplicitChildOpacity && opacity >= 0.99) {
889
+ this.ctx.fillStyle = `rgba(${baseR},${baseG},${baseB}, ${opacity})`;
890
+ } else {
891
+ const grad = this.ctx.createRadialGradient(
892
+ hlX,
893
+ hlY,
894
+ gradR * 0.05,
895
+ cx,
896
+ cy,
897
+ gradR * 1.05
898
+ );
899
+ grad.addColorStop(0, `rgba(${lr},${lgr},${lb}, ${opacity * 0.95})`);
900
+ grad.addColorStop(0.4, `rgba(${baseR},${baseG},${baseB}, ${opacity * 0.8})`);
901
+ grad.addColorStop(0.8, `rgba(${dr},${dg},${db}, ${opacity * 0.6})`);
902
+ grad.addColorStop(1, `rgba(${dr},${dg},${db}, ${opacity * 0.25})`);
903
+ this.ctx.fillStyle = grad;
904
+ }
859
905
  if (isRect) {
860
906
  const halfW = particle.width / 2;
861
907
  const halfH = particle.height / 2;
package/dist/index.d.mts CHANGED
@@ -38,6 +38,8 @@ interface LiquidGlassConfig {
38
38
  minRadius?: number;
39
39
  /** Maximum radius (px) for liquid glass particles (overrides root maxRadius). */
40
40
  maxRadius?: number;
41
+ /** Blob deformation speed multiplier (default 1). 0 = frozen, 2 = double speed. */
42
+ blobSpeed?: number;
41
43
  }
42
44
  interface CircleTypeEntry {
43
45
  type: "circle";
@@ -94,6 +96,12 @@ interface ChildParticleConfig {
94
96
  mouseInfluence?: number;
95
97
  /** Render as liquid glass particle. Default false. */
96
98
  liquidGlass?: boolean;
99
+ /** Opacity (0–1) for liquid glass child particles. Overrides global liquidGlass.opacity. */
100
+ glassOpacity?: number;
101
+ /** Color (hex) for liquid glass child particles. Overrides global liquidGlass.color. */
102
+ glassColor?: string;
103
+ /** Full liquid glass config override for child particles. Merged over global liquidGlass. */
104
+ liquidGlassConfig?: Partial<LiquidGlassConfig>;
97
105
  }
98
106
  /** Emitted position data for a child particle each frame. */
99
107
  interface ChildParticlePosition {
@@ -208,6 +216,8 @@ interface Particle {
208
216
  overflow?: string;
209
217
  /** Per-type config entry (shallow overrides root config). */
210
218
  typeConfig?: ParticleTypeEntry;
219
+ /** Random phase offset (0–2π) for pulse animation. Makes particles pulse out of sync. */
220
+ pulsePhase?: number;
211
221
  /** Smoothed x for overlay positioning. */
212
222
  smoothX?: number;
213
223
  /** Smoothed y for overlay positioning. */
package/dist/index.d.ts CHANGED
@@ -38,6 +38,8 @@ interface LiquidGlassConfig {
38
38
  minRadius?: number;
39
39
  /** Maximum radius (px) for liquid glass particles (overrides root maxRadius). */
40
40
  maxRadius?: number;
41
+ /** Blob deformation speed multiplier (default 1). 0 = frozen, 2 = double speed. */
42
+ blobSpeed?: number;
41
43
  }
42
44
  interface CircleTypeEntry {
43
45
  type: "circle";
@@ -94,6 +96,12 @@ interface ChildParticleConfig {
94
96
  mouseInfluence?: number;
95
97
  /** Render as liquid glass particle. Default false. */
96
98
  liquidGlass?: boolean;
99
+ /** Opacity (0–1) for liquid glass child particles. Overrides global liquidGlass.opacity. */
100
+ glassOpacity?: number;
101
+ /** Color (hex) for liquid glass child particles. Overrides global liquidGlass.color. */
102
+ glassColor?: string;
103
+ /** Full liquid glass config override for child particles. Merged over global liquidGlass. */
104
+ liquidGlassConfig?: Partial<LiquidGlassConfig>;
97
105
  }
98
106
  /** Emitted position data for a child particle each frame. */
99
107
  interface ChildParticlePosition {
@@ -208,6 +216,8 @@ interface Particle {
208
216
  overflow?: string;
209
217
  /** Per-type config entry (shallow overrides root config). */
210
218
  typeConfig?: ParticleTypeEntry;
219
+ /** Random phase offset (0–2π) for pulse animation. Makes particles pulse out of sync. */
220
+ pulsePhase?: number;
211
221
  /** Smoothed x for overlay positioning. */
212
222
  smoothX?: number;
213
223
  /** Smoothed y for overlay positioning. */
package/dist/index.js CHANGED
@@ -67,6 +67,7 @@ var DEFAULT_LIQUID_GLASS = {
67
67
  shadowStrength: 0.4,
68
68
  secondaryReflection: 0.25,
69
69
  secondaryHighlightPosition: "bottom-right",
70
+ blobSpeed: 1,
70
71
  minRadius: 20,
71
72
  maxRadius: 40
72
73
  };
@@ -182,7 +183,7 @@ var ParticleNetwork = class {
182
183
  const h = isRect ? particle.height : (particle.currentRadius ?? particle.radius) * 2;
183
184
  el.style.width = w + "px";
184
185
  el.style.height = h + "px";
185
- el.style.transform = `translate(${px - w / 2}px, ${py - h / 2}px)`;
186
+ el.style.transform = `translate3d(${Math.round(px - w / 2)}px, ${Math.round(py - h / 2)}px, 0)`;
186
187
  if (isRect) {
187
188
  const br = particle.borderRadius ?? Math.min(w, h) / 2;
188
189
  el.style.borderRadius = br + "px";
@@ -338,7 +339,14 @@ var ParticleNetwork = class {
338
339
  if (!Array.isArray(config.particleTypes)) {
339
340
  throw new Error("particleTypes must be an array");
340
341
  }
341
- if (!config.assets || typeof config.assets !== "object") {
342
+ let hasAssetEntries = false;
343
+ for (const entry of config.particleTypes) {
344
+ if (entry?.type === "asset") {
345
+ hasAssetEntries = true;
346
+ break;
347
+ }
348
+ }
349
+ if (hasAssetEntries && (!config.assets || typeof config.assets !== "object")) {
342
350
  throw new Error("assets map is required when using particleTypes with asset entries");
343
351
  }
344
352
  for (let i = 0; i < config.particleTypes.length; i++) {
@@ -358,8 +366,9 @@ var ParticleNetwork = class {
358
366
  if (typeof pt.asset !== "string" || !pt.asset) {
359
367
  throw new Error(`particleTypes[${i}].asset must be a non-empty string`);
360
368
  }
361
- if (!config.assets[pt.asset]) {
362
- throw new Error(`particleTypes[${i}]: asset "${pt.asset}" not found in assets`);
369
+ const assetKey = pt.asset;
370
+ if (!config.assets?.[assetKey]) {
371
+ throw new Error(`particleTypes[${i}]: asset "${assetKey}" not found in assets`);
363
372
  }
364
373
  }
365
374
  }
@@ -457,6 +466,7 @@ var ParticleNetwork = class {
457
466
  delete p.assetId;
458
467
  delete p.liquidGlass;
459
468
  delete p.typeConfig;
469
+ delete p.pulsePhase;
460
470
  });
461
471
  if (particleTypes?.length) {
462
472
  const counts = [];
@@ -525,6 +535,14 @@ var ParticleNetwork = class {
525
535
  if (!particleTypes?.length && (this.config.liquidGlassPercentage != null || this.config.liquidGlassCount != null)) {
526
536
  this.assignLiquidGlass();
527
537
  }
538
+ this.particles.forEach((p) => {
539
+ if (p.isChild) return;
540
+ const tc = p.typeConfig;
541
+ const doPulse = tc?.pulse ?? this.config.pulseEnabled;
542
+ if (doPulse) {
543
+ p.pulsePhase = Math.random() * Math.PI * 2;
544
+ }
545
+ });
528
546
  this.assignMouseBehavior();
529
547
  }
530
548
  assignLiquidGlass() {
@@ -630,7 +648,8 @@ var ParticleNetwork = class {
630
648
  if (doPulse) {
631
649
  const speed = tc?.pulseSpeed ?? this.config.pulseSpeed;
632
650
  this.pulseAngle += speed;
633
- const pulseScale = Math.sin(this.pulseAngle) * 0.5 + 1;
651
+ const phase = particle.pulsePhase ?? 0;
652
+ const pulseScale = Math.sin(this.pulseAngle + phase) * 0.5 + 1;
634
653
  particle.currentRadius = particle.radius * pulseScale;
635
654
  } else {
636
655
  particle.currentRadius = particle.radius;
@@ -831,7 +850,20 @@ var ParticleNetwork = class {
831
850
  }
832
851
  draw3DFluidSphere(particle) {
833
852
  const tc = particle.typeConfig;
834
- const perTypeLg = tc && "liquidGlass" in tc && typeof tc.liquidGlass === "object" ? tc.liquidGlass : void 0;
853
+ let perTypeLg = tc && "liquidGlass" in tc && typeof tc.liquidGlass === "object" ? tc.liquidGlass : void 0;
854
+ if (particle.isChild && particle.childId) {
855
+ const childConfig = this.childParticleConfigs.get(particle.childId);
856
+ if (childConfig) {
857
+ const overrides = {
858
+ ...childConfig.liquidGlassConfig ?? {},
859
+ ...childConfig.glassOpacity != null && { opacity: childConfig.glassOpacity },
860
+ ...childConfig.glassColor != null && { color: childConfig.glassColor }
861
+ };
862
+ if (Object.keys(overrides).length > 0) {
863
+ perTypeLg = { ...perTypeLg ?? {}, ...overrides };
864
+ }
865
+ }
866
+ }
835
867
  const lg = perTypeLg ? { ...this.getLiquidGlassConfig(), ...perTypeLg } : this.getLiquidGlassConfig();
836
868
  const r = particle.currentRadius ?? particle.radius;
837
869
  if (r <= 0) return;
@@ -840,46 +872,60 @@ var ParticleNetwork = class {
840
872
  const cx = particle.x;
841
873
  const cy = particle.y;
842
874
  const isRect = particle.width != null && particle.height != null;
843
- let opacity = (lg.opacity ?? DEFAULT_LIQUID_GLASS.opacity) * this.config.particleOpacity;
844
- if (this.config.depthEffectEnabled) {
845
- opacity *= 0.6 + 0.4 * particle.z;
875
+ const hasExplicitChildOpacity = particle.isChild && particle.childId && (() => {
876
+ const cfg = this.childParticleConfigs.get(particle.childId);
877
+ return cfg?.glassOpacity != null || cfg?.liquidGlassConfig?.opacity != null;
878
+ })();
879
+ let opacity = lg.opacity ?? DEFAULT_LIQUID_GLASS.opacity;
880
+ if (!hasExplicitChildOpacity) {
881
+ opacity *= this.config.particleOpacity;
882
+ if (this.config.depthEffectEnabled) {
883
+ opacity *= 0.6 + 0.4 * particle.z;
884
+ }
846
885
  }
847
886
  if (opacity <= 0) return;
848
- blob.rotation += blob.rotSpeed;
887
+ const blobSpeed = lg.blobSpeed ?? 1;
888
+ blob.rotation += blob.rotSpeed * blobSpeed;
849
889
  for (let m = 0; m < blob.phases.length; m++) {
850
- blob.phases[m] += blob.phaseSpeeds[m];
890
+ blob.phases[m] += blob.phaseSpeeds[m] * blobSpeed;
851
891
  }
852
- blob.hlAngle += blob.hlAngleSpeed;
892
+ blob.hlAngle += blob.hlAngleSpeed * blobSpeed;
853
893
  this.updateBlobMouse(particle);
854
894
  const color = lg.color ?? DEFAULT_LIQUID_GLASS.color;
855
895
  const baseR = parseInt(color.slice(1, 3), 16);
856
896
  const baseG = parseInt(color.slice(3, 5), 16);
857
897
  const baseB = parseInt(color.slice(5, 7), 16);
858
898
  const shadowStr = lg.shadowStrength ?? DEFAULT_LIQUID_GLASS.shadowStrength;
899
+ const refl = (lg.reflectionStrength ?? DEFAULT_LIQUID_GLASS.reflectionStrength) / DEFAULT_LIQUID_GLASS.reflectionStrength;
859
900
  const gradR = isRect ? Math.max(particle.width, particle.height) / 2 : r;
860
901
  const hlDist = gradR * 0.35;
861
902
  const hlX = cx + Math.cos(blob.hlAngle) * hlDist;
862
903
  const hlY = cy + Math.sin(blob.hlAngle) * hlDist;
863
- const lr = Math.min(255, baseR + Math.round((255 - baseR) * 0.55));
864
- const lgr = Math.min(255, baseG + Math.round((255 - baseG) * 0.55));
865
- const lb = Math.min(255, baseB + Math.round((255 - baseB) * 0.55));
904
+ const lr = Math.min(255, baseR + Math.round((255 - baseR) * 0.55 * refl));
905
+ const lgr = Math.min(255, baseG + Math.round((255 - baseG) * 0.55 * refl));
906
+ const lb = Math.min(255, baseB + Math.round((255 - baseB) * 0.55 * refl));
866
907
  const dr = Math.max(0, Math.round(baseR * (1 - shadowStr * 0.35)));
867
908
  const dg = Math.max(0, Math.round(baseG * (1 - shadowStr * 0.35)));
868
909
  const db = Math.max(0, Math.round(baseB * (1 - shadowStr * 0.35)));
869
910
  this.ctx.save();
870
- const grad = this.ctx.createRadialGradient(
871
- hlX,
872
- hlY,
873
- gradR * 0.05,
874
- cx,
875
- cy,
876
- gradR * 1.05
877
- );
878
- grad.addColorStop(0, `rgba(${lr},${lgr},${lb}, ${opacity * 0.95})`);
879
- grad.addColorStop(0.4, `rgba(${baseR},${baseG},${baseB}, ${opacity * 0.8})`);
880
- grad.addColorStop(0.8, `rgba(${dr},${dg},${db}, ${opacity * 0.6})`);
881
- grad.addColorStop(1, `rgba(${dr},${dg},${db}, ${opacity * 0.25})`);
882
- this.ctx.fillStyle = grad;
911
+ this.ctx.globalAlpha = 1;
912
+ if (hasExplicitChildOpacity && opacity >= 0.99) {
913
+ this.ctx.fillStyle = `rgba(${baseR},${baseG},${baseB}, ${opacity})`;
914
+ } else {
915
+ const grad = this.ctx.createRadialGradient(
916
+ hlX,
917
+ hlY,
918
+ gradR * 0.05,
919
+ cx,
920
+ cy,
921
+ gradR * 1.05
922
+ );
923
+ grad.addColorStop(0, `rgba(${lr},${lgr},${lb}, ${opacity * 0.95})`);
924
+ grad.addColorStop(0.4, `rgba(${baseR},${baseG},${baseB}, ${opacity * 0.8})`);
925
+ grad.addColorStop(0.8, `rgba(${dr},${dg},${db}, ${opacity * 0.6})`);
926
+ grad.addColorStop(1, `rgba(${dr},${dg},${db}, ${opacity * 0.25})`);
927
+ this.ctx.fillStyle = grad;
928
+ }
883
929
  if (isRect) {
884
930
  const halfW = particle.width / 2;
885
931
  const halfH = particle.height / 2;
package/dist/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  ParticleNetwork
3
- } from "./chunk-BMXQY2SK.mjs";
3
+ } from "./chunk-JZTYQ6XI.mjs";
4
4
  export {
5
5
  ParticleNetwork
6
6
  };
package/dist/react.d.mts CHANGED
@@ -1,7 +1,9 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import * as react from 'react';
2
3
  import { ReactNode, CSSProperties, RefObject } from 'react';
3
- import { ParticleNetworkConfig } from './index.mjs';
4
+ import { LiquidGlassConfig, ParticleNetworkConfig, ParticleNetwork } from './index.mjs';
4
5
 
6
+ declare const ParticleNetworkContext: react.Context<ParticleNetwork | null>;
5
7
  declare function useParticleNetwork(config?: Partial<ParticleNetworkConfig>): RefObject<HTMLCanvasElement | null>;
6
8
  interface ParticleNetworkBgProps {
7
9
  config?: Partial<ParticleNetworkConfig>;
@@ -27,6 +29,12 @@ interface ChildParticleProps {
27
29
  anchorForce?: number;
28
30
  /** Mouse influence multiplier (0-1). Default 0.1. */
29
31
  mouseInfluence?: number;
32
+ /** Opacity (0-1) for liquid glass. Only applies when liquidGlass is true. */
33
+ glassOpacity?: number;
34
+ /** Color (hex) for liquid glass. Only applies when liquidGlass is true. */
35
+ glassColor?: string;
36
+ /** Full liquid glass config override. Merged over global liquidGlass. */
37
+ liquidGlassConfig?: Partial<LiquidGlassConfig>;
30
38
  children?: ReactNode;
31
39
  style?: CSSProperties;
32
40
  className?: string;
@@ -49,10 +57,16 @@ interface GlassChildParticleProps {
49
57
  anchorForce?: number;
50
58
  /** Mouse influence multiplier (0-1). Default 0.1. */
51
59
  mouseInfluence?: number;
60
+ /** Opacity (0-1) for the glass background. Default uses global liquidGlass.opacity. */
61
+ glassOpacity?: number;
62
+ /** Color (hex) for the glass background. Default uses global liquidGlass.color. */
63
+ glassColor?: string;
64
+ /** Full liquid glass config override. Merged over global liquidGlass. */
65
+ liquidGlassConfig?: Partial<LiquidGlassConfig>;
52
66
  children?: ReactNode;
53
67
  style?: CSSProperties;
54
68
  className?: string;
55
69
  }
56
70
  declare function GlassChildParticle(props: GlassChildParticleProps): react_jsx_runtime.JSX.Element;
57
71
 
58
- export { ChildParticle, type ChildParticleProps, GlassChildParticle, type GlassChildParticleProps, ParticleNetworkBg, type ParticleNetworkBgProps, useParticleNetwork };
72
+ export { ChildParticle, type ChildParticleProps, GlassChildParticle, type GlassChildParticleProps, ParticleNetworkBg, type ParticleNetworkBgProps, ParticleNetworkContext, useParticleNetwork };
package/dist/react.d.ts CHANGED
@@ -1,7 +1,9 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import * as react from 'react';
2
3
  import { ReactNode, CSSProperties, RefObject } from 'react';
3
- import { ParticleNetworkConfig } from './index.js';
4
+ import { LiquidGlassConfig, ParticleNetworkConfig, ParticleNetwork } from './index.js';
4
5
 
6
+ declare const ParticleNetworkContext: react.Context<ParticleNetwork | null>;
5
7
  declare function useParticleNetwork(config?: Partial<ParticleNetworkConfig>): RefObject<HTMLCanvasElement | null>;
6
8
  interface ParticleNetworkBgProps {
7
9
  config?: Partial<ParticleNetworkConfig>;
@@ -27,6 +29,12 @@ interface ChildParticleProps {
27
29
  anchorForce?: number;
28
30
  /** Mouse influence multiplier (0-1). Default 0.1. */
29
31
  mouseInfluence?: number;
32
+ /** Opacity (0-1) for liquid glass. Only applies when liquidGlass is true. */
33
+ glassOpacity?: number;
34
+ /** Color (hex) for liquid glass. Only applies when liquidGlass is true. */
35
+ glassColor?: string;
36
+ /** Full liquid glass config override. Merged over global liquidGlass. */
37
+ liquidGlassConfig?: Partial<LiquidGlassConfig>;
30
38
  children?: ReactNode;
31
39
  style?: CSSProperties;
32
40
  className?: string;
@@ -49,10 +57,16 @@ interface GlassChildParticleProps {
49
57
  anchorForce?: number;
50
58
  /** Mouse influence multiplier (0-1). Default 0.1. */
51
59
  mouseInfluence?: number;
60
+ /** Opacity (0-1) for the glass background. Default uses global liquidGlass.opacity. */
61
+ glassOpacity?: number;
62
+ /** Color (hex) for the glass background. Default uses global liquidGlass.color. */
63
+ glassColor?: string;
64
+ /** Full liquid glass config override. Merged over global liquidGlass. */
65
+ liquidGlassConfig?: Partial<LiquidGlassConfig>;
52
66
  children?: ReactNode;
53
67
  style?: CSSProperties;
54
68
  className?: string;
55
69
  }
56
70
  declare function GlassChildParticle(props: GlassChildParticleProps): react_jsx_runtime.JSX.Element;
57
71
 
58
- export { ChildParticle, type ChildParticleProps, GlassChildParticle, type GlassChildParticleProps, ParticleNetworkBg, type ParticleNetworkBgProps, useParticleNetwork };
72
+ export { ChildParticle, type ChildParticleProps, GlassChildParticle, type GlassChildParticleProps, ParticleNetworkBg, type ParticleNetworkBgProps, ParticleNetworkContext, useParticleNetwork };
package/dist/react.js CHANGED
@@ -23,6 +23,7 @@ __export(react_exports, {
23
23
  ChildParticle: () => ChildParticle,
24
24
  GlassChildParticle: () => GlassChildParticle,
25
25
  ParticleNetworkBg: () => ParticleNetworkBg,
26
+ ParticleNetworkContext: () => ParticleNetworkContext,
26
27
  useParticleNetwork: () => useParticleNetwork
27
28
  });
28
29
  module.exports = __toCommonJS(react_exports);
@@ -74,6 +75,7 @@ var DEFAULT_LIQUID_GLASS = {
74
75
  shadowStrength: 0.4,
75
76
  secondaryReflection: 0.25,
76
77
  secondaryHighlightPosition: "bottom-right",
78
+ blobSpeed: 1,
77
79
  minRadius: 20,
78
80
  maxRadius: 40
79
81
  };
@@ -189,7 +191,7 @@ var ParticleNetwork = class {
189
191
  const h = isRect ? particle.height : (particle.currentRadius ?? particle.radius) * 2;
190
192
  el.style.width = w + "px";
191
193
  el.style.height = h + "px";
192
- el.style.transform = `translate(${px - w / 2}px, ${py - h / 2}px)`;
194
+ el.style.transform = `translate3d(${Math.round(px - w / 2)}px, ${Math.round(py - h / 2)}px, 0)`;
193
195
  if (isRect) {
194
196
  const br = particle.borderRadius ?? Math.min(w, h) / 2;
195
197
  el.style.borderRadius = br + "px";
@@ -345,7 +347,14 @@ var ParticleNetwork = class {
345
347
  if (!Array.isArray(config.particleTypes)) {
346
348
  throw new Error("particleTypes must be an array");
347
349
  }
348
- if (!config.assets || typeof config.assets !== "object") {
350
+ let hasAssetEntries = false;
351
+ for (const entry of config.particleTypes) {
352
+ if (entry?.type === "asset") {
353
+ hasAssetEntries = true;
354
+ break;
355
+ }
356
+ }
357
+ if (hasAssetEntries && (!config.assets || typeof config.assets !== "object")) {
349
358
  throw new Error("assets map is required when using particleTypes with asset entries");
350
359
  }
351
360
  for (let i = 0; i < config.particleTypes.length; i++) {
@@ -365,8 +374,9 @@ var ParticleNetwork = class {
365
374
  if (typeof pt.asset !== "string" || !pt.asset) {
366
375
  throw new Error(`particleTypes[${i}].asset must be a non-empty string`);
367
376
  }
368
- if (!config.assets[pt.asset]) {
369
- throw new Error(`particleTypes[${i}]: asset "${pt.asset}" not found in assets`);
377
+ const assetKey = pt.asset;
378
+ if (!config.assets?.[assetKey]) {
379
+ throw new Error(`particleTypes[${i}]: asset "${assetKey}" not found in assets`);
370
380
  }
371
381
  }
372
382
  }
@@ -464,6 +474,7 @@ var ParticleNetwork = class {
464
474
  delete p.assetId;
465
475
  delete p.liquidGlass;
466
476
  delete p.typeConfig;
477
+ delete p.pulsePhase;
467
478
  });
468
479
  if (particleTypes?.length) {
469
480
  const counts = [];
@@ -532,6 +543,14 @@ var ParticleNetwork = class {
532
543
  if (!particleTypes?.length && (this.config.liquidGlassPercentage != null || this.config.liquidGlassCount != null)) {
533
544
  this.assignLiquidGlass();
534
545
  }
546
+ this.particles.forEach((p) => {
547
+ if (p.isChild) return;
548
+ const tc = p.typeConfig;
549
+ const doPulse = tc?.pulse ?? this.config.pulseEnabled;
550
+ if (doPulse) {
551
+ p.pulsePhase = Math.random() * Math.PI * 2;
552
+ }
553
+ });
535
554
  this.assignMouseBehavior();
536
555
  }
537
556
  assignLiquidGlass() {
@@ -637,7 +656,8 @@ var ParticleNetwork = class {
637
656
  if (doPulse) {
638
657
  const speed = tc?.pulseSpeed ?? this.config.pulseSpeed;
639
658
  this.pulseAngle += speed;
640
- const pulseScale = Math.sin(this.pulseAngle) * 0.5 + 1;
659
+ const phase = particle.pulsePhase ?? 0;
660
+ const pulseScale = Math.sin(this.pulseAngle + phase) * 0.5 + 1;
641
661
  particle.currentRadius = particle.radius * pulseScale;
642
662
  } else {
643
663
  particle.currentRadius = particle.radius;
@@ -838,7 +858,20 @@ var ParticleNetwork = class {
838
858
  }
839
859
  draw3DFluidSphere(particle) {
840
860
  const tc = particle.typeConfig;
841
- const perTypeLg = tc && "liquidGlass" in tc && typeof tc.liquidGlass === "object" ? tc.liquidGlass : void 0;
861
+ let perTypeLg = tc && "liquidGlass" in tc && typeof tc.liquidGlass === "object" ? tc.liquidGlass : void 0;
862
+ if (particle.isChild && particle.childId) {
863
+ const childConfig = this.childParticleConfigs.get(particle.childId);
864
+ if (childConfig) {
865
+ const overrides = {
866
+ ...childConfig.liquidGlassConfig ?? {},
867
+ ...childConfig.glassOpacity != null && { opacity: childConfig.glassOpacity },
868
+ ...childConfig.glassColor != null && { color: childConfig.glassColor }
869
+ };
870
+ if (Object.keys(overrides).length > 0) {
871
+ perTypeLg = { ...perTypeLg ?? {}, ...overrides };
872
+ }
873
+ }
874
+ }
842
875
  const lg = perTypeLg ? { ...this.getLiquidGlassConfig(), ...perTypeLg } : this.getLiquidGlassConfig();
843
876
  const r = particle.currentRadius ?? particle.radius;
844
877
  if (r <= 0) return;
@@ -847,46 +880,60 @@ var ParticleNetwork = class {
847
880
  const cx = particle.x;
848
881
  const cy = particle.y;
849
882
  const isRect = particle.width != null && particle.height != null;
850
- let opacity = (lg.opacity ?? DEFAULT_LIQUID_GLASS.opacity) * this.config.particleOpacity;
851
- if (this.config.depthEffectEnabled) {
852
- opacity *= 0.6 + 0.4 * particle.z;
883
+ const hasExplicitChildOpacity = particle.isChild && particle.childId && (() => {
884
+ const cfg = this.childParticleConfigs.get(particle.childId);
885
+ return cfg?.glassOpacity != null || cfg?.liquidGlassConfig?.opacity != null;
886
+ })();
887
+ let opacity = lg.opacity ?? DEFAULT_LIQUID_GLASS.opacity;
888
+ if (!hasExplicitChildOpacity) {
889
+ opacity *= this.config.particleOpacity;
890
+ if (this.config.depthEffectEnabled) {
891
+ opacity *= 0.6 + 0.4 * particle.z;
892
+ }
853
893
  }
854
894
  if (opacity <= 0) return;
855
- blob.rotation += blob.rotSpeed;
895
+ const blobSpeed = lg.blobSpeed ?? 1;
896
+ blob.rotation += blob.rotSpeed * blobSpeed;
856
897
  for (let m = 0; m < blob.phases.length; m++) {
857
- blob.phases[m] += blob.phaseSpeeds[m];
898
+ blob.phases[m] += blob.phaseSpeeds[m] * blobSpeed;
858
899
  }
859
- blob.hlAngle += blob.hlAngleSpeed;
900
+ blob.hlAngle += blob.hlAngleSpeed * blobSpeed;
860
901
  this.updateBlobMouse(particle);
861
902
  const color = lg.color ?? DEFAULT_LIQUID_GLASS.color;
862
903
  const baseR = parseInt(color.slice(1, 3), 16);
863
904
  const baseG = parseInt(color.slice(3, 5), 16);
864
905
  const baseB = parseInt(color.slice(5, 7), 16);
865
906
  const shadowStr = lg.shadowStrength ?? DEFAULT_LIQUID_GLASS.shadowStrength;
907
+ const refl = (lg.reflectionStrength ?? DEFAULT_LIQUID_GLASS.reflectionStrength) / DEFAULT_LIQUID_GLASS.reflectionStrength;
866
908
  const gradR = isRect ? Math.max(particle.width, particle.height) / 2 : r;
867
909
  const hlDist = gradR * 0.35;
868
910
  const hlX = cx + Math.cos(blob.hlAngle) * hlDist;
869
911
  const hlY = cy + Math.sin(blob.hlAngle) * hlDist;
870
- const lr = Math.min(255, baseR + Math.round((255 - baseR) * 0.55));
871
- const lgr = Math.min(255, baseG + Math.round((255 - baseG) * 0.55));
872
- const lb = Math.min(255, baseB + Math.round((255 - baseB) * 0.55));
912
+ const lr = Math.min(255, baseR + Math.round((255 - baseR) * 0.55 * refl));
913
+ const lgr = Math.min(255, baseG + Math.round((255 - baseG) * 0.55 * refl));
914
+ const lb = Math.min(255, baseB + Math.round((255 - baseB) * 0.55 * refl));
873
915
  const dr = Math.max(0, Math.round(baseR * (1 - shadowStr * 0.35)));
874
916
  const dg = Math.max(0, Math.round(baseG * (1 - shadowStr * 0.35)));
875
917
  const db = Math.max(0, Math.round(baseB * (1 - shadowStr * 0.35)));
876
918
  this.ctx.save();
877
- const grad = this.ctx.createRadialGradient(
878
- hlX,
879
- hlY,
880
- gradR * 0.05,
881
- cx,
882
- cy,
883
- gradR * 1.05
884
- );
885
- grad.addColorStop(0, `rgba(${lr},${lgr},${lb}, ${opacity * 0.95})`);
886
- grad.addColorStop(0.4, `rgba(${baseR},${baseG},${baseB}, ${opacity * 0.8})`);
887
- grad.addColorStop(0.8, `rgba(${dr},${dg},${db}, ${opacity * 0.6})`);
888
- grad.addColorStop(1, `rgba(${dr},${dg},${db}, ${opacity * 0.25})`);
889
- this.ctx.fillStyle = grad;
919
+ this.ctx.globalAlpha = 1;
920
+ if (hasExplicitChildOpacity && opacity >= 0.99) {
921
+ this.ctx.fillStyle = `rgba(${baseR},${baseG},${baseB}, ${opacity})`;
922
+ } else {
923
+ const grad = this.ctx.createRadialGradient(
924
+ hlX,
925
+ hlY,
926
+ gradR * 0.05,
927
+ cx,
928
+ cy,
929
+ gradR * 1.05
930
+ );
931
+ grad.addColorStop(0, `rgba(${lr},${lgr},${lb}, ${opacity * 0.95})`);
932
+ grad.addColorStop(0.4, `rgba(${baseR},${baseG},${baseB}, ${opacity * 0.8})`);
933
+ grad.addColorStop(0.8, `rgba(${dr},${dg},${db}, ${opacity * 0.6})`);
934
+ grad.addColorStop(1, `rgba(${dr},${dg},${db}, ${opacity * 0.25})`);
935
+ this.ctx.fillStyle = grad;
936
+ }
890
937
  if (isRect) {
891
938
  const halfW = particle.width / 2;
892
939
  const halfH = particle.height / 2;
@@ -1308,6 +1355,9 @@ function BaseChildParticle({
1308
1355
  anchorForce,
1309
1356
  mouseInfluence,
1310
1357
  liquidGlass,
1358
+ glassOpacity,
1359
+ glassColor,
1360
+ liquidGlassConfig,
1311
1361
  children,
1312
1362
  style,
1313
1363
  className
@@ -1327,7 +1377,10 @@ function BaseChildParticle({
1327
1377
  overflow,
1328
1378
  anchorForce,
1329
1379
  mouseInfluence,
1330
- liquidGlass
1380
+ liquidGlass,
1381
+ glassOpacity,
1382
+ glassColor,
1383
+ liquidGlassConfig
1331
1384
  });
1332
1385
  setOverlayEl(instance.getChildOverlayElement(id));
1333
1386
  return () => {
@@ -1347,9 +1400,12 @@ function BaseChildParticle({
1347
1400
  overflow,
1348
1401
  anchorForce,
1349
1402
  mouseInfluence,
1350
- liquidGlass
1403
+ liquidGlass,
1404
+ glassOpacity,
1405
+ glassColor,
1406
+ liquidGlassConfig
1351
1407
  });
1352
- }, [instance, id, x, y, radius, width, height, borderRadius, overflow, anchorForce, mouseInfluence, liquidGlass]);
1408
+ }, [instance, id, x, y, radius, width, height, borderRadius, overflow, anchorForce, mouseInfluence, liquidGlass, glassOpacity, glassColor, liquidGlassConfig]);
1353
1409
  if (!overlayEl) return null;
1354
1410
  const isRect = width != null && height != null;
1355
1411
  const portalBorderRadius = isRect ? (borderRadius ?? Math.min(width, height) / 2) + "px" : "50%";
@@ -1379,5 +1435,6 @@ function BaseChildParticle({
1379
1435
  ChildParticle,
1380
1436
  GlassChildParticle,
1381
1437
  ParticleNetworkBg,
1438
+ ParticleNetworkContext,
1382
1439
  useParticleNetwork
1383
1440
  });
package/dist/react.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  ParticleNetwork
3
- } from "./chunk-BMXQY2SK.mjs";
3
+ } from "./chunk-JZTYQ6XI.mjs";
4
4
 
5
5
  // src/react.tsx
6
6
  import {
@@ -78,6 +78,9 @@ function BaseChildParticle({
78
78
  anchorForce,
79
79
  mouseInfluence,
80
80
  liquidGlass,
81
+ glassOpacity,
82
+ glassColor,
83
+ liquidGlassConfig,
81
84
  children,
82
85
  style,
83
86
  className
@@ -97,7 +100,10 @@ function BaseChildParticle({
97
100
  overflow,
98
101
  anchorForce,
99
102
  mouseInfluence,
100
- liquidGlass
103
+ liquidGlass,
104
+ glassOpacity,
105
+ glassColor,
106
+ liquidGlassConfig
101
107
  });
102
108
  setOverlayEl(instance.getChildOverlayElement(id));
103
109
  return () => {
@@ -117,9 +123,12 @@ function BaseChildParticle({
117
123
  overflow,
118
124
  anchorForce,
119
125
  mouseInfluence,
120
- liquidGlass
126
+ liquidGlass,
127
+ glassOpacity,
128
+ glassColor,
129
+ liquidGlassConfig
121
130
  });
122
- }, [instance, id, x, y, radius, width, height, borderRadius, overflow, anchorForce, mouseInfluence, liquidGlass]);
131
+ }, [instance, id, x, y, radius, width, height, borderRadius, overflow, anchorForce, mouseInfluence, liquidGlass, glassOpacity, glassColor, liquidGlassConfig]);
123
132
  if (!overlayEl) return null;
124
133
  const isRect = width != null && height != null;
125
134
  const portalBorderRadius = isRect ? (borderRadius ?? Math.min(width, height) / 2) + "px" : "50%";
@@ -148,5 +157,6 @@ export {
148
157
  ChildParticle,
149
158
  GlassChildParticle,
150
159
  ParticleNetworkBg,
160
+ ParticleNetworkContext,
151
161
  useParticleNetwork
152
162
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "particle-network-bg",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "description": "Interactive particle network animation for backgrounds",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",