particle-network-bg 0.0.2 → 0.0.3
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 +5 -1
- package/dist/{chunk-WD7BVW5Q.mjs → chunk-35XTC6S6.mjs} +68 -93
- package/dist/index.d.mts +5 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +68 -93
- package/dist/index.mjs +1 -1
- package/dist/react.js +68 -93
- package/dist/react.mjs +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -88,7 +88,7 @@ function App() {
|
|
|
88
88
|
}
|
|
89
89
|
```
|
|
90
90
|
|
|
91
|
-
> **Note:** Config is applied on mount. The canvas resizes to the window.
|
|
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
92
|
|
|
93
93
|
## Configuration
|
|
94
94
|
|
|
@@ -124,9 +124,13 @@ function App() {
|
|
|
124
124
|
| `gradientSpin` | boolean | false | **Linear:** `true` = rotate, `false` = flow continuously |
|
|
125
125
|
| `gradientFlowAngle` | number | 45 | **Linear flow mode:** Direction of color flow (degrees) |
|
|
126
126
|
| `gradientOrbitRadius` | number | 0.3 | **Radial:** Orbit radius for center movement (0–1) |
|
|
127
|
+
| `gradientDithering` | boolean | true | *(Deprecated)* No longer used with CSS gradients |
|
|
128
|
+
| `gradientSmoothStops` | number | 4 | *(Deprecated)* No longer used with CSS gradients |
|
|
127
129
|
|
|
128
130
|
## Gradient Examples
|
|
129
131
|
|
|
132
|
+
Gradients are rendered via CSS (not canvas) for smooth, band-free appearance. A background `<div>` is automatically created behind the canvas.
|
|
133
|
+
|
|
130
134
|
### Linear Gradient (Continuous Flow)
|
|
131
135
|
|
|
132
136
|
```js
|
|
@@ -28,7 +28,9 @@ var DEFAULT_CONFIG = {
|
|
|
28
28
|
gradientRadius: 1,
|
|
29
29
|
gradientSpin: false,
|
|
30
30
|
gradientFlowAngle: 45,
|
|
31
|
-
gradientOrbitRadius: 0.3
|
|
31
|
+
gradientOrbitRadius: 0.3,
|
|
32
|
+
gradientDithering: true,
|
|
33
|
+
gradientSmoothStops: 4
|
|
32
34
|
};
|
|
33
35
|
var HEX_COLOR_REGEX = /^#[0-9A-Fa-f]{6}$/;
|
|
34
36
|
var ParticleNetwork = class {
|
|
@@ -42,6 +44,7 @@ var ParticleNetwork = class {
|
|
|
42
44
|
this.gradientFlowOffset = 0;
|
|
43
45
|
this.gradientCenter = { x: 0, y: 0 };
|
|
44
46
|
this.smoothedMouseAngle = 0;
|
|
47
|
+
this.gradientDiv = null;
|
|
45
48
|
this.canvas = canvas;
|
|
46
49
|
const ctx = canvas.getContext("2d", { alpha: true });
|
|
47
50
|
if (!ctx) {
|
|
@@ -49,6 +52,7 @@ var ParticleNetwork = class {
|
|
|
49
52
|
}
|
|
50
53
|
this.ctx = ctx;
|
|
51
54
|
this.config = this.validateConfig({ ...DEFAULT_CONFIG, ...config });
|
|
55
|
+
this.createGradientDiv();
|
|
52
56
|
this.boundHandleResize = this.handleResize.bind(this);
|
|
53
57
|
this.boundHandleMouseMove = this.handleMouseMove.bind(this);
|
|
54
58
|
this.boundHandleMouseLeave = this.handleMouseLeave.bind(this);
|
|
@@ -56,6 +60,11 @@ var ParticleNetwork = class {
|
|
|
56
60
|
this.createParticles();
|
|
57
61
|
this.setupEventListeners();
|
|
58
62
|
}
|
|
63
|
+
createGradientDiv() {
|
|
64
|
+
this.gradientDiv = document.createElement("div");
|
|
65
|
+
this.gradientDiv.style.cssText = "position:fixed;inset:0;width:100%;height:100%;pointer-events:none;z-index:-1;";
|
|
66
|
+
this.canvas.parentElement?.insertBefore(this.gradientDiv, this.canvas);
|
|
67
|
+
}
|
|
59
68
|
validateConfig(config) {
|
|
60
69
|
const numericParams = [
|
|
61
70
|
"particleCount",
|
|
@@ -75,7 +84,8 @@ var ParticleNetwork = class {
|
|
|
75
84
|
"gradientAngle",
|
|
76
85
|
"gradientRadius",
|
|
77
86
|
"gradientFlowAngle",
|
|
78
|
-
"gradientOrbitRadius"
|
|
87
|
+
"gradientOrbitRadius",
|
|
88
|
+
"gradientSmoothStops"
|
|
79
89
|
];
|
|
80
90
|
for (const param of numericParams) {
|
|
81
91
|
const val = config[param];
|
|
@@ -89,7 +99,8 @@ var ParticleNetwork = class {
|
|
|
89
99
|
"depthEffectEnabled",
|
|
90
100
|
"gradientEnabled",
|
|
91
101
|
"gradientMouseReaction",
|
|
92
|
-
"gradientSpin"
|
|
102
|
+
"gradientSpin",
|
|
103
|
+
"gradientDithering"
|
|
93
104
|
];
|
|
94
105
|
for (const param of booleanParams) {
|
|
95
106
|
if (typeof config[param] !== "boolean") {
|
|
@@ -148,6 +159,8 @@ var ParticleNetwork = class {
|
|
|
148
159
|
window.removeEventListener("resize", this.boundHandleResize);
|
|
149
160
|
this.canvas.removeEventListener("mousemove", this.boundHandleMouseMove);
|
|
150
161
|
this.canvas.removeEventListener("mouseleave", this.boundHandleMouseLeave);
|
|
162
|
+
this.gradientDiv?.remove();
|
|
163
|
+
this.gradientDiv = null;
|
|
151
164
|
this.stop();
|
|
152
165
|
}
|
|
153
166
|
handleMouseMove(e) {
|
|
@@ -277,100 +290,62 @@ var ParticleNetwork = class {
|
|
|
277
290
|
}
|
|
278
291
|
}
|
|
279
292
|
drawBackground() {
|
|
280
|
-
this.
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
if (this.
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
angle = baseAngleRad;
|
|
301
|
-
}
|
|
302
|
-
const cos = Math.cos(angle);
|
|
303
|
-
const sin = Math.sin(angle);
|
|
304
|
-
const lineLen = this.config.gradientSpin ? diag / 2 : diag * 1.5;
|
|
305
|
-
const cx = w / 2 + offsetX;
|
|
306
|
-
const cy = h / 2 + offsetY;
|
|
307
|
-
const x1 = cx - cos * lineLen;
|
|
308
|
-
const y1 = cy - sin * lineLen;
|
|
309
|
-
const x2 = cx + cos * lineLen;
|
|
310
|
-
const y2 = cy + sin * lineLen;
|
|
311
|
-
const gradient = this.ctx.createLinearGradient(x1, y1, x2, y2);
|
|
312
|
-
if (this.config.gradientSpin) {
|
|
313
|
-
colors.forEach((c, i) => gradient.addColorStop(stops[i], c));
|
|
314
|
-
} else {
|
|
315
|
-
const extColors = [...colors, colors[0]];
|
|
316
|
-
for (let rep = 0; rep < 3; rep++) {
|
|
317
|
-
extColors.forEach((c, i) => {
|
|
318
|
-
gradient.addColorStop((rep + i / (extColors.length - 1)) / 3, c);
|
|
319
|
-
});
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
this.ctx.fillStyle = gradient;
|
|
323
|
-
} else {
|
|
324
|
-
const cx = w / 2;
|
|
325
|
-
const cy = h / 2;
|
|
326
|
-
const orbitR = Math.min(w, h) * this.config.gradientOrbitRadius;
|
|
327
|
-
let targetX;
|
|
328
|
-
let targetY;
|
|
293
|
+
if (this.config.gradientEnabled && this.gradientDiv) {
|
|
294
|
+
this.gradientDiv.style.display = "";
|
|
295
|
+
this.gradientDiv.style.opacity = String(this.config.backgroundOpacity);
|
|
296
|
+
this.updateGradientCSS();
|
|
297
|
+
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
|
298
|
+
} else {
|
|
299
|
+
if (this.gradientDiv) this.gradientDiv.style.display = "none";
|
|
300
|
+
this.ctx.globalAlpha = this.config.backgroundOpacity;
|
|
301
|
+
this.ctx.fillStyle = this.config.backgroundColor;
|
|
302
|
+
this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
|
|
303
|
+
this.ctx.globalAlpha = 1;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
updateGradientCSS() {
|
|
307
|
+
if (!this.gradientDiv) return;
|
|
308
|
+
const w = this.canvas.width;
|
|
309
|
+
const h = this.canvas.height;
|
|
310
|
+
const colors = this.config.gradientColors;
|
|
311
|
+
if (this.config.gradientType === "linear") {
|
|
312
|
+
if (this.config.gradientSpin) {
|
|
329
313
|
this.gradientAngle += this.config.gradientSpeed;
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
this.
|
|
339
|
-
this.
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
const r = Math.max(w, h) * this.config.gradientRadius * 2;
|
|
343
|
-
const gradient = this.ctx.createRadialGradient(
|
|
344
|
-
this.gradientCenter.x,
|
|
345
|
-
this.gradientCenter.y,
|
|
346
|
-
0,
|
|
347
|
-
this.gradientCenter.x,
|
|
348
|
-
this.gradientCenter.y,
|
|
349
|
-
r
|
|
350
|
-
);
|
|
351
|
-
const extColors = [...colors, colors[0]];
|
|
352
|
-
const entries = [];
|
|
353
|
-
for (let rep = 0; rep < 3; rep++) {
|
|
354
|
-
extColors.forEach((c, i) => {
|
|
355
|
-
const base = (rep + i / (extColors.length - 1)) / 3;
|
|
356
|
-
entries.push({ stop: (base + t) % 1, color: c });
|
|
357
|
-
});
|
|
358
|
-
}
|
|
359
|
-
entries.sort((a, b) => a.stop - b.stop);
|
|
360
|
-
if (entries[0].stop > 1e-3) {
|
|
361
|
-
entries.unshift({ stop: 0, color: entries[entries.length - 1].color });
|
|
362
|
-
}
|
|
363
|
-
if (entries[entries.length - 1].stop < 0.999) {
|
|
364
|
-
entries.push({ stop: 1, color: entries[0].color });
|
|
365
|
-
}
|
|
366
|
-
entries.forEach((e) => gradient.addColorStop(e.stop, e.color));
|
|
367
|
-
this.ctx.fillStyle = gradient;
|
|
314
|
+
const deg = this.gradientAngle * 180 / Math.PI;
|
|
315
|
+
this.gradientDiv.style.background = `linear-gradient(${deg}deg, ${colors.join(", ")})`;
|
|
316
|
+
this.gradientDiv.style.backgroundSize = "";
|
|
317
|
+
this.gradientDiv.style.backgroundPosition = "";
|
|
318
|
+
} else {
|
|
319
|
+
const angle = this.config.gradientFlowAngle;
|
|
320
|
+
const cycle = [...colors, ...colors, ...colors, colors[0]];
|
|
321
|
+
this.gradientDiv.style.background = `linear-gradient(${angle}deg, ${cycle.join(", ")})`;
|
|
322
|
+
this.gradientDiv.style.backgroundSize = "300% 300%";
|
|
323
|
+
this.gradientFlowOffset = (this.gradientFlowOffset + this.config.gradientSpeed * 0.5) % 100;
|
|
324
|
+
const pct = this.gradientFlowOffset;
|
|
325
|
+
this.gradientDiv.style.backgroundPosition = `${pct}% ${pct}%`;
|
|
368
326
|
}
|
|
369
327
|
} else {
|
|
370
|
-
|
|
328
|
+
const cx = w / 2;
|
|
329
|
+
const cy = h / 2;
|
|
330
|
+
const orbitR = Math.min(w, h) * this.config.gradientOrbitRadius;
|
|
331
|
+
this.gradientAngle += this.config.gradientSpeed;
|
|
332
|
+
let targetX = cx + Math.cos(this.gradientAngle) * orbitR;
|
|
333
|
+
let targetY = cy + Math.sin(this.gradientAngle) * orbitR;
|
|
334
|
+
if (this.config.gradientMouseReaction && this.mousePosition) {
|
|
335
|
+
const influence = this.config.gradientMouseInfluence;
|
|
336
|
+
targetX += (this.mousePosition.x - targetX) * influence;
|
|
337
|
+
targetY += (this.mousePosition.y - targetY) * influence;
|
|
338
|
+
}
|
|
339
|
+
this.gradientCenter.x += (targetX - this.gradientCenter.x) * 0.03;
|
|
340
|
+
this.gradientCenter.y += (targetY - this.gradientCenter.y) * 0.03;
|
|
341
|
+
const r = Math.max(w, h) * this.config.gradientRadius;
|
|
342
|
+
const cycle = [...colors, ...colors, colors[0]];
|
|
343
|
+
const step = r * 2 / (cycle.length - 1);
|
|
344
|
+
const stops = cycle.map((c, i) => `${c} ${i * step}px`).join(", ");
|
|
345
|
+
this.gradientDiv.style.background = `radial-gradient(circle at ${this.gradientCenter.x}px ${this.gradientCenter.y}px, ${stops})`;
|
|
346
|
+
this.gradientDiv.style.backgroundSize = "";
|
|
347
|
+
this.gradientDiv.style.backgroundPosition = "";
|
|
371
348
|
}
|
|
372
|
-
this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
|
|
373
|
-
this.ctx.globalAlpha = 1;
|
|
374
349
|
}
|
|
375
350
|
hexToRgb(hex) {
|
|
376
351
|
hex = hex.replace(/^#/, "");
|
package/dist/index.d.mts
CHANGED
|
@@ -30,6 +30,8 @@ interface ParticleNetworkConfig {
|
|
|
30
30
|
gradientSpin: boolean;
|
|
31
31
|
gradientFlowAngle: number;
|
|
32
32
|
gradientOrbitRadius: number;
|
|
33
|
+
gradientDithering: boolean;
|
|
34
|
+
gradientSmoothStops: number;
|
|
33
35
|
}
|
|
34
36
|
interface Particle {
|
|
35
37
|
x: number;
|
|
@@ -54,10 +56,12 @@ declare class ParticleNetwork {
|
|
|
54
56
|
private gradientFlowOffset;
|
|
55
57
|
private gradientCenter;
|
|
56
58
|
private smoothedMouseAngle;
|
|
59
|
+
private gradientDiv;
|
|
57
60
|
private boundHandleResize;
|
|
58
61
|
private boundHandleMouseMove;
|
|
59
62
|
private boundHandleMouseLeave;
|
|
60
63
|
constructor(canvas: HTMLCanvasElement, config?: Partial<ParticleNetworkConfig>);
|
|
64
|
+
private createGradientDiv;
|
|
61
65
|
private validateConfig;
|
|
62
66
|
private setupEventListeners;
|
|
63
67
|
cleanup(): void;
|
|
@@ -69,6 +73,7 @@ declare class ParticleNetwork {
|
|
|
69
73
|
private drawParticles;
|
|
70
74
|
private drawConnections;
|
|
71
75
|
private drawBackground;
|
|
76
|
+
private updateGradientCSS;
|
|
72
77
|
private hexToRgb;
|
|
73
78
|
stop(): void;
|
|
74
79
|
start(): void;
|
package/dist/index.d.ts
CHANGED
|
@@ -30,6 +30,8 @@ interface ParticleNetworkConfig {
|
|
|
30
30
|
gradientSpin: boolean;
|
|
31
31
|
gradientFlowAngle: number;
|
|
32
32
|
gradientOrbitRadius: number;
|
|
33
|
+
gradientDithering: boolean;
|
|
34
|
+
gradientSmoothStops: number;
|
|
33
35
|
}
|
|
34
36
|
interface Particle {
|
|
35
37
|
x: number;
|
|
@@ -54,10 +56,12 @@ declare class ParticleNetwork {
|
|
|
54
56
|
private gradientFlowOffset;
|
|
55
57
|
private gradientCenter;
|
|
56
58
|
private smoothedMouseAngle;
|
|
59
|
+
private gradientDiv;
|
|
57
60
|
private boundHandleResize;
|
|
58
61
|
private boundHandleMouseMove;
|
|
59
62
|
private boundHandleMouseLeave;
|
|
60
63
|
constructor(canvas: HTMLCanvasElement, config?: Partial<ParticleNetworkConfig>);
|
|
64
|
+
private createGradientDiv;
|
|
61
65
|
private validateConfig;
|
|
62
66
|
private setupEventListeners;
|
|
63
67
|
cleanup(): void;
|
|
@@ -69,6 +73,7 @@ declare class ParticleNetwork {
|
|
|
69
73
|
private drawParticles;
|
|
70
74
|
private drawConnections;
|
|
71
75
|
private drawBackground;
|
|
76
|
+
private updateGradientCSS;
|
|
72
77
|
private hexToRgb;
|
|
73
78
|
stop(): void;
|
|
74
79
|
start(): void;
|
package/dist/index.js
CHANGED
|
@@ -52,7 +52,9 @@ var DEFAULT_CONFIG = {
|
|
|
52
52
|
gradientRadius: 1,
|
|
53
53
|
gradientSpin: false,
|
|
54
54
|
gradientFlowAngle: 45,
|
|
55
|
-
gradientOrbitRadius: 0.3
|
|
55
|
+
gradientOrbitRadius: 0.3,
|
|
56
|
+
gradientDithering: true,
|
|
57
|
+
gradientSmoothStops: 4
|
|
56
58
|
};
|
|
57
59
|
var HEX_COLOR_REGEX = /^#[0-9A-Fa-f]{6}$/;
|
|
58
60
|
var ParticleNetwork = class {
|
|
@@ -66,6 +68,7 @@ var ParticleNetwork = class {
|
|
|
66
68
|
this.gradientFlowOffset = 0;
|
|
67
69
|
this.gradientCenter = { x: 0, y: 0 };
|
|
68
70
|
this.smoothedMouseAngle = 0;
|
|
71
|
+
this.gradientDiv = null;
|
|
69
72
|
this.canvas = canvas;
|
|
70
73
|
const ctx = canvas.getContext("2d", { alpha: true });
|
|
71
74
|
if (!ctx) {
|
|
@@ -73,6 +76,7 @@ var ParticleNetwork = class {
|
|
|
73
76
|
}
|
|
74
77
|
this.ctx = ctx;
|
|
75
78
|
this.config = this.validateConfig({ ...DEFAULT_CONFIG, ...config });
|
|
79
|
+
this.createGradientDiv();
|
|
76
80
|
this.boundHandleResize = this.handleResize.bind(this);
|
|
77
81
|
this.boundHandleMouseMove = this.handleMouseMove.bind(this);
|
|
78
82
|
this.boundHandleMouseLeave = this.handleMouseLeave.bind(this);
|
|
@@ -80,6 +84,11 @@ var ParticleNetwork = class {
|
|
|
80
84
|
this.createParticles();
|
|
81
85
|
this.setupEventListeners();
|
|
82
86
|
}
|
|
87
|
+
createGradientDiv() {
|
|
88
|
+
this.gradientDiv = document.createElement("div");
|
|
89
|
+
this.gradientDiv.style.cssText = "position:fixed;inset:0;width:100%;height:100%;pointer-events:none;z-index:-1;";
|
|
90
|
+
this.canvas.parentElement?.insertBefore(this.gradientDiv, this.canvas);
|
|
91
|
+
}
|
|
83
92
|
validateConfig(config) {
|
|
84
93
|
const numericParams = [
|
|
85
94
|
"particleCount",
|
|
@@ -99,7 +108,8 @@ var ParticleNetwork = class {
|
|
|
99
108
|
"gradientAngle",
|
|
100
109
|
"gradientRadius",
|
|
101
110
|
"gradientFlowAngle",
|
|
102
|
-
"gradientOrbitRadius"
|
|
111
|
+
"gradientOrbitRadius",
|
|
112
|
+
"gradientSmoothStops"
|
|
103
113
|
];
|
|
104
114
|
for (const param of numericParams) {
|
|
105
115
|
const val = config[param];
|
|
@@ -113,7 +123,8 @@ var ParticleNetwork = class {
|
|
|
113
123
|
"depthEffectEnabled",
|
|
114
124
|
"gradientEnabled",
|
|
115
125
|
"gradientMouseReaction",
|
|
116
|
-
"gradientSpin"
|
|
126
|
+
"gradientSpin",
|
|
127
|
+
"gradientDithering"
|
|
117
128
|
];
|
|
118
129
|
for (const param of booleanParams) {
|
|
119
130
|
if (typeof config[param] !== "boolean") {
|
|
@@ -172,6 +183,8 @@ var ParticleNetwork = class {
|
|
|
172
183
|
window.removeEventListener("resize", this.boundHandleResize);
|
|
173
184
|
this.canvas.removeEventListener("mousemove", this.boundHandleMouseMove);
|
|
174
185
|
this.canvas.removeEventListener("mouseleave", this.boundHandleMouseLeave);
|
|
186
|
+
this.gradientDiv?.remove();
|
|
187
|
+
this.gradientDiv = null;
|
|
175
188
|
this.stop();
|
|
176
189
|
}
|
|
177
190
|
handleMouseMove(e) {
|
|
@@ -301,100 +314,62 @@ var ParticleNetwork = class {
|
|
|
301
314
|
}
|
|
302
315
|
}
|
|
303
316
|
drawBackground() {
|
|
304
|
-
this.
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
if (this.
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
angle = baseAngleRad;
|
|
325
|
-
}
|
|
326
|
-
const cos = Math.cos(angle);
|
|
327
|
-
const sin = Math.sin(angle);
|
|
328
|
-
const lineLen = this.config.gradientSpin ? diag / 2 : diag * 1.5;
|
|
329
|
-
const cx = w / 2 + offsetX;
|
|
330
|
-
const cy = h / 2 + offsetY;
|
|
331
|
-
const x1 = cx - cos * lineLen;
|
|
332
|
-
const y1 = cy - sin * lineLen;
|
|
333
|
-
const x2 = cx + cos * lineLen;
|
|
334
|
-
const y2 = cy + sin * lineLen;
|
|
335
|
-
const gradient = this.ctx.createLinearGradient(x1, y1, x2, y2);
|
|
336
|
-
if (this.config.gradientSpin) {
|
|
337
|
-
colors.forEach((c, i) => gradient.addColorStop(stops[i], c));
|
|
338
|
-
} else {
|
|
339
|
-
const extColors = [...colors, colors[0]];
|
|
340
|
-
for (let rep = 0; rep < 3; rep++) {
|
|
341
|
-
extColors.forEach((c, i) => {
|
|
342
|
-
gradient.addColorStop((rep + i / (extColors.length - 1)) / 3, c);
|
|
343
|
-
});
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
this.ctx.fillStyle = gradient;
|
|
347
|
-
} else {
|
|
348
|
-
const cx = w / 2;
|
|
349
|
-
const cy = h / 2;
|
|
350
|
-
const orbitR = Math.min(w, h) * this.config.gradientOrbitRadius;
|
|
351
|
-
let targetX;
|
|
352
|
-
let targetY;
|
|
317
|
+
if (this.config.gradientEnabled && this.gradientDiv) {
|
|
318
|
+
this.gradientDiv.style.display = "";
|
|
319
|
+
this.gradientDiv.style.opacity = String(this.config.backgroundOpacity);
|
|
320
|
+
this.updateGradientCSS();
|
|
321
|
+
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
|
322
|
+
} else {
|
|
323
|
+
if (this.gradientDiv) this.gradientDiv.style.display = "none";
|
|
324
|
+
this.ctx.globalAlpha = this.config.backgroundOpacity;
|
|
325
|
+
this.ctx.fillStyle = this.config.backgroundColor;
|
|
326
|
+
this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
|
|
327
|
+
this.ctx.globalAlpha = 1;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
updateGradientCSS() {
|
|
331
|
+
if (!this.gradientDiv) return;
|
|
332
|
+
const w = this.canvas.width;
|
|
333
|
+
const h = this.canvas.height;
|
|
334
|
+
const colors = this.config.gradientColors;
|
|
335
|
+
if (this.config.gradientType === "linear") {
|
|
336
|
+
if (this.config.gradientSpin) {
|
|
353
337
|
this.gradientAngle += this.config.gradientSpeed;
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
this.
|
|
363
|
-
this.
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
const r = Math.max(w, h) * this.config.gradientRadius * 2;
|
|
367
|
-
const gradient = this.ctx.createRadialGradient(
|
|
368
|
-
this.gradientCenter.x,
|
|
369
|
-
this.gradientCenter.y,
|
|
370
|
-
0,
|
|
371
|
-
this.gradientCenter.x,
|
|
372
|
-
this.gradientCenter.y,
|
|
373
|
-
r
|
|
374
|
-
);
|
|
375
|
-
const extColors = [...colors, colors[0]];
|
|
376
|
-
const entries = [];
|
|
377
|
-
for (let rep = 0; rep < 3; rep++) {
|
|
378
|
-
extColors.forEach((c, i) => {
|
|
379
|
-
const base = (rep + i / (extColors.length - 1)) / 3;
|
|
380
|
-
entries.push({ stop: (base + t) % 1, color: c });
|
|
381
|
-
});
|
|
382
|
-
}
|
|
383
|
-
entries.sort((a, b) => a.stop - b.stop);
|
|
384
|
-
if (entries[0].stop > 1e-3) {
|
|
385
|
-
entries.unshift({ stop: 0, color: entries[entries.length - 1].color });
|
|
386
|
-
}
|
|
387
|
-
if (entries[entries.length - 1].stop < 0.999) {
|
|
388
|
-
entries.push({ stop: 1, color: entries[0].color });
|
|
389
|
-
}
|
|
390
|
-
entries.forEach((e) => gradient.addColorStop(e.stop, e.color));
|
|
391
|
-
this.ctx.fillStyle = gradient;
|
|
338
|
+
const deg = this.gradientAngle * 180 / Math.PI;
|
|
339
|
+
this.gradientDiv.style.background = `linear-gradient(${deg}deg, ${colors.join(", ")})`;
|
|
340
|
+
this.gradientDiv.style.backgroundSize = "";
|
|
341
|
+
this.gradientDiv.style.backgroundPosition = "";
|
|
342
|
+
} else {
|
|
343
|
+
const angle = this.config.gradientFlowAngle;
|
|
344
|
+
const cycle = [...colors, ...colors, ...colors, colors[0]];
|
|
345
|
+
this.gradientDiv.style.background = `linear-gradient(${angle}deg, ${cycle.join(", ")})`;
|
|
346
|
+
this.gradientDiv.style.backgroundSize = "300% 300%";
|
|
347
|
+
this.gradientFlowOffset = (this.gradientFlowOffset + this.config.gradientSpeed * 0.5) % 100;
|
|
348
|
+
const pct = this.gradientFlowOffset;
|
|
349
|
+
this.gradientDiv.style.backgroundPosition = `${pct}% ${pct}%`;
|
|
392
350
|
}
|
|
393
351
|
} else {
|
|
394
|
-
|
|
352
|
+
const cx = w / 2;
|
|
353
|
+
const cy = h / 2;
|
|
354
|
+
const orbitR = Math.min(w, h) * this.config.gradientOrbitRadius;
|
|
355
|
+
this.gradientAngle += this.config.gradientSpeed;
|
|
356
|
+
let targetX = cx + Math.cos(this.gradientAngle) * orbitR;
|
|
357
|
+
let targetY = cy + Math.sin(this.gradientAngle) * orbitR;
|
|
358
|
+
if (this.config.gradientMouseReaction && this.mousePosition) {
|
|
359
|
+
const influence = this.config.gradientMouseInfluence;
|
|
360
|
+
targetX += (this.mousePosition.x - targetX) * influence;
|
|
361
|
+
targetY += (this.mousePosition.y - targetY) * influence;
|
|
362
|
+
}
|
|
363
|
+
this.gradientCenter.x += (targetX - this.gradientCenter.x) * 0.03;
|
|
364
|
+
this.gradientCenter.y += (targetY - this.gradientCenter.y) * 0.03;
|
|
365
|
+
const r = Math.max(w, h) * this.config.gradientRadius;
|
|
366
|
+
const cycle = [...colors, ...colors, colors[0]];
|
|
367
|
+
const step = r * 2 / (cycle.length - 1);
|
|
368
|
+
const stops = cycle.map((c, i) => `${c} ${i * step}px`).join(", ");
|
|
369
|
+
this.gradientDiv.style.background = `radial-gradient(circle at ${this.gradientCenter.x}px ${this.gradientCenter.y}px, ${stops})`;
|
|
370
|
+
this.gradientDiv.style.backgroundSize = "";
|
|
371
|
+
this.gradientDiv.style.backgroundPosition = "";
|
|
395
372
|
}
|
|
396
|
-
this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
|
|
397
|
-
this.ctx.globalAlpha = 1;
|
|
398
373
|
}
|
|
399
374
|
hexToRgb(hex) {
|
|
400
375
|
hex = hex.replace(/^#/, "");
|
package/dist/index.mjs
CHANGED
package/dist/react.js
CHANGED
|
@@ -56,7 +56,9 @@ var DEFAULT_CONFIG = {
|
|
|
56
56
|
gradientRadius: 1,
|
|
57
57
|
gradientSpin: false,
|
|
58
58
|
gradientFlowAngle: 45,
|
|
59
|
-
gradientOrbitRadius: 0.3
|
|
59
|
+
gradientOrbitRadius: 0.3,
|
|
60
|
+
gradientDithering: true,
|
|
61
|
+
gradientSmoothStops: 4
|
|
60
62
|
};
|
|
61
63
|
var HEX_COLOR_REGEX = /^#[0-9A-Fa-f]{6}$/;
|
|
62
64
|
var ParticleNetwork = class {
|
|
@@ -70,6 +72,7 @@ var ParticleNetwork = class {
|
|
|
70
72
|
this.gradientFlowOffset = 0;
|
|
71
73
|
this.gradientCenter = { x: 0, y: 0 };
|
|
72
74
|
this.smoothedMouseAngle = 0;
|
|
75
|
+
this.gradientDiv = null;
|
|
73
76
|
this.canvas = canvas;
|
|
74
77
|
const ctx = canvas.getContext("2d", { alpha: true });
|
|
75
78
|
if (!ctx) {
|
|
@@ -77,6 +80,7 @@ var ParticleNetwork = class {
|
|
|
77
80
|
}
|
|
78
81
|
this.ctx = ctx;
|
|
79
82
|
this.config = this.validateConfig({ ...DEFAULT_CONFIG, ...config });
|
|
83
|
+
this.createGradientDiv();
|
|
80
84
|
this.boundHandleResize = this.handleResize.bind(this);
|
|
81
85
|
this.boundHandleMouseMove = this.handleMouseMove.bind(this);
|
|
82
86
|
this.boundHandleMouseLeave = this.handleMouseLeave.bind(this);
|
|
@@ -84,6 +88,11 @@ var ParticleNetwork = class {
|
|
|
84
88
|
this.createParticles();
|
|
85
89
|
this.setupEventListeners();
|
|
86
90
|
}
|
|
91
|
+
createGradientDiv() {
|
|
92
|
+
this.gradientDiv = document.createElement("div");
|
|
93
|
+
this.gradientDiv.style.cssText = "position:fixed;inset:0;width:100%;height:100%;pointer-events:none;z-index:-1;";
|
|
94
|
+
this.canvas.parentElement?.insertBefore(this.gradientDiv, this.canvas);
|
|
95
|
+
}
|
|
87
96
|
validateConfig(config) {
|
|
88
97
|
const numericParams = [
|
|
89
98
|
"particleCount",
|
|
@@ -103,7 +112,8 @@ var ParticleNetwork = class {
|
|
|
103
112
|
"gradientAngle",
|
|
104
113
|
"gradientRadius",
|
|
105
114
|
"gradientFlowAngle",
|
|
106
|
-
"gradientOrbitRadius"
|
|
115
|
+
"gradientOrbitRadius",
|
|
116
|
+
"gradientSmoothStops"
|
|
107
117
|
];
|
|
108
118
|
for (const param of numericParams) {
|
|
109
119
|
const val = config[param];
|
|
@@ -117,7 +127,8 @@ var ParticleNetwork = class {
|
|
|
117
127
|
"depthEffectEnabled",
|
|
118
128
|
"gradientEnabled",
|
|
119
129
|
"gradientMouseReaction",
|
|
120
|
-
"gradientSpin"
|
|
130
|
+
"gradientSpin",
|
|
131
|
+
"gradientDithering"
|
|
121
132
|
];
|
|
122
133
|
for (const param of booleanParams) {
|
|
123
134
|
if (typeof config[param] !== "boolean") {
|
|
@@ -176,6 +187,8 @@ var ParticleNetwork = class {
|
|
|
176
187
|
window.removeEventListener("resize", this.boundHandleResize);
|
|
177
188
|
this.canvas.removeEventListener("mousemove", this.boundHandleMouseMove);
|
|
178
189
|
this.canvas.removeEventListener("mouseleave", this.boundHandleMouseLeave);
|
|
190
|
+
this.gradientDiv?.remove();
|
|
191
|
+
this.gradientDiv = null;
|
|
179
192
|
this.stop();
|
|
180
193
|
}
|
|
181
194
|
handleMouseMove(e) {
|
|
@@ -305,100 +318,62 @@ var ParticleNetwork = class {
|
|
|
305
318
|
}
|
|
306
319
|
}
|
|
307
320
|
drawBackground() {
|
|
308
|
-
this.
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
if (this.
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
angle = baseAngleRad;
|
|
329
|
-
}
|
|
330
|
-
const cos = Math.cos(angle);
|
|
331
|
-
const sin = Math.sin(angle);
|
|
332
|
-
const lineLen = this.config.gradientSpin ? diag / 2 : diag * 1.5;
|
|
333
|
-
const cx = w / 2 + offsetX;
|
|
334
|
-
const cy = h / 2 + offsetY;
|
|
335
|
-
const x1 = cx - cos * lineLen;
|
|
336
|
-
const y1 = cy - sin * lineLen;
|
|
337
|
-
const x2 = cx + cos * lineLen;
|
|
338
|
-
const y2 = cy + sin * lineLen;
|
|
339
|
-
const gradient = this.ctx.createLinearGradient(x1, y1, x2, y2);
|
|
340
|
-
if (this.config.gradientSpin) {
|
|
341
|
-
colors.forEach((c, i) => gradient.addColorStop(stops[i], c));
|
|
342
|
-
} else {
|
|
343
|
-
const extColors = [...colors, colors[0]];
|
|
344
|
-
for (let rep = 0; rep < 3; rep++) {
|
|
345
|
-
extColors.forEach((c, i) => {
|
|
346
|
-
gradient.addColorStop((rep + i / (extColors.length - 1)) / 3, c);
|
|
347
|
-
});
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
this.ctx.fillStyle = gradient;
|
|
351
|
-
} else {
|
|
352
|
-
const cx = w / 2;
|
|
353
|
-
const cy = h / 2;
|
|
354
|
-
const orbitR = Math.min(w, h) * this.config.gradientOrbitRadius;
|
|
355
|
-
let targetX;
|
|
356
|
-
let targetY;
|
|
321
|
+
if (this.config.gradientEnabled && this.gradientDiv) {
|
|
322
|
+
this.gradientDiv.style.display = "";
|
|
323
|
+
this.gradientDiv.style.opacity = String(this.config.backgroundOpacity);
|
|
324
|
+
this.updateGradientCSS();
|
|
325
|
+
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
|
326
|
+
} else {
|
|
327
|
+
if (this.gradientDiv) this.gradientDiv.style.display = "none";
|
|
328
|
+
this.ctx.globalAlpha = this.config.backgroundOpacity;
|
|
329
|
+
this.ctx.fillStyle = this.config.backgroundColor;
|
|
330
|
+
this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
|
|
331
|
+
this.ctx.globalAlpha = 1;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
updateGradientCSS() {
|
|
335
|
+
if (!this.gradientDiv) return;
|
|
336
|
+
const w = this.canvas.width;
|
|
337
|
+
const h = this.canvas.height;
|
|
338
|
+
const colors = this.config.gradientColors;
|
|
339
|
+
if (this.config.gradientType === "linear") {
|
|
340
|
+
if (this.config.gradientSpin) {
|
|
357
341
|
this.gradientAngle += this.config.gradientSpeed;
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
this.
|
|
367
|
-
this.
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
const r = Math.max(w, h) * this.config.gradientRadius * 2;
|
|
371
|
-
const gradient = this.ctx.createRadialGradient(
|
|
372
|
-
this.gradientCenter.x,
|
|
373
|
-
this.gradientCenter.y,
|
|
374
|
-
0,
|
|
375
|
-
this.gradientCenter.x,
|
|
376
|
-
this.gradientCenter.y,
|
|
377
|
-
r
|
|
378
|
-
);
|
|
379
|
-
const extColors = [...colors, colors[0]];
|
|
380
|
-
const entries = [];
|
|
381
|
-
for (let rep = 0; rep < 3; rep++) {
|
|
382
|
-
extColors.forEach((c, i) => {
|
|
383
|
-
const base = (rep + i / (extColors.length - 1)) / 3;
|
|
384
|
-
entries.push({ stop: (base + t) % 1, color: c });
|
|
385
|
-
});
|
|
386
|
-
}
|
|
387
|
-
entries.sort((a, b) => a.stop - b.stop);
|
|
388
|
-
if (entries[0].stop > 1e-3) {
|
|
389
|
-
entries.unshift({ stop: 0, color: entries[entries.length - 1].color });
|
|
390
|
-
}
|
|
391
|
-
if (entries[entries.length - 1].stop < 0.999) {
|
|
392
|
-
entries.push({ stop: 1, color: entries[0].color });
|
|
393
|
-
}
|
|
394
|
-
entries.forEach((e) => gradient.addColorStop(e.stop, e.color));
|
|
395
|
-
this.ctx.fillStyle = gradient;
|
|
342
|
+
const deg = this.gradientAngle * 180 / Math.PI;
|
|
343
|
+
this.gradientDiv.style.background = `linear-gradient(${deg}deg, ${colors.join(", ")})`;
|
|
344
|
+
this.gradientDiv.style.backgroundSize = "";
|
|
345
|
+
this.gradientDiv.style.backgroundPosition = "";
|
|
346
|
+
} else {
|
|
347
|
+
const angle = this.config.gradientFlowAngle;
|
|
348
|
+
const cycle = [...colors, ...colors, ...colors, colors[0]];
|
|
349
|
+
this.gradientDiv.style.background = `linear-gradient(${angle}deg, ${cycle.join(", ")})`;
|
|
350
|
+
this.gradientDiv.style.backgroundSize = "300% 300%";
|
|
351
|
+
this.gradientFlowOffset = (this.gradientFlowOffset + this.config.gradientSpeed * 0.5) % 100;
|
|
352
|
+
const pct = this.gradientFlowOffset;
|
|
353
|
+
this.gradientDiv.style.backgroundPosition = `${pct}% ${pct}%`;
|
|
396
354
|
}
|
|
397
355
|
} else {
|
|
398
|
-
|
|
356
|
+
const cx = w / 2;
|
|
357
|
+
const cy = h / 2;
|
|
358
|
+
const orbitR = Math.min(w, h) * this.config.gradientOrbitRadius;
|
|
359
|
+
this.gradientAngle += this.config.gradientSpeed;
|
|
360
|
+
let targetX = cx + Math.cos(this.gradientAngle) * orbitR;
|
|
361
|
+
let targetY = cy + Math.sin(this.gradientAngle) * orbitR;
|
|
362
|
+
if (this.config.gradientMouseReaction && this.mousePosition) {
|
|
363
|
+
const influence = this.config.gradientMouseInfluence;
|
|
364
|
+
targetX += (this.mousePosition.x - targetX) * influence;
|
|
365
|
+
targetY += (this.mousePosition.y - targetY) * influence;
|
|
366
|
+
}
|
|
367
|
+
this.gradientCenter.x += (targetX - this.gradientCenter.x) * 0.03;
|
|
368
|
+
this.gradientCenter.y += (targetY - this.gradientCenter.y) * 0.03;
|
|
369
|
+
const r = Math.max(w, h) * this.config.gradientRadius;
|
|
370
|
+
const cycle = [...colors, ...colors, colors[0]];
|
|
371
|
+
const step = r * 2 / (cycle.length - 1);
|
|
372
|
+
const stops = cycle.map((c, i) => `${c} ${i * step}px`).join(", ");
|
|
373
|
+
this.gradientDiv.style.background = `radial-gradient(circle at ${this.gradientCenter.x}px ${this.gradientCenter.y}px, ${stops})`;
|
|
374
|
+
this.gradientDiv.style.backgroundSize = "";
|
|
375
|
+
this.gradientDiv.style.backgroundPosition = "";
|
|
399
376
|
}
|
|
400
|
-
this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
|
|
401
|
-
this.ctx.globalAlpha = 1;
|
|
402
377
|
}
|
|
403
378
|
hexToRgb(hex) {
|
|
404
379
|
hex = hex.replace(/^#/, "");
|
package/dist/react.mjs
CHANGED