particle-network-bg 1.0.0 → 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 +57 -10
- package/dist/{chunk-VMVSPMPB.mjs → chunk-JZTYQ6XI.mjs} +99 -39
- package/dist/index.d.mts +37 -10
- package/dist/index.d.ts +37 -10
- package/dist/index.js +99 -39
- package/dist/index.mjs +1 -1
- package/dist/react.d.mts +16 -2
- package/dist/react.d.ts +16 -2
- package/dist/react.js +113 -42
- package/dist/react.mjs +14 -4
- package/package.json +1 -1
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,29 +162,63 @@ 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
|
```
|
|
166
169
|
|
|
167
170
|
## Particle Types
|
|
168
171
|
|
|
169
|
-
Use `particleTypes` to mix circle, asset, and liquid glass particles with full control:
|
|
172
|
+
Use `particleTypes` to mix circle, asset, and liquid glass particles with full control. Each entry can carry **per-type overrides** that shadow the global config:
|
|
170
173
|
|
|
171
174
|
```js
|
|
172
175
|
new ParticleNetwork(canvas, {
|
|
176
|
+
particleCount: 100,
|
|
177
|
+
particleColor: "#000000",
|
|
178
|
+
particleOpacity: 1,
|
|
179
|
+
pulseEnabled: false,
|
|
173
180
|
particleTypes: [
|
|
174
|
-
|
|
175
|
-
{ type: "
|
|
176
|
-
|
|
181
|
+
// 50% circles — override color and enable pulse just for these
|
|
182
|
+
{ type: "circle", percentage: 50, color: "#ff6600", pulse: true, pulseSpeed: 0.03 },
|
|
183
|
+
// 30% liquid glass — override glass config just for these
|
|
184
|
+
{ type: "liquidGlass", percentage: 30, liquidGlass: { color: "#00ccff", opacity: 0.8 } },
|
|
185
|
+
// 20 asset particles with custom opacity
|
|
186
|
+
{ type: "asset", asset: "star", count: 20, opacity: 0.7 },
|
|
177
187
|
],
|
|
178
188
|
assets: { star: "https://..." },
|
|
179
|
-
liquidGlass: { color: "#88ccff", blur: 12, contrast: 25
|
|
189
|
+
liquidGlass: { color: "#88ccff", blur: 12, contrast: 25 },
|
|
180
190
|
});
|
|
181
191
|
```
|
|
182
192
|
|
|
183
|
-
-
|
|
184
|
-
|
|
185
|
-
|
|
193
|
+
Per-type values are a **shallow override** on top of the root config. Omitted fields fall back to the global value, so existing usage without `particleTypes` stays unchanged.
|
|
194
|
+
|
|
195
|
+
### Circle type overrides
|
|
196
|
+
|
|
197
|
+
| Field | Type | Description |
|
|
198
|
+
| ------------ | ------- | -------------------------------------------- |
|
|
199
|
+
| `color` | string | Particle color (hex). Overrides `particleColor` |
|
|
200
|
+
| `opacity` | number | Particle opacity (0–1). Overrides `particleOpacity` |
|
|
201
|
+
| `minRadius` | number | Min radius (px). Overrides root `minRadius` |
|
|
202
|
+
| `maxRadius` | number | Max radius (px). Overrides root `maxRadius` |
|
|
203
|
+
| `pulse` | boolean | Enable pulse. Overrides `pulseEnabled` |
|
|
204
|
+
| `pulseSpeed` | number | Pulse speed. Overrides root `pulseSpeed` |
|
|
205
|
+
|
|
206
|
+
### Liquid glass type overrides
|
|
207
|
+
|
|
208
|
+
| Field | Type | Description |
|
|
209
|
+
| ------------ | ------------------------- | -------------------------------------------------------------- |
|
|
210
|
+
| `liquidGlass`| `Partial<LiquidGlassConfig>` | Shallow-merged over root `liquidGlass` (color, opacity, minRadius, etc.) |
|
|
211
|
+
| `pulse` | boolean | Enable pulse. Overrides `pulseEnabled` |
|
|
212
|
+
| `pulseSpeed` | number | Pulse speed. Overrides root `pulseSpeed` |
|
|
213
|
+
|
|
214
|
+
### Asset type overrides
|
|
215
|
+
|
|
216
|
+
| Field | Type | Description |
|
|
217
|
+
| ------------ | ---------------------------------------- | ----------------------------------------------------------------- |
|
|
218
|
+
| `liquidGlass`| `boolean \| Partial<LiquidGlassConfig>` | `true` for glass rendering, or partial config for per-type glass |
|
|
219
|
+
| `opacity` | number | Particle opacity (0–1). Overrides `particleOpacity` |
|
|
220
|
+
| `pulse` | boolean | Enable pulse. Overrides `pulseEnabled` |
|
|
221
|
+
| `pulseSpeed` | number | Pulse speed. Overrides root `pulseSpeed` |
|
|
186
222
|
|
|
187
223
|
## Connection Rules
|
|
188
224
|
|
|
@@ -311,7 +347,7 @@ function App() {
|
|
|
311
347
|
</ChildParticle>
|
|
312
348
|
|
|
313
349
|
{/* Liquid glass blob particle */}
|
|
314
|
-
<GlassChildParticle id="clock" x={600} y={400} radius={60}>
|
|
350
|
+
<GlassChildParticle id="clock" x={600} y={400} radius={60} glassColor="#ff6600" glassOpacity={0.8}>
|
|
315
351
|
<span>🕐</span>
|
|
316
352
|
</GlassChildParticle>
|
|
317
353
|
|
|
@@ -338,6 +374,9 @@ function App() {
|
|
|
338
374
|
| `overflow` | string | `"hidden"` | CSS overflow for the child content container |
|
|
339
375
|
| `anchorForce` | number | `0.05` | Spring force pulling back to anchor (0–1). Lower = more floaty |
|
|
340
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` |
|
|
341
380
|
| `children` | ReactNode | — | Content to render inside the particle |
|
|
342
381
|
| `style` | CSSProperties | — | Style applied to the inner wrapper div |
|
|
343
382
|
| `className` | string | — | Class applied to the inner wrapper div |
|
|
@@ -358,7 +397,9 @@ network.addChildParticle({
|
|
|
358
397
|
radius: 50,
|
|
359
398
|
anchorForce: 0.05,
|
|
360
399
|
mouseInfluence: 0.1,
|
|
361
|
-
liquidGlass:
|
|
400
|
+
liquidGlass: true,
|
|
401
|
+
glassOpacity: 0.7, // override global liquidGlass.opacity
|
|
402
|
+
glassColor: "#ff6600", // override global liquidGlass.color
|
|
362
403
|
});
|
|
363
404
|
|
|
364
405
|
// Update its anchor (e.g. on scroll/resize)
|
|
@@ -403,6 +444,9 @@ interface ChildParticleConfig {
|
|
|
403
444
|
anchorForce?: number;
|
|
404
445
|
mouseInfluence?: number;
|
|
405
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
|
|
406
450
|
}
|
|
407
451
|
|
|
408
452
|
interface ChildParticlePosition {
|
|
@@ -445,6 +489,9 @@ import type {
|
|
|
445
489
|
LiquidGlassConfig,
|
|
446
490
|
LiquidGlassHighlightPosition,
|
|
447
491
|
ParticleTypeEntry,
|
|
492
|
+
CircleTypeEntry,
|
|
493
|
+
LiquidGlassTypeEntry,
|
|
494
|
+
AssetTypeEntry,
|
|
448
495
|
ParticleAssetConfig,
|
|
449
496
|
ChildParticleConfig,
|
|
450
497
|
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 = `
|
|
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
|
-
|
|
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
|
-
|
|
338
|
-
|
|
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
|
}
|
|
@@ -432,6 +441,8 @@ var ParticleNetwork = class {
|
|
|
432
441
|
this.particles.forEach((p) => {
|
|
433
442
|
delete p.assetId;
|
|
434
443
|
delete p.liquidGlass;
|
|
444
|
+
delete p.typeConfig;
|
|
445
|
+
delete p.pulsePhase;
|
|
435
446
|
});
|
|
436
447
|
if (particleTypes?.length) {
|
|
437
448
|
const counts = [];
|
|
@@ -444,15 +455,16 @@ var ParticleNetwork = class {
|
|
|
444
455
|
}
|
|
445
456
|
if (count > 0) {
|
|
446
457
|
if (pt.type === "circle") {
|
|
447
|
-
counts.push({ liquidGlass: false, count });
|
|
458
|
+
counts.push({ entry: pt, liquidGlass: false, count });
|
|
448
459
|
} else if (pt.type === "asset") {
|
|
449
460
|
counts.push({
|
|
461
|
+
entry: pt,
|
|
450
462
|
assetId: pt.asset,
|
|
451
|
-
liquidGlass: pt.liquidGlass
|
|
463
|
+
liquidGlass: !!pt.liquidGlass,
|
|
452
464
|
count
|
|
453
465
|
});
|
|
454
466
|
} else {
|
|
455
|
-
counts.push({ liquidGlass: true, count });
|
|
467
|
+
counts.push({ entry: pt, liquidGlass: true, count });
|
|
456
468
|
}
|
|
457
469
|
}
|
|
458
470
|
}
|
|
@@ -463,9 +475,10 @@ var ParticleNetwork = class {
|
|
|
463
475
|
[indices[i], indices[j]] = [indices[j], indices[i]];
|
|
464
476
|
}
|
|
465
477
|
let idx = 0;
|
|
466
|
-
for (const { assetId, liquidGlass, count } of counts) {
|
|
478
|
+
for (const { entry, assetId, liquidGlass, count } of counts) {
|
|
467
479
|
for (let c = 0; c < count && idx < indices.length; c++, idx++) {
|
|
468
480
|
const p = this.particles[indices[idx]];
|
|
481
|
+
p.typeConfig = entry;
|
|
469
482
|
if (assetId) p.assetId = assetId;
|
|
470
483
|
p.liquidGlass = liquidGlass;
|
|
471
484
|
if (liquidGlass) this.resizeAsLiquidGlass(p);
|
|
@@ -498,6 +511,14 @@ var ParticleNetwork = class {
|
|
|
498
511
|
if (!particleTypes?.length && (this.config.liquidGlassPercentage != null || this.config.liquidGlassCount != null)) {
|
|
499
512
|
this.assignLiquidGlass();
|
|
500
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
|
+
});
|
|
501
522
|
this.assignMouseBehavior();
|
|
502
523
|
}
|
|
503
524
|
assignLiquidGlass() {
|
|
@@ -524,8 +545,10 @@ var ParticleNetwork = class {
|
|
|
524
545
|
}
|
|
525
546
|
resizeAsLiquidGlass(p) {
|
|
526
547
|
const lg = this.getLiquidGlassConfig();
|
|
527
|
-
const
|
|
528
|
-
const
|
|
548
|
+
const tc = p.typeConfig;
|
|
549
|
+
const perTypeLg = tc && "liquidGlass" in tc && typeof tc.liquidGlass === "object" ? tc.liquidGlass : void 0;
|
|
550
|
+
const min = perTypeLg?.minRadius ?? lg.minRadius ?? DEFAULT_LIQUID_GLASS.minRadius;
|
|
551
|
+
const max = perTypeLg?.maxRadius ?? lg.maxRadius ?? DEFAULT_LIQUID_GLASS.maxRadius;
|
|
529
552
|
p.radius = Math.random() * (max - min) + min;
|
|
530
553
|
delete p.currentRadius;
|
|
531
554
|
this.initBlob(p);
|
|
@@ -596,9 +619,13 @@ var ParticleNetwork = class {
|
|
|
596
619
|
if (particle.isChild) {
|
|
597
620
|
particle.currentRadius = particle.radius;
|
|
598
621
|
} else {
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
622
|
+
const tc = particle.typeConfig;
|
|
623
|
+
const doPulse = tc?.pulse ?? this.config.pulseEnabled;
|
|
624
|
+
if (doPulse) {
|
|
625
|
+
const speed = tc?.pulseSpeed ?? this.config.pulseSpeed;
|
|
626
|
+
this.pulseAngle += speed;
|
|
627
|
+
const phase = particle.pulsePhase ?? 0;
|
|
628
|
+
const pulseScale = Math.sin(this.pulseAngle + phase) * 0.5 + 1;
|
|
602
629
|
particle.currentRadius = particle.radius * pulseScale;
|
|
603
630
|
} else {
|
|
604
631
|
particle.currentRadius = particle.radius;
|
|
@@ -798,7 +825,22 @@ var ParticleNetwork = class {
|
|
|
798
825
|
blob.mouseStretchY *= 1 - smoothing;
|
|
799
826
|
}
|
|
800
827
|
draw3DFluidSphere(particle) {
|
|
801
|
-
const
|
|
828
|
+
const tc = particle.typeConfig;
|
|
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
|
+
}
|
|
843
|
+
const lg = perTypeLg ? { ...this.getLiquidGlassConfig(), ...perTypeLg } : this.getLiquidGlassConfig();
|
|
802
844
|
const r = particle.currentRadius ?? particle.radius;
|
|
803
845
|
if (r <= 0) return;
|
|
804
846
|
if (!particle.blob) this.initBlob(particle);
|
|
@@ -806,46 +848,60 @@ var ParticleNetwork = class {
|
|
|
806
848
|
const cx = particle.x;
|
|
807
849
|
const cy = particle.y;
|
|
808
850
|
const isRect = particle.width != null && particle.height != null;
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
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
|
+
}
|
|
812
861
|
}
|
|
813
862
|
if (opacity <= 0) return;
|
|
814
|
-
|
|
863
|
+
const blobSpeed = lg.blobSpeed ?? 1;
|
|
864
|
+
blob.rotation += blob.rotSpeed * blobSpeed;
|
|
815
865
|
for (let m = 0; m < blob.phases.length; m++) {
|
|
816
|
-
blob.phases[m] += blob.phaseSpeeds[m];
|
|
866
|
+
blob.phases[m] += blob.phaseSpeeds[m] * blobSpeed;
|
|
817
867
|
}
|
|
818
|
-
blob.hlAngle += blob.hlAngleSpeed;
|
|
868
|
+
blob.hlAngle += blob.hlAngleSpeed * blobSpeed;
|
|
819
869
|
this.updateBlobMouse(particle);
|
|
820
870
|
const color = lg.color ?? DEFAULT_LIQUID_GLASS.color;
|
|
821
871
|
const baseR = parseInt(color.slice(1, 3), 16);
|
|
822
872
|
const baseG = parseInt(color.slice(3, 5), 16);
|
|
823
873
|
const baseB = parseInt(color.slice(5, 7), 16);
|
|
824
874
|
const shadowStr = lg.shadowStrength ?? DEFAULT_LIQUID_GLASS.shadowStrength;
|
|
875
|
+
const refl = (lg.reflectionStrength ?? DEFAULT_LIQUID_GLASS.reflectionStrength) / DEFAULT_LIQUID_GLASS.reflectionStrength;
|
|
825
876
|
const gradR = isRect ? Math.max(particle.width, particle.height) / 2 : r;
|
|
826
877
|
const hlDist = gradR * 0.35;
|
|
827
878
|
const hlX = cx + Math.cos(blob.hlAngle) * hlDist;
|
|
828
879
|
const hlY = cy + Math.sin(blob.hlAngle) * hlDist;
|
|
829
|
-
const lr = Math.min(255, baseR + Math.round((255 - baseR) * 0.55));
|
|
830
|
-
const lgr = Math.min(255, baseG + Math.round((255 - baseG) * 0.55));
|
|
831
|
-
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));
|
|
832
883
|
const dr = Math.max(0, Math.round(baseR * (1 - shadowStr * 0.35)));
|
|
833
884
|
const dg = Math.max(0, Math.round(baseG * (1 - shadowStr * 0.35)));
|
|
834
885
|
const db = Math.max(0, Math.round(baseB * (1 - shadowStr * 0.35)));
|
|
835
886
|
this.ctx.save();
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
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
|
+
}
|
|
849
905
|
if (isRect) {
|
|
850
906
|
const halfW = particle.width / 2;
|
|
851
907
|
const halfH = particle.height / 2;
|
|
@@ -883,7 +939,9 @@ var ParticleNetwork = class {
|
|
|
883
939
|
this.draw3DFluidSphere(particle);
|
|
884
940
|
return;
|
|
885
941
|
}
|
|
886
|
-
|
|
942
|
+
const tc = particle.typeConfig;
|
|
943
|
+
const tcOpacity = tc && "opacity" in tc ? tc.opacity : void 0;
|
|
944
|
+
let opacity = tcOpacity ?? this.config.particleOpacity;
|
|
887
945
|
if (this.config.depthEffectEnabled) {
|
|
888
946
|
opacity *= 0.6 + 0.4 * particle.z;
|
|
889
947
|
}
|
|
@@ -900,8 +958,9 @@ var ParticleNetwork = class {
|
|
|
900
958
|
this.ctx.drawImage(img, x, y, size, size);
|
|
901
959
|
}
|
|
902
960
|
} else if (particle.isChild && particle.width != null && particle.height != null) {
|
|
961
|
+
const color = (tc && "color" in tc ? tc.color : void 0) ?? defaultColor;
|
|
903
962
|
this.ctx.globalAlpha = opacity;
|
|
904
|
-
this.ctx.fillStyle =
|
|
963
|
+
this.ctx.fillStyle = color;
|
|
905
964
|
const w = particle.width;
|
|
906
965
|
const h = particle.height;
|
|
907
966
|
const br = particle.borderRadius ?? Math.min(w, h) / 2;
|
|
@@ -911,8 +970,9 @@ var ParticleNetwork = class {
|
|
|
911
970
|
this.ctx.roundRect(x, y, w, h, br);
|
|
912
971
|
this.ctx.fill();
|
|
913
972
|
} else {
|
|
973
|
+
const color = (tc && "color" in tc ? tc.color : void 0) ?? defaultColor;
|
|
914
974
|
this.ctx.globalAlpha = opacity;
|
|
915
|
-
this.ctx.fillStyle =
|
|
975
|
+
this.ctx.fillStyle = color;
|
|
916
976
|
this.ctx.beginPath();
|
|
917
977
|
this.ctx.arc(particle.x, particle.y, r, 0, Math.PI * 2);
|
|
918
978
|
this.ctx.fill();
|
package/dist/index.d.mts
CHANGED
|
@@ -38,23 +38,40 @@ 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
|
-
|
|
43
|
-
type ParticleTypeEntry = {
|
|
44
|
+
interface CircleTypeEntry {
|
|
44
45
|
type: "circle";
|
|
46
|
+
percentage?: number;
|
|
45
47
|
count?: number;
|
|
48
|
+
color?: string;
|
|
49
|
+
opacity?: number;
|
|
50
|
+
minRadius?: number;
|
|
51
|
+
maxRadius?: number;
|
|
52
|
+
pulse?: boolean;
|
|
53
|
+
pulseSpeed?: number;
|
|
54
|
+
}
|
|
55
|
+
interface LiquidGlassTypeEntry {
|
|
56
|
+
type: "liquidGlass";
|
|
46
57
|
percentage?: number;
|
|
47
|
-
|
|
58
|
+
count?: number;
|
|
59
|
+
liquidGlass?: Partial<LiquidGlassConfig>;
|
|
60
|
+
pulse?: boolean;
|
|
61
|
+
pulseSpeed?: number;
|
|
62
|
+
}
|
|
63
|
+
interface AssetTypeEntry {
|
|
48
64
|
type: "asset";
|
|
49
65
|
asset: string;
|
|
50
|
-
count?: number;
|
|
51
66
|
percentage?: number;
|
|
52
|
-
liquidGlass?: boolean;
|
|
53
|
-
} | {
|
|
54
|
-
type: "liquidGlass";
|
|
55
67
|
count?: number;
|
|
56
|
-
|
|
57
|
-
|
|
68
|
+
liquidGlass?: boolean | Partial<LiquidGlassConfig>;
|
|
69
|
+
opacity?: number;
|
|
70
|
+
pulse?: boolean;
|
|
71
|
+
pulseSpeed?: number;
|
|
72
|
+
}
|
|
73
|
+
/** Particle type entry for mixing circle, asset, liquidGlass with optional per-type overrides. */
|
|
74
|
+
type ParticleTypeEntry = CircleTypeEntry | LiquidGlassTypeEntry | AssetTypeEntry;
|
|
58
75
|
/** Configuration for a child particle that holds a UI component. */
|
|
59
76
|
interface ChildParticleConfig {
|
|
60
77
|
/** Unique identifier linking this particle to a React/DOM child. */
|
|
@@ -79,6 +96,12 @@ interface ChildParticleConfig {
|
|
|
79
96
|
mouseInfluence?: number;
|
|
80
97
|
/** Render as liquid glass particle. Default false. */
|
|
81
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>;
|
|
82
105
|
}
|
|
83
106
|
/** Emitted position data for a child particle each frame. */
|
|
84
107
|
interface ChildParticlePosition {
|
|
@@ -191,6 +214,10 @@ interface Particle {
|
|
|
191
214
|
borderRadius?: number;
|
|
192
215
|
/** CSS overflow for the child content overlay. */
|
|
193
216
|
overflow?: string;
|
|
217
|
+
/** Per-type config entry (shallow overrides root config). */
|
|
218
|
+
typeConfig?: ParticleTypeEntry;
|
|
219
|
+
/** Random phase offset (0–2π) for pulse animation. Makes particles pulse out of sync. */
|
|
220
|
+
pulsePhase?: number;
|
|
194
221
|
/** Smoothed x for overlay positioning. */
|
|
195
222
|
smoothX?: number;
|
|
196
223
|
/** Smoothed y for overlay positioning. */
|
|
@@ -276,4 +303,4 @@ declare class ParticleNetwork {
|
|
|
276
303
|
reset(defaults: Partial<ParticleNetworkConfig>): void;
|
|
277
304
|
}
|
|
278
305
|
|
|
279
|
-
export { type ChildParticleConfig, type ChildParticlePosition, type ConnectionRules, type GradientType, type LiquidGlassConfig, type LiquidGlassHighlightPosition, type Particle, type ParticleAssetConfig, ParticleNetwork, type ParticleNetworkConfig, type ParticleTypeEntry };
|
|
306
|
+
export { type AssetTypeEntry, type ChildParticleConfig, type ChildParticlePosition, type CircleTypeEntry, type ConnectionRules, type GradientType, type LiquidGlassConfig, type LiquidGlassHighlightPosition, type LiquidGlassTypeEntry, type Particle, type ParticleAssetConfig, ParticleNetwork, type ParticleNetworkConfig, type ParticleTypeEntry };
|
package/dist/index.d.ts
CHANGED
|
@@ -38,23 +38,40 @@ 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
|
-
|
|
43
|
-
type ParticleTypeEntry = {
|
|
44
|
+
interface CircleTypeEntry {
|
|
44
45
|
type: "circle";
|
|
46
|
+
percentage?: number;
|
|
45
47
|
count?: number;
|
|
48
|
+
color?: string;
|
|
49
|
+
opacity?: number;
|
|
50
|
+
minRadius?: number;
|
|
51
|
+
maxRadius?: number;
|
|
52
|
+
pulse?: boolean;
|
|
53
|
+
pulseSpeed?: number;
|
|
54
|
+
}
|
|
55
|
+
interface LiquidGlassTypeEntry {
|
|
56
|
+
type: "liquidGlass";
|
|
46
57
|
percentage?: number;
|
|
47
|
-
|
|
58
|
+
count?: number;
|
|
59
|
+
liquidGlass?: Partial<LiquidGlassConfig>;
|
|
60
|
+
pulse?: boolean;
|
|
61
|
+
pulseSpeed?: number;
|
|
62
|
+
}
|
|
63
|
+
interface AssetTypeEntry {
|
|
48
64
|
type: "asset";
|
|
49
65
|
asset: string;
|
|
50
|
-
count?: number;
|
|
51
66
|
percentage?: number;
|
|
52
|
-
liquidGlass?: boolean;
|
|
53
|
-
} | {
|
|
54
|
-
type: "liquidGlass";
|
|
55
67
|
count?: number;
|
|
56
|
-
|
|
57
|
-
|
|
68
|
+
liquidGlass?: boolean | Partial<LiquidGlassConfig>;
|
|
69
|
+
opacity?: number;
|
|
70
|
+
pulse?: boolean;
|
|
71
|
+
pulseSpeed?: number;
|
|
72
|
+
}
|
|
73
|
+
/** Particle type entry for mixing circle, asset, liquidGlass with optional per-type overrides. */
|
|
74
|
+
type ParticleTypeEntry = CircleTypeEntry | LiquidGlassTypeEntry | AssetTypeEntry;
|
|
58
75
|
/** Configuration for a child particle that holds a UI component. */
|
|
59
76
|
interface ChildParticleConfig {
|
|
60
77
|
/** Unique identifier linking this particle to a React/DOM child. */
|
|
@@ -79,6 +96,12 @@ interface ChildParticleConfig {
|
|
|
79
96
|
mouseInfluence?: number;
|
|
80
97
|
/** Render as liquid glass particle. Default false. */
|
|
81
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>;
|
|
82
105
|
}
|
|
83
106
|
/** Emitted position data for a child particle each frame. */
|
|
84
107
|
interface ChildParticlePosition {
|
|
@@ -191,6 +214,10 @@ interface Particle {
|
|
|
191
214
|
borderRadius?: number;
|
|
192
215
|
/** CSS overflow for the child content overlay. */
|
|
193
216
|
overflow?: string;
|
|
217
|
+
/** Per-type config entry (shallow overrides root config). */
|
|
218
|
+
typeConfig?: ParticleTypeEntry;
|
|
219
|
+
/** Random phase offset (0–2π) for pulse animation. Makes particles pulse out of sync. */
|
|
220
|
+
pulsePhase?: number;
|
|
194
221
|
/** Smoothed x for overlay positioning. */
|
|
195
222
|
smoothX?: number;
|
|
196
223
|
/** Smoothed y for overlay positioning. */
|
|
@@ -276,4 +303,4 @@ declare class ParticleNetwork {
|
|
|
276
303
|
reset(defaults: Partial<ParticleNetworkConfig>): void;
|
|
277
304
|
}
|
|
278
305
|
|
|
279
|
-
export { type ChildParticleConfig, type ChildParticlePosition, type ConnectionRules, type GradientType, type LiquidGlassConfig, type LiquidGlassHighlightPosition, type Particle, type ParticleAssetConfig, ParticleNetwork, type ParticleNetworkConfig, type ParticleTypeEntry };
|
|
306
|
+
export { type AssetTypeEntry, type ChildParticleConfig, type ChildParticlePosition, type CircleTypeEntry, type ConnectionRules, type GradientType, type LiquidGlassConfig, type LiquidGlassHighlightPosition, type LiquidGlassTypeEntry, type Particle, type ParticleAssetConfig, ParticleNetwork, type ParticleNetworkConfig, type ParticleTypeEntry };
|
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 = `
|
|
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
|
-
|
|
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
|
-
|
|
362
|
-
|
|
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
|
}
|
|
@@ -456,6 +465,8 @@ var ParticleNetwork = class {
|
|
|
456
465
|
this.particles.forEach((p) => {
|
|
457
466
|
delete p.assetId;
|
|
458
467
|
delete p.liquidGlass;
|
|
468
|
+
delete p.typeConfig;
|
|
469
|
+
delete p.pulsePhase;
|
|
459
470
|
});
|
|
460
471
|
if (particleTypes?.length) {
|
|
461
472
|
const counts = [];
|
|
@@ -468,15 +479,16 @@ var ParticleNetwork = class {
|
|
|
468
479
|
}
|
|
469
480
|
if (count > 0) {
|
|
470
481
|
if (pt.type === "circle") {
|
|
471
|
-
counts.push({ liquidGlass: false, count });
|
|
482
|
+
counts.push({ entry: pt, liquidGlass: false, count });
|
|
472
483
|
} else if (pt.type === "asset") {
|
|
473
484
|
counts.push({
|
|
485
|
+
entry: pt,
|
|
474
486
|
assetId: pt.asset,
|
|
475
|
-
liquidGlass: pt.liquidGlass
|
|
487
|
+
liquidGlass: !!pt.liquidGlass,
|
|
476
488
|
count
|
|
477
489
|
});
|
|
478
490
|
} else {
|
|
479
|
-
counts.push({ liquidGlass: true, count });
|
|
491
|
+
counts.push({ entry: pt, liquidGlass: true, count });
|
|
480
492
|
}
|
|
481
493
|
}
|
|
482
494
|
}
|
|
@@ -487,9 +499,10 @@ var ParticleNetwork = class {
|
|
|
487
499
|
[indices[i], indices[j]] = [indices[j], indices[i]];
|
|
488
500
|
}
|
|
489
501
|
let idx = 0;
|
|
490
|
-
for (const { assetId, liquidGlass, count } of counts) {
|
|
502
|
+
for (const { entry, assetId, liquidGlass, count } of counts) {
|
|
491
503
|
for (let c = 0; c < count && idx < indices.length; c++, idx++) {
|
|
492
504
|
const p = this.particles[indices[idx]];
|
|
505
|
+
p.typeConfig = entry;
|
|
493
506
|
if (assetId) p.assetId = assetId;
|
|
494
507
|
p.liquidGlass = liquidGlass;
|
|
495
508
|
if (liquidGlass) this.resizeAsLiquidGlass(p);
|
|
@@ -522,6 +535,14 @@ var ParticleNetwork = class {
|
|
|
522
535
|
if (!particleTypes?.length && (this.config.liquidGlassPercentage != null || this.config.liquidGlassCount != null)) {
|
|
523
536
|
this.assignLiquidGlass();
|
|
524
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
|
+
});
|
|
525
546
|
this.assignMouseBehavior();
|
|
526
547
|
}
|
|
527
548
|
assignLiquidGlass() {
|
|
@@ -548,8 +569,10 @@ var ParticleNetwork = class {
|
|
|
548
569
|
}
|
|
549
570
|
resizeAsLiquidGlass(p) {
|
|
550
571
|
const lg = this.getLiquidGlassConfig();
|
|
551
|
-
const
|
|
552
|
-
const
|
|
572
|
+
const tc = p.typeConfig;
|
|
573
|
+
const perTypeLg = tc && "liquidGlass" in tc && typeof tc.liquidGlass === "object" ? tc.liquidGlass : void 0;
|
|
574
|
+
const min = perTypeLg?.minRadius ?? lg.minRadius ?? DEFAULT_LIQUID_GLASS.minRadius;
|
|
575
|
+
const max = perTypeLg?.maxRadius ?? lg.maxRadius ?? DEFAULT_LIQUID_GLASS.maxRadius;
|
|
553
576
|
p.radius = Math.random() * (max - min) + min;
|
|
554
577
|
delete p.currentRadius;
|
|
555
578
|
this.initBlob(p);
|
|
@@ -620,9 +643,13 @@ var ParticleNetwork = class {
|
|
|
620
643
|
if (particle.isChild) {
|
|
621
644
|
particle.currentRadius = particle.radius;
|
|
622
645
|
} else {
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
646
|
+
const tc = particle.typeConfig;
|
|
647
|
+
const doPulse = tc?.pulse ?? this.config.pulseEnabled;
|
|
648
|
+
if (doPulse) {
|
|
649
|
+
const speed = tc?.pulseSpeed ?? this.config.pulseSpeed;
|
|
650
|
+
this.pulseAngle += speed;
|
|
651
|
+
const phase = particle.pulsePhase ?? 0;
|
|
652
|
+
const pulseScale = Math.sin(this.pulseAngle + phase) * 0.5 + 1;
|
|
626
653
|
particle.currentRadius = particle.radius * pulseScale;
|
|
627
654
|
} else {
|
|
628
655
|
particle.currentRadius = particle.radius;
|
|
@@ -822,7 +849,22 @@ var ParticleNetwork = class {
|
|
|
822
849
|
blob.mouseStretchY *= 1 - smoothing;
|
|
823
850
|
}
|
|
824
851
|
draw3DFluidSphere(particle) {
|
|
825
|
-
const
|
|
852
|
+
const tc = particle.typeConfig;
|
|
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
|
+
}
|
|
867
|
+
const lg = perTypeLg ? { ...this.getLiquidGlassConfig(), ...perTypeLg } : this.getLiquidGlassConfig();
|
|
826
868
|
const r = particle.currentRadius ?? particle.radius;
|
|
827
869
|
if (r <= 0) return;
|
|
828
870
|
if (!particle.blob) this.initBlob(particle);
|
|
@@ -830,46 +872,60 @@ var ParticleNetwork = class {
|
|
|
830
872
|
const cx = particle.x;
|
|
831
873
|
const cy = particle.y;
|
|
832
874
|
const isRect = particle.width != null && particle.height != null;
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
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
|
+
}
|
|
836
885
|
}
|
|
837
886
|
if (opacity <= 0) return;
|
|
838
|
-
|
|
887
|
+
const blobSpeed = lg.blobSpeed ?? 1;
|
|
888
|
+
blob.rotation += blob.rotSpeed * blobSpeed;
|
|
839
889
|
for (let m = 0; m < blob.phases.length; m++) {
|
|
840
|
-
blob.phases[m] += blob.phaseSpeeds[m];
|
|
890
|
+
blob.phases[m] += blob.phaseSpeeds[m] * blobSpeed;
|
|
841
891
|
}
|
|
842
|
-
blob.hlAngle += blob.hlAngleSpeed;
|
|
892
|
+
blob.hlAngle += blob.hlAngleSpeed * blobSpeed;
|
|
843
893
|
this.updateBlobMouse(particle);
|
|
844
894
|
const color = lg.color ?? DEFAULT_LIQUID_GLASS.color;
|
|
845
895
|
const baseR = parseInt(color.slice(1, 3), 16);
|
|
846
896
|
const baseG = parseInt(color.slice(3, 5), 16);
|
|
847
897
|
const baseB = parseInt(color.slice(5, 7), 16);
|
|
848
898
|
const shadowStr = lg.shadowStrength ?? DEFAULT_LIQUID_GLASS.shadowStrength;
|
|
899
|
+
const refl = (lg.reflectionStrength ?? DEFAULT_LIQUID_GLASS.reflectionStrength) / DEFAULT_LIQUID_GLASS.reflectionStrength;
|
|
849
900
|
const gradR = isRect ? Math.max(particle.width, particle.height) / 2 : r;
|
|
850
901
|
const hlDist = gradR * 0.35;
|
|
851
902
|
const hlX = cx + Math.cos(blob.hlAngle) * hlDist;
|
|
852
903
|
const hlY = cy + Math.sin(blob.hlAngle) * hlDist;
|
|
853
|
-
const lr = Math.min(255, baseR + Math.round((255 - baseR) * 0.55));
|
|
854
|
-
const lgr = Math.min(255, baseG + Math.round((255 - baseG) * 0.55));
|
|
855
|
-
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));
|
|
856
907
|
const dr = Math.max(0, Math.round(baseR * (1 - shadowStr * 0.35)));
|
|
857
908
|
const dg = Math.max(0, Math.round(baseG * (1 - shadowStr * 0.35)));
|
|
858
909
|
const db = Math.max(0, Math.round(baseB * (1 - shadowStr * 0.35)));
|
|
859
910
|
this.ctx.save();
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
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
|
+
}
|
|
873
929
|
if (isRect) {
|
|
874
930
|
const halfW = particle.width / 2;
|
|
875
931
|
const halfH = particle.height / 2;
|
|
@@ -907,7 +963,9 @@ var ParticleNetwork = class {
|
|
|
907
963
|
this.draw3DFluidSphere(particle);
|
|
908
964
|
return;
|
|
909
965
|
}
|
|
910
|
-
|
|
966
|
+
const tc = particle.typeConfig;
|
|
967
|
+
const tcOpacity = tc && "opacity" in tc ? tc.opacity : void 0;
|
|
968
|
+
let opacity = tcOpacity ?? this.config.particleOpacity;
|
|
911
969
|
if (this.config.depthEffectEnabled) {
|
|
912
970
|
opacity *= 0.6 + 0.4 * particle.z;
|
|
913
971
|
}
|
|
@@ -924,8 +982,9 @@ var ParticleNetwork = class {
|
|
|
924
982
|
this.ctx.drawImage(img, x, y, size, size);
|
|
925
983
|
}
|
|
926
984
|
} else if (particle.isChild && particle.width != null && particle.height != null) {
|
|
985
|
+
const color = (tc && "color" in tc ? tc.color : void 0) ?? defaultColor;
|
|
927
986
|
this.ctx.globalAlpha = opacity;
|
|
928
|
-
this.ctx.fillStyle =
|
|
987
|
+
this.ctx.fillStyle = color;
|
|
929
988
|
const w = particle.width;
|
|
930
989
|
const h = particle.height;
|
|
931
990
|
const br = particle.borderRadius ?? Math.min(w, h) / 2;
|
|
@@ -935,8 +994,9 @@ var ParticleNetwork = class {
|
|
|
935
994
|
this.ctx.roundRect(x, y, w, h, br);
|
|
936
995
|
this.ctx.fill();
|
|
937
996
|
} else {
|
|
997
|
+
const color = (tc && "color" in tc ? tc.color : void 0) ?? defaultColor;
|
|
938
998
|
this.ctx.globalAlpha = opacity;
|
|
939
|
-
this.ctx.fillStyle =
|
|
999
|
+
this.ctx.fillStyle = color;
|
|
940
1000
|
this.ctx.beginPath();
|
|
941
1001
|
this.ctx.arc(particle.x, particle.y, r, 0, Math.PI * 2);
|
|
942
1002
|
this.ctx.fill();
|
package/dist/index.mjs
CHANGED
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 = `
|
|
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
|
-
|
|
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
|
-
|
|
369
|
-
|
|
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
|
}
|
|
@@ -463,6 +473,8 @@ var ParticleNetwork = class {
|
|
|
463
473
|
this.particles.forEach((p) => {
|
|
464
474
|
delete p.assetId;
|
|
465
475
|
delete p.liquidGlass;
|
|
476
|
+
delete p.typeConfig;
|
|
477
|
+
delete p.pulsePhase;
|
|
466
478
|
});
|
|
467
479
|
if (particleTypes?.length) {
|
|
468
480
|
const counts = [];
|
|
@@ -475,15 +487,16 @@ var ParticleNetwork = class {
|
|
|
475
487
|
}
|
|
476
488
|
if (count > 0) {
|
|
477
489
|
if (pt.type === "circle") {
|
|
478
|
-
counts.push({ liquidGlass: false, count });
|
|
490
|
+
counts.push({ entry: pt, liquidGlass: false, count });
|
|
479
491
|
} else if (pt.type === "asset") {
|
|
480
492
|
counts.push({
|
|
493
|
+
entry: pt,
|
|
481
494
|
assetId: pt.asset,
|
|
482
|
-
liquidGlass: pt.liquidGlass
|
|
495
|
+
liquidGlass: !!pt.liquidGlass,
|
|
483
496
|
count
|
|
484
497
|
});
|
|
485
498
|
} else {
|
|
486
|
-
counts.push({ liquidGlass: true, count });
|
|
499
|
+
counts.push({ entry: pt, liquidGlass: true, count });
|
|
487
500
|
}
|
|
488
501
|
}
|
|
489
502
|
}
|
|
@@ -494,9 +507,10 @@ var ParticleNetwork = class {
|
|
|
494
507
|
[indices[i], indices[j]] = [indices[j], indices[i]];
|
|
495
508
|
}
|
|
496
509
|
let idx = 0;
|
|
497
|
-
for (const { assetId, liquidGlass, count } of counts) {
|
|
510
|
+
for (const { entry, assetId, liquidGlass, count } of counts) {
|
|
498
511
|
for (let c = 0; c < count && idx < indices.length; c++, idx++) {
|
|
499
512
|
const p = this.particles[indices[idx]];
|
|
513
|
+
p.typeConfig = entry;
|
|
500
514
|
if (assetId) p.assetId = assetId;
|
|
501
515
|
p.liquidGlass = liquidGlass;
|
|
502
516
|
if (liquidGlass) this.resizeAsLiquidGlass(p);
|
|
@@ -529,6 +543,14 @@ var ParticleNetwork = class {
|
|
|
529
543
|
if (!particleTypes?.length && (this.config.liquidGlassPercentage != null || this.config.liquidGlassCount != null)) {
|
|
530
544
|
this.assignLiquidGlass();
|
|
531
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
|
+
});
|
|
532
554
|
this.assignMouseBehavior();
|
|
533
555
|
}
|
|
534
556
|
assignLiquidGlass() {
|
|
@@ -555,8 +577,10 @@ var ParticleNetwork = class {
|
|
|
555
577
|
}
|
|
556
578
|
resizeAsLiquidGlass(p) {
|
|
557
579
|
const lg = this.getLiquidGlassConfig();
|
|
558
|
-
const
|
|
559
|
-
const
|
|
580
|
+
const tc = p.typeConfig;
|
|
581
|
+
const perTypeLg = tc && "liquidGlass" in tc && typeof tc.liquidGlass === "object" ? tc.liquidGlass : void 0;
|
|
582
|
+
const min = perTypeLg?.minRadius ?? lg.minRadius ?? DEFAULT_LIQUID_GLASS.minRadius;
|
|
583
|
+
const max = perTypeLg?.maxRadius ?? lg.maxRadius ?? DEFAULT_LIQUID_GLASS.maxRadius;
|
|
560
584
|
p.radius = Math.random() * (max - min) + min;
|
|
561
585
|
delete p.currentRadius;
|
|
562
586
|
this.initBlob(p);
|
|
@@ -627,9 +651,13 @@ var ParticleNetwork = class {
|
|
|
627
651
|
if (particle.isChild) {
|
|
628
652
|
particle.currentRadius = particle.radius;
|
|
629
653
|
} else {
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
654
|
+
const tc = particle.typeConfig;
|
|
655
|
+
const doPulse = tc?.pulse ?? this.config.pulseEnabled;
|
|
656
|
+
if (doPulse) {
|
|
657
|
+
const speed = tc?.pulseSpeed ?? this.config.pulseSpeed;
|
|
658
|
+
this.pulseAngle += speed;
|
|
659
|
+
const phase = particle.pulsePhase ?? 0;
|
|
660
|
+
const pulseScale = Math.sin(this.pulseAngle + phase) * 0.5 + 1;
|
|
633
661
|
particle.currentRadius = particle.radius * pulseScale;
|
|
634
662
|
} else {
|
|
635
663
|
particle.currentRadius = particle.radius;
|
|
@@ -829,7 +857,22 @@ var ParticleNetwork = class {
|
|
|
829
857
|
blob.mouseStretchY *= 1 - smoothing;
|
|
830
858
|
}
|
|
831
859
|
draw3DFluidSphere(particle) {
|
|
832
|
-
const
|
|
860
|
+
const tc = particle.typeConfig;
|
|
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
|
+
}
|
|
875
|
+
const lg = perTypeLg ? { ...this.getLiquidGlassConfig(), ...perTypeLg } : this.getLiquidGlassConfig();
|
|
833
876
|
const r = particle.currentRadius ?? particle.radius;
|
|
834
877
|
if (r <= 0) return;
|
|
835
878
|
if (!particle.blob) this.initBlob(particle);
|
|
@@ -837,46 +880,60 @@ var ParticleNetwork = class {
|
|
|
837
880
|
const cx = particle.x;
|
|
838
881
|
const cy = particle.y;
|
|
839
882
|
const isRect = particle.width != null && particle.height != null;
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
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
|
+
}
|
|
843
893
|
}
|
|
844
894
|
if (opacity <= 0) return;
|
|
845
|
-
|
|
895
|
+
const blobSpeed = lg.blobSpeed ?? 1;
|
|
896
|
+
blob.rotation += blob.rotSpeed * blobSpeed;
|
|
846
897
|
for (let m = 0; m < blob.phases.length; m++) {
|
|
847
|
-
blob.phases[m] += blob.phaseSpeeds[m];
|
|
898
|
+
blob.phases[m] += blob.phaseSpeeds[m] * blobSpeed;
|
|
848
899
|
}
|
|
849
|
-
blob.hlAngle += blob.hlAngleSpeed;
|
|
900
|
+
blob.hlAngle += blob.hlAngleSpeed * blobSpeed;
|
|
850
901
|
this.updateBlobMouse(particle);
|
|
851
902
|
const color = lg.color ?? DEFAULT_LIQUID_GLASS.color;
|
|
852
903
|
const baseR = parseInt(color.slice(1, 3), 16);
|
|
853
904
|
const baseG = parseInt(color.slice(3, 5), 16);
|
|
854
905
|
const baseB = parseInt(color.slice(5, 7), 16);
|
|
855
906
|
const shadowStr = lg.shadowStrength ?? DEFAULT_LIQUID_GLASS.shadowStrength;
|
|
907
|
+
const refl = (lg.reflectionStrength ?? DEFAULT_LIQUID_GLASS.reflectionStrength) / DEFAULT_LIQUID_GLASS.reflectionStrength;
|
|
856
908
|
const gradR = isRect ? Math.max(particle.width, particle.height) / 2 : r;
|
|
857
909
|
const hlDist = gradR * 0.35;
|
|
858
910
|
const hlX = cx + Math.cos(blob.hlAngle) * hlDist;
|
|
859
911
|
const hlY = cy + Math.sin(blob.hlAngle) * hlDist;
|
|
860
|
-
const lr = Math.min(255, baseR + Math.round((255 - baseR) * 0.55));
|
|
861
|
-
const lgr = Math.min(255, baseG + Math.round((255 - baseG) * 0.55));
|
|
862
|
-
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));
|
|
863
915
|
const dr = Math.max(0, Math.round(baseR * (1 - shadowStr * 0.35)));
|
|
864
916
|
const dg = Math.max(0, Math.round(baseG * (1 - shadowStr * 0.35)));
|
|
865
917
|
const db = Math.max(0, Math.round(baseB * (1 - shadowStr * 0.35)));
|
|
866
918
|
this.ctx.save();
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
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
|
+
}
|
|
880
937
|
if (isRect) {
|
|
881
938
|
const halfW = particle.width / 2;
|
|
882
939
|
const halfH = particle.height / 2;
|
|
@@ -914,7 +971,9 @@ var ParticleNetwork = class {
|
|
|
914
971
|
this.draw3DFluidSphere(particle);
|
|
915
972
|
return;
|
|
916
973
|
}
|
|
917
|
-
|
|
974
|
+
const tc = particle.typeConfig;
|
|
975
|
+
const tcOpacity = tc && "opacity" in tc ? tc.opacity : void 0;
|
|
976
|
+
let opacity = tcOpacity ?? this.config.particleOpacity;
|
|
918
977
|
if (this.config.depthEffectEnabled) {
|
|
919
978
|
opacity *= 0.6 + 0.4 * particle.z;
|
|
920
979
|
}
|
|
@@ -931,8 +990,9 @@ var ParticleNetwork = class {
|
|
|
931
990
|
this.ctx.drawImage(img, x, y, size, size);
|
|
932
991
|
}
|
|
933
992
|
} else if (particle.isChild && particle.width != null && particle.height != null) {
|
|
993
|
+
const color = (tc && "color" in tc ? tc.color : void 0) ?? defaultColor;
|
|
934
994
|
this.ctx.globalAlpha = opacity;
|
|
935
|
-
this.ctx.fillStyle =
|
|
995
|
+
this.ctx.fillStyle = color;
|
|
936
996
|
const w = particle.width;
|
|
937
997
|
const h = particle.height;
|
|
938
998
|
const br = particle.borderRadius ?? Math.min(w, h) / 2;
|
|
@@ -942,8 +1002,9 @@ var ParticleNetwork = class {
|
|
|
942
1002
|
this.ctx.roundRect(x, y, w, h, br);
|
|
943
1003
|
this.ctx.fill();
|
|
944
1004
|
} else {
|
|
1005
|
+
const color = (tc && "color" in tc ? tc.color : void 0) ?? defaultColor;
|
|
945
1006
|
this.ctx.globalAlpha = opacity;
|
|
946
|
-
this.ctx.fillStyle =
|
|
1007
|
+
this.ctx.fillStyle = color;
|
|
947
1008
|
this.ctx.beginPath();
|
|
948
1009
|
this.ctx.arc(particle.x, particle.y, r, 0, Math.PI * 2);
|
|
949
1010
|
this.ctx.fill();
|
|
@@ -1294,6 +1355,9 @@ function BaseChildParticle({
|
|
|
1294
1355
|
anchorForce,
|
|
1295
1356
|
mouseInfluence,
|
|
1296
1357
|
liquidGlass,
|
|
1358
|
+
glassOpacity,
|
|
1359
|
+
glassColor,
|
|
1360
|
+
liquidGlassConfig,
|
|
1297
1361
|
children,
|
|
1298
1362
|
style,
|
|
1299
1363
|
className
|
|
@@ -1313,7 +1377,10 @@ function BaseChildParticle({
|
|
|
1313
1377
|
overflow,
|
|
1314
1378
|
anchorForce,
|
|
1315
1379
|
mouseInfluence,
|
|
1316
|
-
liquidGlass
|
|
1380
|
+
liquidGlass,
|
|
1381
|
+
glassOpacity,
|
|
1382
|
+
glassColor,
|
|
1383
|
+
liquidGlassConfig
|
|
1317
1384
|
});
|
|
1318
1385
|
setOverlayEl(instance.getChildOverlayElement(id));
|
|
1319
1386
|
return () => {
|
|
@@ -1333,9 +1400,12 @@ function BaseChildParticle({
|
|
|
1333
1400
|
overflow,
|
|
1334
1401
|
anchorForce,
|
|
1335
1402
|
mouseInfluence,
|
|
1336
|
-
liquidGlass
|
|
1403
|
+
liquidGlass,
|
|
1404
|
+
glassOpacity,
|
|
1405
|
+
glassColor,
|
|
1406
|
+
liquidGlassConfig
|
|
1337
1407
|
});
|
|
1338
|
-
}, [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]);
|
|
1339
1409
|
if (!overlayEl) return null;
|
|
1340
1410
|
const isRect = width != null && height != null;
|
|
1341
1411
|
const portalBorderRadius = isRect ? (borderRadius ?? Math.min(width, height) / 2) + "px" : "50%";
|
|
@@ -1365,5 +1435,6 @@ function BaseChildParticle({
|
|
|
1365
1435
|
ChildParticle,
|
|
1366
1436
|
GlassChildParticle,
|
|
1367
1437
|
ParticleNetworkBg,
|
|
1438
|
+
ParticleNetworkContext,
|
|
1368
1439
|
useParticleNetwork
|
|
1369
1440
|
});
|
package/dist/react.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ParticleNetwork
|
|
3
|
-
} from "./chunk-
|
|
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
|
};
|