jl-particle-interactive 0.2.0 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,38 @@
1
- # jl-particle-interactive
1
+ # jl-particle-interactive — React Canvas Particle Animations
2
2
 
3
- Particle animations for React — text that comes alive and backgrounds that react to your cursor.
3
+ [![npm version](https://img.shields.io/npm/v/jl-particle-interactive)](https://www.npmjs.com/package/jl-particle-interactive)
4
+ [![npm downloads](https://img.shields.io/npm/dm/jl-particle-interactive)](https://www.npmjs.com/package/jl-particle-interactive)
5
+ [![bundle size](https://img.shields.io/bundlephobia/minzip/jl-particle-interactive)](https://bundlephobia.com/package/jl-particle-interactive)
6
+ [![license](https://img.shields.io/npm/l/jl-particle-interactive)](https://github.com/cjorgeluis122333/jl-particles-interactive/blob/main/LICENSE)
7
+ [![TypeScript](https://img.shields.io/badge/TypeScript-strict-blue)](https://www.typescriptlang.org/)
8
+
9
+ A canvas-based React library for rendering text and backgrounds as thousands of animated particles. Letters form from particle swarms, respond to magnetic hover, attract or repel on click, and backgrounds come alive with NET graphs, JELLYFISH glows, or pointer-following swarms. Built with zero runtime dependencies, full TypeScript support, and DPR-aware rendering for sharp output on retina displays.
10
+
11
+ > Requires React 18+. No global CSS. Zero runtime dependencies.
12
+
13
+ ---
14
+
15
+ ## Why jl-particle-interactive?
16
+
17
+ | Feature | jl-particle-interactive | tsparticles | particles.js |
18
+ |---|---|---|---|
19
+ | **Text that forms from particles** | ✓ (native, spatial coherence) | Plugin only (complex setup) | ✗ |
20
+ | **Spring physics + float noise** | ✓ | ✗ | ✗ |
21
+ | **Magnetic hover / click interact** | ✓ (attract & repel) | ✗ | ✗ |
22
+ | **Ready-made background presets** | ✓ NET, JELLYFISH, FOLLOW_POINTER | General engine (DIY) | General engine (DIY) |
23
+ | **Runtime dependencies** | **Zero** | 14+ packages | 0 (vanilla JS only) |
24
+ | **React integration** | Native React hooks | Wrapper package needed | Manual integration |
25
+ | **TypeScript** | Strict mode | Partial | ✗ |
26
+ | **DPR-aware (retina)** | ✓ | ✗ | ✗ |
27
+
28
+ ---
29
+
30
+ ## Use cases
31
+
32
+ - **Hero section animated titles** — Text materializes from particle chaos on page load
33
+ - **Loading screens** — Words form and dissolve while content loads
34
+ - **Interactive word carousels** — Cycle through words; particles re-form smoothly
35
+ - **Animated backgrounds** — NET graph, JELLYFISH glow, or pointer-following swarm behind any content
4
36
 
5
37
  ---
6
38
 
@@ -10,7 +42,18 @@ Particle animations for React — text that comes alive and backgrounds that rea
10
42
  npm install jl-particle-interactive
11
43
  ```
12
44
 
13
- > Requires React 18+. No global CSS. No extra dependencies.
45
+ ---
46
+
47
+ ## Framework compatibility
48
+
49
+ | Framework | Supported |
50
+ |---|---|
51
+ | Vite + React | ✓ |
52
+ | Next.js (App Router & Pages) | ✓ (client components only — add `'use client'`) |
53
+ | Create React App | ✓ |
54
+ | Remix | ✓ (client-side only) |
55
+ | Astro | ✓ (inside `client:only` components) |
56
+ | TypeScript | ✓ (strict mode, declarations included) |
14
57
 
15
58
  ---
16
59
 
@@ -18,11 +61,12 @@ npm install jl-particle-interactive
18
61
 
19
62
  | Feature | Description |
20
63
  |---|---|
21
- | **Text particles** | Thousands of particles that form any text string |
22
- | **Magnetic hover** | Particles are attracted to the cursor on hover |
23
- | **Click interactions** | Attract or repel particles on click/tap |
24
- | **Animated backgrounds** | NET, JELLYFISH, and FOLLOW_POINTER modes |
25
- | **Customizable** | Colors, shapes, density, speed, and more |
64
+ | **Text particles** | Thousands of canvas particles that form any text string using pixel-sampling with spatial coherence — letters look sharp at any size |
65
+ | **Magnetic hover** | Particles are attracted to the cursor on hover (~173px radius spring force) |
66
+ | **Click interactions** | Attract or repel particles on click/tap — particles flee or swarm toward the pointer |
67
+ | **Animated backgrounds** | NET (connected node graph), JELLYFISH (organic glow rings), and FOLLOW_POINTER (swarm) modes |
68
+ | **Spring physics** | Each particle uses spring + friction + float-noise physics — movement feels natural, never robotic |
69
+ | **Customizable** | Colors (hex or RGB palettes), shapes (circle, square, bean), density, speed, ease, and more |
26
70
 
27
71
  ---
28
72
 
@@ -102,7 +146,6 @@ A connected particle network moves behind your content.
102
146
  background={{
103
147
  name: 'NET',
104
148
  color: '#4ecdc4',
105
- lineDistance: 120,
106
149
  density: 0.8,
107
150
  }}
108
151
  >
@@ -156,6 +199,149 @@ export default function Carousel() {
156
199
 
157
200
  ---
158
201
 
202
+ ## Background guide
203
+
204
+ The `background` prop on `ParticleCanvas` supports four usage patterns:
205
+
206
+ 1. No animated background
207
+ 2. `FOLLOW_POINTER` swarm background
208
+ 3. `NET` node-link graph background
209
+ 4. `JELLYFISH` organic glow/swim background
210
+
211
+ ### 1) No background engine
212
+
213
+ Use this when you only want text particles.
214
+
215
+ ```tsx
216
+ <ParticleCanvas background={{ name: 'NONE' }}>
217
+ <TextParticleEngine text="Only Text" />
218
+ </ParticleCanvas>
219
+ ```
220
+
221
+ ### 2) FOLLOW_POINTER background
222
+
223
+ Particles form a swarm that follows the cursor smoothly.
224
+
225
+ ```tsx
226
+ <ParticleCanvas
227
+ background={{
228
+ name: 'FOLLOW_POINTER',
229
+ orientation: 'diagonal',
230
+ density: 1,
231
+ shape: 'bean',
232
+ colors: ['#00d4ff', '#6ee7b7', '#facc15'],
233
+ colorMode: 'wave',
234
+ particleSpeed: 1,
235
+ pointerTrackingSpeed: 0.06,
236
+ }}
237
+ >
238
+ <TextParticleEngine text="Follow" particleColor="255, 255, 255" />
239
+ </ParticleCanvas>
240
+ ```
241
+
242
+ ### 3) NET background
243
+
244
+ Moving nodes connect with lines. Great for tech-style hero sections.
245
+
246
+ ```tsx
247
+ <ParticleCanvas
248
+ background={{
249
+ name: 'NET',
250
+ density: 0.9,
251
+ shape: 'circle',
252
+ colors: ['#7dd3fc', '#60a5fa'],
253
+ colorMode: 'mixed',
254
+ particleSpeed: 1,
255
+ pointerTrackingSpeed: 0.08,
256
+ }}
257
+ >
258
+ <TextParticleEngine text="Network" particleColor="255, 255, 255" />
259
+ </ParticleCanvas>
260
+ ```
261
+
262
+ ### 4) JELLYFISH background
263
+
264
+ Soft organic particle body with pulse/swimming motion.
265
+
266
+ ```tsx
267
+ <ParticleCanvas
268
+ background={{
269
+ name: 'JELLYFISH',
270
+ density: 1.1,
271
+ shape: 'bean',
272
+ colors: ['#f472b6', '#a78bfa', '#22d3ee'],
273
+ colorMode: 'wave',
274
+ particleSpeed: 1,
275
+ pointerTrackingSpeed: 0.02,
276
+ }}
277
+ >
278
+ <TextParticleEngine text="Jelly" particleColor="255, 255, 255" />
279
+ </ParticleCanvas>
280
+ ```
281
+
282
+ ### Background with transparent stage
283
+
284
+ Use `backgroundColor="transparent"` to place particles over your own image/gradient layer.
285
+
286
+ ```tsx
287
+ <div style={{ background: 'linear-gradient(135deg, #0f172a, #111827)' }}>
288
+ <ParticleCanvas
289
+ height="70vh"
290
+ backgroundColor="transparent"
291
+ background={{ name: 'NET', density: 0.7, color: '#67e8f9' }}
292
+ >
293
+ <TextParticleEngine text="Overlay" backgroundColor="transparent" />
294
+ </ParticleCanvas>
295
+ </div>
296
+ ```
297
+
298
+ ### Full `BackgroundCanvas` type
299
+
300
+ ```ts
301
+ type BackgroundModeName = 'NONE' | 'FOLLOW_POINTER' | 'NET' | 'JELLYFISH';
302
+ type ParticleOrientation = 'vertical' | 'horizontal' | 'diagonal';
303
+
304
+ interface BackgroundCanvas {
305
+ name: BackgroundModeName;
306
+ orientation?: ParticleOrientation;
307
+ density?: number;
308
+ color?: string;
309
+ colors?: string[];
310
+ colorMode?: 'wave' | 'mixed';
311
+ interactionRadius?: number;
312
+ lineDistance?: number;
313
+ shape?: 'circle' | 'square' | 'bean';
314
+ particleSpeed?: number;
315
+ pointerTrackingSpeed?: number;
316
+ }
317
+ ```
318
+
319
+ ### Option matrix (what applies to each mode)
320
+
321
+ | Option | FOLLOW_POINTER | NET | JELLYFISH | Notes |
322
+ |---|---|---|---|---|
323
+ | `name` | ✓ | ✓ | ✓ | Mode selector |
324
+ | `density` | ✓ | ✓ | ✓ | Particle count multiplier |
325
+ | `color` | ✓ | ✓ | ✓ | Single hex/HSL/CSS color |
326
+ | `colors` | ✓ | ✓ | ✓ | Palette override |
327
+ | `colorMode` | ✓ | ✓ | ✓ | `wave` (default) or `mixed` |
328
+ | `shape` | ✓ | ✓ | ✓ | `bean` default in FOLLOW_POINTER, `circle` in NET/JELLYFISH |
329
+ | `particleSpeed` | ✓ | ✓ | ✓ | Animation speed multiplier |
330
+ | `pointerTrackingSpeed` | ✓ | ✓ | ✓ | Cursor-follow/response smoothness |
331
+ | `orientation` | ✓ | — | — | Only FOLLOW_POINTER (`vertical` default) |
332
+ | `interactionRadius` | — | — | — | Declared in type, currently not applied in v0.2.1 |
333
+ | `lineDistance` | — | — | — | Declared in type, currently not applied in v0.2.1 |
334
+
335
+ ### Practical presets
336
+
337
+ | Goal | Suggested config |
338
+ |---|---|
339
+ | Calm ambient hero | `JELLYFISH`, `density: 0.8`, `pointerTrackingSpeed: 0.015` |
340
+ | High-energy interactive background | `FOLLOW_POINTER`, `density: 1.2`, `particleSpeed: 1.3`, `shape: 'bean'` |
341
+ | Tech/network look | `NET`, `density: 0.9`, `colorMode: 'mixed'`, cool blue palette |
342
+
343
+ ---
344
+
159
345
  ## API reference
160
346
 
161
347
  ### `<ParticleCanvas>`
@@ -169,6 +355,22 @@ export default function Carousel() {
169
355
  | `className` | `string` | `''` | Additional CSS class |
170
356
  | `style` | `CSSProperties` | — | Inline style overrides |
171
357
 
358
+ ### `BackgroundCanvas`
359
+
360
+ | Prop | Type | Description |
361
+ |---|---|---|
362
+ | `name` | `'NONE' \| 'FOLLOW_POINTER' \| 'NET' \| 'JELLYFISH'` | Background mode |
363
+ | `orientation` | `'vertical' \| 'horizontal' \| 'diagonal'` | Direction style for `FOLLOW_POINTER` |
364
+ | `density` | `number` | Particle amount multiplier |
365
+ | `color` | `string` | Single background particle color |
366
+ | `colors` | `string[]` | Background particle palette |
367
+ | `colorMode` | `'wave' \| 'mixed'` | Palette propagation mode |
368
+ | `shape` | `'circle' \| 'square' \| 'bean'` | Particle drawing shape |
369
+ | `particleSpeed` | `number` | Background animation speed multiplier |
370
+ | `pointerTrackingSpeed` | `number` | Cursor tracking interpolation factor |
371
+ | `interactionRadius` | `number` | Reserved in type (not applied in v0.2.1) |
372
+ | `lineDistance` | `number` | Reserved in type (not applied in v0.2.1) |
373
+
172
374
  ### `<TextParticleEngine>`
173
375
 
174
376
  | Prop | Type | Default | Description |
package/dist/index.d.ts CHANGED
@@ -2,6 +2,12 @@ export { default as ParticleCanvas } from './components/ParticleCanvas';
2
2
  export type { ParticleCanvasProps } from './components/ParticleCanvas';
3
3
  export { default as TextParticleEngine } from './components/text/TextParticleEngine';
4
4
  export type { TextParticleEngineProps } from './components/text/TextParticleEngine';
5
+ export { default as BackgroundParticleEngine } from './components/background/BackgroundParticleEngine';
6
+ export type { BackgroundParticleEngineProps } from './components/background/BackgroundParticleEngine';
7
+ export { default as NetParticleEngine } from './components/background/NetParticleEngine';
8
+ export type { NetParticleEngineProps } from './components/background/NetParticleEngine';
9
+ export { default as JellyfishParticleEngine } from './components/background/JellyfishParticleEngine';
10
+ export type { JellyfishParticleEngineProps } from './components/background/JellyfishParticleEngine';
5
11
  export { useParticleInteraction, getMagnetTarget } from './hooks/useParticleInteraction';
6
12
  export type { ClickMode } from './hooks/useParticleInteraction';
7
13
  export { useTextParticles } from './hooks/text/useTextParticles';
@@ -761,6 +761,9 @@ function yt({
761
761
  );
762
762
  }
763
763
  export {
764
+ et as BackgroundParticleEngine,
765
+ ot as JellyfishParticleEngine,
766
+ rt as NetParticleEngine,
764
767
  ft as ParticleCanvas,
765
768
  yt as TextParticleEngine,
766
769
  at as getMagnetTarget,
@@ -1 +1 @@
1
- (function(A,q){typeof exports=="object"&&typeof module<"u"?q(exports,require("react/jsx-runtime"),require("react")):typeof define=="function"&&define.amd?define(["exports","react/jsx-runtime","react"],q):(A=typeof globalThis<"u"?globalThis:A||self,q(A.JlParticleInteractive={},A.ReactJsxRuntime,A.React))})(this,(function(A,q,f){"use strict";var lt=Object.defineProperty;var ht=(A,q,f)=>q in A?lt(A,q,{enumerable:!0,configurable:!0,writable:!0,value:f}):A[q]=f;var v=(A,q,f)=>ht(A,typeof q!="symbol"?q+"":q,f);function _(t,e,n,i){let s=e+t.baseX,m=n+t.baseY;s+=Math.sin(i*t.randomSpeed+t.baseY*.05)*15*t.z,m+=Math.cos(i*t.randomSpeed+t.baseX*.05)*15*t.z;const r=s-t.x,o=m-t.y,h=r*.06,g=o*.06;return{forceX:h,forceY:g}}function U(t,e,n,i,s,m){const r=i,o=s,h=t.x-r,g=t.y-o,d=Math.max(Math.sqrt(h*h+g*g),1);let c=0,a=0;const u=Math.sin(d*.02-m*3)*2.5*t.z;c+=h/d*u,a+=g/d*u;const E=Math.cos(d*.01-m*1+t.randomSpeed)*.8*t.z,T=-g/d,b=h/d;return c+=T*E,a+=b*E,{forceX:c,forceY:a,dxCenter:h,dyCenter:g,distToCenter:d}}function Q(t,e){let s=Math.min(e/400,1)*2.5+t.sizeBias*1;s<.4&&(s=0),t.scale+=(s-t.scale)*.15}function V(t,e,n,i,s,m){let r=0,o=0;s==="horizontal"?(r=1,o=0):s==="diagonal"?(r=-e/i,o=-n/i):(r=-n/i,o=e/i);const h=Math.atan2(o,r),g=t.sizeBias>.3?1:0,d=Math.max(0,Math.sin(m*.5+t.randomSpeed*10)),c=Math.sin(m*3+t.baseX*.1+t.randomSpeed)*.5*d*g;r=Math.cos(h+c),o=Math.sin(h+c);const a=Math.max(1-i/400,.1);s==="horizontal"?(t.dirX=t.dirX*(1-.2)+r*.2,t.dirY=t.dirY*(1-.2)+o*.2):(t.dirX=t.dirX*(1-a*.3)+r*a*.3,t.dirY=t.dirY*(1-a*.3)+o*a*.3);const l=Math.sqrt(t.dirX*t.dirX+t.dirY*t.dirY);l>0&&(t.dirX/=l,t.dirY/=l)}class Z{constructor(e,n,i){v(this,"x");v(this,"y");v(this,"baseX");v(this,"baseY");v(this,"z");v(this,"vx");v(this,"vy");v(this,"color");v(this,"targetColor",null);v(this,"colorDelay",0);v(this,"angleTarget");v(this,"randomSpeed");v(this,"sizeBias");v(this,"scale");v(this,"dirX");v(this,"dirY");v(this,"initialized",!1);this.x=e,this.y=n,this.baseX=e,this.baseY=n,this.z=Math.random()*1.5+.2,this.vx=0,this.vy=0,this.color=i,this.angleTarget=Math.random()*Math.PI*2,this.randomSpeed=Math.random()*2+1,this.sizeBias=Math.random(),this.scale=1,this.dirX=Math.cos(this.angleTarget),this.dirY=Math.sin(this.angleTarget)}update(e,n,i,s,m,r,o,h="vertical"){this.initialized||(this.x=i+this.baseX,this.y=s+this.baseY,this.initialized=!0);const{forceX:g,forceY:d,dxCenter:c,dyCenter:a,distToCenter:l}=U(this,e,n,i,s,o),{forceX:u,forceY:R}=_(this,i,s,o);this.vx+=g+u,this.vy+=d+R,this.vx*=.75,this.vy*=.75,this.x+=this.vx,this.y+=this.vy,Q(this,l),V(this,c,a,l,h,o),this.targetColor&&(this.colorDelay-=1,this.colorDelay<=0&&(this.color=this.targetColor,this.targetColor=null))}draw(e,n,i="bean"){if(this.scale<=.05)return;e.fillStyle=this.color;const s=Math.sqrt(this.baseX*this.baseX+this.baseY*this.baseY),m=Math.max(0,1-s/350);if(e.globalAlpha=Math.min(1,(.5+this.z*.5)*Math.min(this.scale,1)*m),!(e.globalAlpha<=.01))if(i==="circle"){const r=Math.max(.1,2*this.scale*this.z);e.beginPath(),e.arc(this.x,this.y,r,0,Math.PI*2),e.fill()}else if(i==="square"){const r=Math.max(.1,2*this.scale*this.z);e.fillRect(this.x-r,this.y-r,r*2,r*2)}else{const o=(6+this.sizeBias*6)*this.scale,h=Math.sin(n*3.5+this.baseX*.1+this.randomSpeed*5),g=.4+.6*((h+1)/2),d=o*.5*g,c=Math.max(.8,this.z*1.5*this.scale*(.8+.2*h)),a=Math.atan2(this.dirY,this.dirX);e.beginPath(),e.ellipse(this.x,this.y,d,c,a,0,Math.PI*2),e.fill()}}}function tt({config:t,backgroundColor:e}){const n=f.useRef(null),i=f.useRef(null),s=f.useRef([]),m=f.useRef(0),r=f.useRef({x:-1e3,y:-1e3,isDown:!1,active:!1}),o=f.useRef({x:0,y:0,initialized:!1});f.useEffect(()=>{const c=l=>{if(!n.current)return;const u=n.current.getBoundingClientRect();r.current.x=l.clientX-u.left,r.current.y=l.clientY-u.top,r.current.active=!0},a=()=>{r.current.active=!1};return window.addEventListener("pointermove",c),window.addEventListener("pointerleave",a),()=>{window.removeEventListener("pointermove",c),window.removeEventListener("pointerleave",a)}},[]);const h=f.useRef(t),g=f.useRef(null),d=f.useRef(0);return f.useEffect(()=>{h.current=t},[t]),f.useEffect(()=>{if(g.current&&(clearInterval(g.current),g.current=null),!t.colors||t.colors.length===0){const a=t.color||"#8B5CF6";s.current.forEach(l=>{l.targetColor=a,l.colorDelay=Math.random()*20});return}const c=a=>{const l=r.current.active?r.current.x:o.current.x,u=r.current.active?r.current.y:o.current.y;s.current.forEach(R=>{const E=R.x-l,T=R.y-u,b=Math.sqrt(E*E+T*T);R.targetColor=a,R.colorDelay=Math.max(0,b*.15)})};return t.colorMode==="mixed"?s.current.forEach((a,l)=>{a.targetColor=t.colors[l%t.colors.length],a.colorDelay=Math.random()*20}):(d.current=0,c(t.colors[0]),t.colors.length>1&&(g.current=setInterval(()=>{const a=t.colors;d.current=(d.current+1)%a.length,c(a[d.current])},3e3))),()=>{g.current&&clearInterval(g.current)}},[t.colors,t.color,t.colorMode]),f.useEffect(()=>{if(!n.current||!i.current||h.current.name==="NONE")return;const c=i.current,a=n.current,l=b=>{for(const C of b){const{width:y,height:w}=C.contentRect,P=window.devicePixelRatio||1;c.width=y*P,c.height=w*P,c.style.width=`${y}px`,c.style.height=`${w}px`;const S=c.getContext("2d");S&&S.scale(P,P),s.current=[];const z=Math.floor(350*(h.current.density??1)),M=h.current.colors,D=h.current.colorMode||"wave";for(let x=0;x<z;x++){const p=Math.sqrt(Math.random())*350,X=Math.random()*Math.PI*2,Y=Math.cos(X)*p,L=Math.sin(X)*p;let F;if(M&&M.length>0)F=D==="mixed"?M[x%M.length]:M[0];else if(h.current.color)F=h.current.color;else{const O=210+Math.max(0,Math.min(1,(Y+350)/700))*130+(Math.random()*15-7.5),$=75+Math.random()*25,N=60+Math.random()*15;F=`hsl(${O}, ${$}%, ${N}%)`}s.current.push(new Z(Y,L,F))}}},u=new ResizeObserver(l);u.observe(a);const R=c.getContext("2d");let E=0;const T=()=>{const b=a.getBoundingClientRect();if(R.clearRect(0,0,b.width,b.height),e!=="transparent"&&(R.fillStyle=e,R.fillRect(0,0,b.width,b.height)),o.current.initialized||(o.current.x=b.width/2,o.current.y=b.height/2,o.current.initialized=!0),h.current.name==="FOLLOW_POINTER"){let C=b.width/2,y=b.height/2;const w=r.current.active?r.current.x:null,P=r.current.active?r.current.y:null;w!==null&&P!==null&&(C=w,y=P);const S=h.current.pointerTrackingSpeed??.06;o.current.x+=(C-o.current.x)*S,o.current.y+=(y-o.current.y)*S;const I=h.current.particleSpeed??1;E+=.012*I;const z=s.current,M=18,D=M*M;for(let x=0;x<z.length;x++){const p=z[x];for(let X=x+1;X<z.length;X++){const Y=z[X],L=p.x-Y.x,F=p.y-Y.y,W=L*L+F*F;if(W<D&&W>0){const O=Math.sqrt(W),$=(M-O)/M,N=L/O*$*.8,B=F/O*$*.8;p.x+=N,p.y+=B,Y.x-=N,Y.y-=B,p.vx+=N*.1,p.vy+=B*.1,Y.vx-=N*.1,Y.vy-=B*.1}}}for(const x of z){x.update(w,P,o.current.x,o.current.y,b.width,b.height,E,h.current.orientation);const p=h.current.shape||"bean";x.draw(R,E,p)}}m.current=requestAnimationFrame(T)};return T(),()=>{u.disconnect(),cancelAnimationFrame(m.current)}},[e,t.density]),t.name==="NONE"?null:q.jsx("div",{ref:n,style:{position:"absolute",inset:0,zIndex:0,overflow:"hidden"},children:q.jsx("canvas",{ref:i,style:{display:"block",width:"100%",height:"100%"}})})}class et{constructor(e,n){v(this,"x");v(this,"y");v(this,"vx");v(this,"vy");v(this,"radius");v(this,"currentColor","#8B5CF6");v(this,"targetColor",null);v(this,"colorDelay",0);this.x=e,this.y=n;const i=Math.random()*Math.PI*2,s=Math.random()*.5+.1;this.vx=Math.cos(i)*s,this.vy=Math.sin(i)*s,this.radius=Math.random()*1.5+1}update(e,n,i,s,m=1,r=.06){if(this.x+=this.vx*m,this.y+=this.vy*m,this.x<0?(this.x=0,this.vx*=-1):this.x>e&&(this.x=e,this.vx*=-1),this.y<0?(this.y=0,this.vy*=-1):this.y>n&&(this.y=n,this.vy*=-1),i!==null&&s!==null){const o=i-this.x,h=s-this.y,g=o*o+h*h,d=150,c=d*d;if(g<c){const a=Math.sqrt(g),l=(d-a)/d,u=r/.06;this.x-=o/a*l*2*u,this.y-=h/a*l*2*u}}this.targetColor&&(this.colorDelay-=1*m,this.colorDelay<=0&&(this.currentColor=this.targetColor,this.targetColor=null))}draw(e,n="circle",i=0){if(e.fillStyle=this.currentColor,n==="square")e.fillRect(this.x-this.radius,this.y-this.radius,this.radius*2,this.radius*2);else if(n==="bean"){const s=Math.sin(i*.05+this.x*.01+this.y*.01),m=this.radius*2*(.8+.4*s),r=this.radius*(.8+.2*s),o=this.vx!==0||this.vy!==0?Math.atan2(this.vy,this.vx):0;e.beginPath(),e.ellipse(this.x,this.y,m,r,o,0,Math.PI*2),e.fill()}else e.beginPath(),e.arc(this.x,this.y,this.radius,0,Math.PI*2),e.fill()}}function nt({config:t,backgroundColor:e}){const n=f.useRef(null),i=f.useRef(null),s=f.useRef([]),m=f.useRef(0),r=f.useRef({x:-1e3,y:-1e3,active:!1});f.useEffect(()=>{const d=a=>{if(!n.current)return;const l=n.current.getBoundingClientRect();r.current.x=a.clientX-l.left,r.current.y=a.clientY-l.top,r.current.active=!0},c=()=>{r.current.active=!1};return window.addEventListener("pointermove",d),window.addEventListener("pointerleave",c),()=>{window.removeEventListener("pointermove",d),window.removeEventListener("pointerleave",c)}},[]);const o=f.useRef(t),h=f.useRef(0),g=f.useRef(null);return f.useEffect(()=>{o.current=t},[t]),f.useEffect(()=>{if(g.current&&(clearInterval(g.current),g.current=null),!t.colors||t.colors.length===0){const l=t.color||"#8B5CF6";s.current.forEach(u=>{u.targetColor=l,u.colorDelay=Math.random()*20});return}const d=r.current.active?r.current.x:n.current?n.current.clientWidth/2:0,c=r.current.active?r.current.y:n.current?n.current.clientHeight/2:0,a=l=>{s.current.forEach(u=>{const R=u.x-d,E=u.y-c,T=Math.sqrt(R*R+E*E);u.targetColor=l,u.colorDelay=Math.max(0,T*.2)})};return t.colorMode==="mixed"?s.current.forEach((l,u)=>{l.targetColor=t.colors[u%t.colors.length],l.colorDelay=Math.random()*20}):(h.current=0,a(t.colors[0]),t.colors.length>1&&(g.current=setInterval(()=>{const l=t.colors;h.current=(h.current+1)%l.length,a(l[h.current])},3e3))),()=>{g.current&&clearInterval(g.current)}},[t.colors,t.color,t.colorMode]),f.useEffect(()=>{if(!n.current||!i.current)return;const d=i.current,c=n.current,a=T=>{for(const b of T){const{width:C,height:y}=b.contentRect,w=window.devicePixelRatio||1;d.width=C*w,d.height=y*w,d.style.width=`${C}px`,d.style.height=`${y}px`;const P=d.getContext("2d");P&&P.scale(w,w),s.current=[];const S=C*y,I=o.current.density??1,z=Math.min(Math.floor(300*I),Math.floor(S/6e3*I)),M=o.current.colors,D=o.current.colorMode||"wave";for(let x=0;x<z;x++){const p=new et(Math.random()*C,Math.random()*y);M&&M.length>0?p.currentColor=D==="mixed"?M[x%M.length]:M[0]:o.current.color&&(p.currentColor=o.current.color),s.current.push(p)}}},l=new ResizeObserver(a);l.observe(c);const u=d.getContext("2d");let R=0;const E=()=>{R+=1;const T=c.getBoundingClientRect();u.clearRect(0,0,T.width,T.height),e!=="transparent"&&(u.fillStyle=e,u.fillRect(0,0,T.width,T.height));const b=r.current.active?r.current.x:null,C=r.current.active?r.current.y:null,y=s.current,w=o.current.shape||"circle",P=120,S=P*P,I=o.current.particleSpeed??1,z=o.current.pointerTrackingSpeed??.06;for(let M=0;M<y.length;M++){const D=y[M];D.update(T.width,T.height,b,C,I,z),u.globalAlpha=1,D.draw(u,w,R);for(let x=M+1;x<y.length;x++){const p=y[x],X=D.x-p.x,Y=D.y-p.y,L=X*X+Y*Y;if(L<S){const F=1-Math.sqrt(L)/P;u.beginPath(),u.moveTo(D.x,D.y),u.lineTo(p.x,p.y),u.strokeStyle=D.currentColor,u.globalAlpha=F*.5,u.lineWidth=1,u.stroke()}}}if(u.globalAlpha=1,b!==null&&C!==null)for(let x=0;x<y.length;x++){const p=y[x],X=p.x-b,Y=p.y-C,L=X*X+Y*Y;if(L<22500){const F=1-Math.sqrt(L)/150;u.beginPath(),u.moveTo(p.x,p.y),u.lineTo(b,C),u.strokeStyle=p.currentColor,u.globalAlpha=F*.8,u.lineWidth=1.5,u.stroke()}}u.globalAlpha=1,m.current=requestAnimationFrame(E)};return E(),()=>{l.disconnect(),cancelAnimationFrame(m.current)}},[e,t.density]),q.jsx("div",{ref:n,style:{position:"absolute",inset:0,zIndex:0,overflow:"hidden"},children:q.jsx("canvas",{ref:i,style:{display:"block",width:"100%",height:"100%"}})})}class rt{constructor(e,n,i,s){v(this,"x",0);v(this,"y",0);v(this,"baseX");v(this,"baseY");v(this,"vx",0);v(this,"vy",0);v(this,"color");v(this,"size");v(this,"angle");v(this,"dist");v(this,"spring");v(this,"friction");v(this,"targetColor",null);v(this,"colorDelay",0);this.baseX=e,this.baseY=n,this.color=i,this.size=s,this.angle=Math.atan2(n,e),this.dist=Math.sqrt(e*e+n*n);const m=Math.min(1,this.dist/350);this.spring=.15-m*.13,this.friction=.85+m*.1}update(e,n,i,s){const r=1+Math.sin(this.angle*3+i*1.2)*.05+Math.cos(this.angle*5-i*.6)*.03,o=this.dist*s*r,h=Math.cos(this.angle)*o,g=Math.sin(this.angle)*o,d=this.dist*.1*r,c=Math.sin(i*3+this.dist*.05)*d,a=e+h+Math.cos(this.angle+Math.PI/2)*c,l=n+g+Math.sin(this.angle+Math.PI/2)*c,u=a-this.x,R=l-this.y;this.vx+=u*this.spring,this.vy+=R*this.spring,this.vx*=this.friction,this.vy*=this.friction,this.x+=this.vx,this.y+=this.vy,this.targetColor&&(this.colorDelay-=1,this.colorDelay<=0&&(this.color=this.targetColor,this.targetColor=null))}draw(e,n,i){const s=Math.min(1,this.dist/350),m=1+(1-s)*(i-1)*1.5,r=this.size*m;e.globalAlpha=Math.max(.15,1-s*.7),e.fillStyle=this.color,e.beginPath(),n==="circle"?e.arc(this.x,this.y,r,0,Math.PI*2):n==="square"?e.rect(this.x-r,this.y-r,r*2,r*2):e.ellipse(this.x,this.y,r*1.5,r,0,0,Math.PI*2),e.fill()}}function st({config:t,backgroundColor:e}){const n=f.useRef(null),i=f.useRef(null),s=f.useRef([]),m=f.useRef(0),r=f.useRef({x:-1e3,y:-1e3,active:!1}),o=f.useRef({x:0,y:0,initialized:!1,angle:-Math.PI/2});f.useEffect(()=>{const c=a=>{if(!n.current)return;const l=n.current.getBoundingClientRect();r.current.x=a.clientX-l.left,r.current.y=a.clientY-l.top,r.current.active=!0};return window.addEventListener("pointermove",c),()=>{window.removeEventListener("pointermove",c)}},[]);const h=f.useRef(t),g=f.useRef(0),d=f.useRef(null);return f.useEffect(()=>{h.current=t},[t]),f.useEffect(()=>{if(d.current&&(clearInterval(d.current),d.current=null),!t.colors||t.colors.length===0){const a=t.color||"#8B5CF6";s.current.forEach(l=>{l.targetColor=a,l.colorDelay=Math.random()*20});return}const c=a=>{s.current.forEach(l=>{l.targetColor=a,l.colorDelay=Math.max(0,l.dist*.2)})};return t.colorMode==="mixed"?s.current.forEach((a,l)=>{a.targetColor=t.colors[l%t.colors.length],a.colorDelay=Math.max(0,a.dist*.2)+Math.random()*10}):(g.current=0,c(t.colors[0]),t.colors.length>1&&(d.current=setInterval(()=>{const a=t.colors;g.current=(g.current+1)%a.length,c(a[g.current])},3e3))),()=>{d.current&&clearInterval(d.current)}},[t.colors,t.color,t.colorMode]),f.useEffect(()=>{if(!n.current||!i.current)return;const c=i.current,a=n.current,l=b=>{for(const C of b){const{width:y,height:w}=C.contentRect,P=window.devicePixelRatio||1;c.width=y*P,c.height=w*P,c.style.width=`${y}px`,c.style.height=`${w}px`;const S=c.getContext("2d");S&&S.scale(P,P),s.current=[];const z=Math.floor(350*(h.current.density??1)),M=h.current.colors,D=h.current.colorMode||"wave";for(let x=0;x<z;x++){const p=Math.random()*Math.PI*2,X=Math.sqrt(Math.random())*350,Y=Math.cos(p)*X,L=Math.sin(p)*X;let F;if(M&&M.length>0)F=D==="mixed"?M[x%M.length]:M[0];else if(h.current.color)F=h.current.color;else{const $=260+Math.random()*60,N=70+Math.random()*30,B=60+Math.random()*20;F=`hsl(${$}, ${N}%, ${B}%)`}let W=2+Math.random()*2;X>200&&(W*=.6),X<80&&(W*=1.5);const O=new rt(Y,L,F,W);O.x=y/2,O.y=w/2,s.current.push(O)}}},u=new ResizeObserver(l);u.observe(a);const R=c.getContext("2d");let E=0;const T=()=>{const b=a.getBoundingClientRect();R.globalCompositeOperation="source-over",e==="transparent"?R.clearRect(0,0,b.width,b.height):(R.fillStyle=e,R.fillRect(0,0,b.width,b.height)),o.current.initialized||(o.current.x=b.width/2,o.current.y=b.height/2,o.current.initialized=!0);let C=b.width/2,y=b.height/2;const w=r.current.active?r.current.x:null,P=r.current.active?r.current.y:null;w!==null&&P!==null&&(C=w,y=P);const S=C-o.current.x,I=y-o.current.y,z=Math.sqrt(S*S+I*I);let M=0,D=0;z>1?(M=S/z,D=I/z,o.current.angle=Math.atan2(D,M)):(M=Math.cos(o.current.angle),D=Math.sin(o.current.angle));const x=h.current.particleSpeed??1;E+=.02*x;const p=E%4;let X=1,Y=1,L=0;if(p<1){const B=p;X=1-Math.sin(B*Math.PI)*.3,Y=1+Math.sin(B*Math.PI)*.15,L=Math.sin(B*Math.PI)*12}else{const B=(p-1)/3;X=.7+.3*Math.sin(B*Math.PI/2),Y=1,L=0}const F=h.current.pointerTrackingSpeed??.02;o.current.x+=S*F,o.current.y+=I*F,o.current.x+=M*L*x,o.current.y+=D*L*x;const O=200*(Y+(p<1?p*.15:0)),$=R.createRadialGradient(o.current.x,o.current.y,0,o.current.x,o.current.y,O);$.addColorStop(0,"rgba(80, 150, 255, 0.05)"),$.addColorStop(1,"rgba(80, 150, 255, 0)"),R.fillStyle=$,R.beginPath(),R.arc(o.current.x,o.current.y,O,0,Math.PI*2),R.fill();const N=s.current;R.globalCompositeOperation="source-over";for(const B of N){B.update(o.current.x,o.current.y,E,X);const at=h.current.shape||"circle";B.draw(R,at,Y)}m.current=requestAnimationFrame(T)};return T(),()=>{u.disconnect(),cancelAnimationFrame(m.current)}},[e,t.density]),q.jsx("div",{ref:n,style:{position:"absolute",inset:0,zIndex:0,overflow:"hidden",pointerEvents:"none"},children:q.jsx("canvas",{ref:i,style:{display:"block",width:"100%",height:"100%",pointerEvents:"none"}})})}const ot={position:"relative",border:"1px solid rgba(255, 255, 255, 0.1)",borderRadius:"1rem",overflow:"hidden",boxShadow:"0 25px 50px -12px rgba(0, 0, 0, 0.25)",transition:"all 300ms ease-out"};function it({children:t,width:e="100%",height:n="60vh",backgroundColor:i="#050505",className:s="",style:m,background:r={name:"NONE"}}){return q.jsxs("div",{className:s,style:{...ot,width:e,height:n,backgroundColor:i,...m},children:[r.name==="FOLLOW_POINTER"&&q.jsx(tt,{config:r,backgroundColor:i}),r.name==="NET"&&q.jsx(nt,{config:r,backgroundColor:i}),r.name==="JELLYFISH"&&q.jsx(st,{config:r,backgroundColor:i}),q.jsx("div",{style:{position:"relative",zIndex:10,width:"100%",height:"100%"},children:t})]})}function k(t){const e=f.useRef({x:-1e3,y:-1e3,isDown:!1,active:!1});return f.useEffect(()=>{const n=t.current;if(!n)return;const i=o=>{const h=n.getBoundingClientRect();e.current.x=o.clientX-h.left,e.current.y=o.clientY-h.top,e.current.active=!0},s=()=>{e.current.active=!1},m=()=>{e.current.isDown=!0},r=()=>{e.current.isDown=!1};return n.addEventListener("pointermove",i),n.addEventListener("pointerleave",s),n.addEventListener("pointerdown",m),n.addEventListener("pointerup",r),n.style.touchAction="none",()=>{n.removeEventListener("pointermove",i),n.removeEventListener("pointerleave",s),n.removeEventListener("pointerdown",m),n.removeEventListener("pointerup",r)}},[t]),e}function H(t,e,n,i,s,m,r,o,h){if(s===null||m===null||!o&&h==="none")return{x:n,y:i};let g=n,d=i;const c=s-t,a=m-e,l=c*c+a*a,u=Math.sqrt(l);if(o&&!r&&l<3e4){const E=(3e4-l)/3e4;g+=c*E*.15,d+=a*E*.15}if(r&&h!=="none"){if(h==="attract"){if(l<3e4){const E=(3e4-l)/3e4;g+=c*E*.8,d+=a*E*.8}}else if(h==="repel"&&l<5e4&&u>0){const E=Math.pow(Math.max(0,5e4-l)/5e4,1.2);g-=c/u*E*400,d-=a/u*E*400}}return{x:g,y:d}}class J{constructor(e,n,i="255, 255, 255"){v(this,"x");v(this,"y");v(this,"vx");v(this,"vy");v(this,"targetX");v(this,"targetY");v(this,"baseColor");v(this,"opacity");v(this,"size");v(this,"sizeMultiplier");v(this,"friction");v(this,"ease");v(this,"easeMultiplier");v(this,"floatSpeed");v(this,"floatOffset");v(this,"randomSpeed");this.x=Math.random()*e,this.y=Math.random()*n,this.targetX=this.x,this.targetY=this.y,this.vx=0,this.vy=0,this.size=Math.random()*1.8+.5,this.sizeMultiplier=1,this.baseColor=Array.isArray(i)?i[Math.floor(Math.random()*i.length)]:i,this.opacity=.4+Math.random()*.6,this.friction=.82+Math.random()*.1,this.ease=.03+Math.random()*.05,this.easeMultiplier=1,this.floatSpeed=Math.random()*.02+.005,this.floatOffset=Math.random()*Math.PI*2,this.randomSpeed=Math.random()}update(e,n,i=null,s=null,m=!1,r=!0,o="none"){const{x:h,y:g}=H(this.x,this.y,this.targetX,this.targetY,i,s,m,r,o),d=h-this.x,c=g-this.y,a=n?0:Math.cos(e*.01+this.y*.01)*.5,l=n?0:Math.sin(e*.01+this.x*.01)*.5;this.vx+=d*(this.ease*this.easeMultiplier)+a,this.vy+=c*(this.ease*this.easeMultiplier)+l,this.vx*=this.friction,this.vy*=this.friction,this.x+=this.vx,this.y+=this.vy;const u=n?.2:2;this.x+=Math.cos(e*this.floatSpeed+this.floatOffset)*u,this.y+=Math.sin(e*this.floatSpeed+this.floatOffset)*u}draw(e,n="circle",i=0){e.fillStyle=`rgba(${this.baseColor}, ${this.opacity})`;const s=Math.max(.1,this.size*this.sizeMultiplier);if(n==="square")e.fillRect(this.x-s,this.y-s,s*2,s*2);else if(n==="bean"){const m=6+this.size*2*this.sizeMultiplier,r=Math.sin(i*.05+this.x*.01+this.randomSpeed*5),o=.4+.6*((r+1)/2),h=m*.5*o,g=Math.max(.8,s*1.5*(.8+.2*r)),d=this.vx!==0||this.vy!==0?Math.atan2(this.vy,this.vx):0;e.beginPath(),e.ellipse(this.x,this.y,h,g,d,0,Math.PI*2),e.fill()}else e.beginPath(),e.arc(this.x,this.y,s,0,Math.PI*2),e.fill()}}function G(t,e,n){if(e<=0||n<=0)return[];const i=document.createElement("canvas");i.width=e,i.height=n;const s=i.getContext("2d",{willReadFrequently:!0});if(!s)return[];s.clearRect(0,0,e,n);let m=Math.min(e,n)*.65;s.font=`bold ${m}px "Georgia", serif`;const r=s.measureText(t);r.width>e*.9&&(m=m*(e*.9)/r.width,s.font=`bold ${m}px "Georgia", serif`),s.fillStyle="white",s.textAlign="center",s.textBaseline="middle",s.fillText(t,e/2,n/2.05);const h=s.getImageData(0,0,e,n).data,g=[],d=window.innerWidth<600?6:8;for(let c=0;c<n;c+=d)for(let a=0;a<e;a+=d){const l=(c*e+a)*4;h[l+3]>128&&g.push({x:a+(Math.random()-.5)*4,y:c+(Math.random()-.5)*4})}return g}function K(t,e,n){const i=f.useRef(t);return f.useEffect(()=>{i.current=t},[t]),{getPixelsForText:G,updateTextTargets:(m,r,o)=>{var T,b;const h=r||((T=n.current)==null?void 0:T.offsetWidth)||window.innerWidth,g=o||((b=n.current)==null?void 0:b.offsetHeight)||window.innerHeight;if(!m){e.current.forEach(y=>{const w=50+Math.random()*(h-100),P=50+Math.random()*(g-100);(Math.abs(w-y.x)>20||Math.abs(P-y.y)>20)&&(y.vx+=(Math.random()-.5)*20,y.vy+=(Math.random()-.5)*20),y.targetX=w,y.targetY=P});return}const d=G(m,h,g);if(d.length===0)return;const c=h*.15,a=d.map(C=>({pt:C,key:C.x+(Math.random()-.5)*c}));a.sort((C,y)=>C.key-y.key);const l=a.map(C=>C.pt),u=e.current.map((C,y)=>({i:y,key:C.x+(Math.random()-.5)*c}));u.sort((C,y)=>C.key-y.key);const R=u.map(C=>C.i),E=Math.ceil(Math.sqrt(R.length));for(let C=0;C<R.length;C+=E){const y=Math.min(C+E,R.length),w=R.slice(C,y),P=[];for(let S=C;S<y;S++)P.push(l[S%l.length]);w.sort((S,I)=>e.current[S].y-e.current[I].y),P.sort((S,I)=>S.y-I.y);for(let S=0;S<w.length;S++){const I=w[S],z=e.current[I],M=P[S],D=M.x-z.x,x=M.y-z.y;if(Math.abs(D)>20||Math.abs(x)>20){z.vx+=(Math.random()-.5)*20,z.vy+=(Math.random()-.5)*20;const p=(Math.random()>.5?1:-1)*(Math.random()*10+5);z.vx+=Math.sign(x)*p,z.vy-=Math.sign(D)*p}z.targetX=M.x,z.targetY=M.y}}},textRef:i}}const j=t=>{if(!t||typeof t!="string")return"0, 0, 0";try{let e=0,n=0,i=0;if(t.length===4)e=parseInt(t[1]+t[1],16),n=parseInt(t[2]+t[2],16),i=parseInt(t[3]+t[3],16);else if(t.length===7)e=parseInt(t.substring(1,3),16),n=parseInt(t.substring(3,5),16),i=parseInt(t.substring(5,7),16);else return t;return`${e}, ${n}, ${i}`}catch{return"0, 0, 0"}};function ct({text:t,particleColor:e="255, 255, 255",particleSize:n=1,particleDensity:i=1,particleEase:s=1,isMagnet:m=!0,clickMode:r="none",particleShape:o="circle",backgroundColor:h="#050505"}){const g=f.useRef(null),d=f.useRef(null),c=f.useRef([]),a=f.useRef(0),l=f.useRef(0),u=k(d),R=f.useRef({isMagnet:m,clickMode:r,particleShape:o,backgroundColor:h});R.current={isMagnet:m,clickMode:r,particleShape:o,backgroundColor:h};const{updateTextTargets:E,textRef:T}=K(t,c,d),b=()=>Array.isArray(e)?e.map(y=>j(y)):j(e),C=(y,w,P=1)=>{const S=window.innerWidth<600?1500:3e3,I=Math.floor(S*P),z=[],M=b();for(let D=0;D<I;D++){const x=new J(y,w,M);x.sizeMultiplier=n,x.easeMultiplier=s,z.push(x)}c.current=z};return f.useEffect(()=>{if(c.current.length>0){const y=b();c.current.forEach(w=>{w.baseColor=Array.isArray(y)?y[Math.floor(Math.random()*y.length)]:y})}},[e]),f.useEffect(()=>{c.current.length>0&&c.current.forEach(y=>{y.sizeMultiplier=n})},[n]),f.useEffect(()=>{c.current.length>0&&c.current.forEach(y=>{y.easeMultiplier=s})},[s]),f.useEffect(()=>{if(c.current.length>0&&g.current&&d.current){const y=window.innerWidth<600?1500:3e3,w=Math.floor(y*i),P=c.current.length;if(w>P){const S=d.current.getBoundingClientRect(),I=b();for(let z=0;z<w-P;z++){const M=new J(S.width,S.height,I);M.sizeMultiplier=n,c.current.push(M)}E(t)}else w<P&&c.current.splice(w)}},[i]),f.useEffect(()=>{const y=d.current,w=g.current;if(!y||!w)return;const P=M=>{for(const D of M){const{width:x,height:p}=D.contentRect,X=window.devicePixelRatio||1;w.width=x*X,w.height=p*X,w.style.width=`${x}px`,w.style.height=`${p}px`;const Y=w.getContext("2d");Y&&Y.scale(X,X),c.current.length===0&&C(x,p,i),E(t,x,p)}},S=new ResizeObserver(P);S.observe(y);const I=w.getContext("2d"),z=()=>{l.current++;const M=y.getBoundingClientRect(),{isMagnet:D,clickMode:x,particleShape:p,backgroundColor:X}=R.current;if(X==="transparent")I.clearRect(0,0,M.width,M.height),I.globalCompositeOperation="source-over";else{const O=j(X);I.fillStyle=`rgba(${O}, 0.25)`,I.fillRect(0,0,M.width,M.height),I.globalCompositeOperation="screen"}const Y=T.current!=="",L=u.current.active?u.current.x:null,F=u.current.active?u.current.y:null,W=u.current.isDown;for(let O=0;O<c.current.length;O++){const $=c.current[O];$.update(l.current,!!Y,L,F,W,D,x),$.draw(I,p,l.current)}I.globalCompositeOperation="source-over",a.current=requestAnimationFrame(z)};return z(),()=>{S.disconnect(),cancelAnimationFrame(a.current)}},[]),f.useEffect(()=>{E(t)},[t]),q.jsx("div",{ref:d,style:{position:"absolute",inset:0,zIndex:0,overflow:"hidden"},children:q.jsx("canvas",{ref:g,style:{display:"block",width:"100%",height:"100%"}})})}A.ParticleCanvas=it,A.TextParticleEngine=ct,A.getMagnetTarget=H,A.useParticleInteraction=k,A.useTextParticles=K,Object.defineProperty(A,Symbol.toStringTag,{value:"Module"})}));
1
+ (function(L,q){typeof exports=="object"&&typeof module<"u"?q(exports,require("react/jsx-runtime"),require("react")):typeof define=="function"&&define.amd?define(["exports","react/jsx-runtime","react"],q):(L=typeof globalThis<"u"?globalThis:L||self,q(L.JlParticleInteractive={},L.ReactJsxRuntime,L.React))})(this,(function(L,q,f){"use strict";var lt=Object.defineProperty;var ht=(L,q,f)=>q in L?lt(L,q,{enumerable:!0,configurable:!0,writable:!0,value:f}):L[q]=f;var v=(L,q,f)=>ht(L,typeof q!="symbol"?q+"":q,f);function V(t,e,n,i){let s=e+t.baseX,m=n+t.baseY;s+=Math.sin(i*t.randomSpeed+t.baseY*.05)*15*t.z,m+=Math.cos(i*t.randomSpeed+t.baseX*.05)*15*t.z;const r=s-t.x,o=m-t.y,h=r*.06,g=o*.06;return{forceX:h,forceY:g}}function Z(t,e,n,i,s,m){const r=i,o=s,h=t.x-r,g=t.y-o,d=Math.max(Math.sqrt(h*h+g*g),1);let c=0,a=0;const u=Math.sin(d*.02-m*3)*2.5*t.z;c+=h/d*u,a+=g/d*u;const E=Math.cos(d*.01-m*1+t.randomSpeed)*.8*t.z,T=-g/d,b=h/d;return c+=T*E,a+=b*E,{forceX:c,forceY:a,dxCenter:h,dyCenter:g,distToCenter:d}}function tt(t,e){let s=Math.min(e/400,1)*2.5+t.sizeBias*1;s<.4&&(s=0),t.scale+=(s-t.scale)*.15}function et(t,e,n,i,s,m){let r=0,o=0;s==="horizontal"?(r=1,o=0):s==="diagonal"?(r=-e/i,o=-n/i):(r=-n/i,o=e/i);const h=Math.atan2(o,r),g=t.sizeBias>.3?1:0,d=Math.max(0,Math.sin(m*.5+t.randomSpeed*10)),c=Math.sin(m*3+t.baseX*.1+t.randomSpeed)*.5*d*g;r=Math.cos(h+c),o=Math.sin(h+c);const a=Math.max(1-i/400,.1);s==="horizontal"?(t.dirX=t.dirX*(1-.2)+r*.2,t.dirY=t.dirY*(1-.2)+o*.2):(t.dirX=t.dirX*(1-a*.3)+r*a*.3,t.dirY=t.dirY*(1-a*.3)+o*a*.3);const l=Math.sqrt(t.dirX*t.dirX+t.dirY*t.dirY);l>0&&(t.dirX/=l,t.dirY/=l)}class nt{constructor(e,n,i){v(this,"x");v(this,"y");v(this,"baseX");v(this,"baseY");v(this,"z");v(this,"vx");v(this,"vy");v(this,"color");v(this,"targetColor",null);v(this,"colorDelay",0);v(this,"angleTarget");v(this,"randomSpeed");v(this,"sizeBias");v(this,"scale");v(this,"dirX");v(this,"dirY");v(this,"initialized",!1);this.x=e,this.y=n,this.baseX=e,this.baseY=n,this.z=Math.random()*1.5+.2,this.vx=0,this.vy=0,this.color=i,this.angleTarget=Math.random()*Math.PI*2,this.randomSpeed=Math.random()*2+1,this.sizeBias=Math.random(),this.scale=1,this.dirX=Math.cos(this.angleTarget),this.dirY=Math.sin(this.angleTarget)}update(e,n,i,s,m,r,o,h="vertical"){this.initialized||(this.x=i+this.baseX,this.y=s+this.baseY,this.initialized=!0);const{forceX:g,forceY:d,dxCenter:c,dyCenter:a,distToCenter:l}=Z(this,e,n,i,s,o),{forceX:u,forceY:R}=V(this,i,s,o);this.vx+=g+u,this.vy+=d+R,this.vx*=.75,this.vy*=.75,this.x+=this.vx,this.y+=this.vy,tt(this,l),et(this,c,a,l,h,o),this.targetColor&&(this.colorDelay-=1,this.colorDelay<=0&&(this.color=this.targetColor,this.targetColor=null))}draw(e,n,i="bean"){if(this.scale<=.05)return;e.fillStyle=this.color;const s=Math.sqrt(this.baseX*this.baseX+this.baseY*this.baseY),m=Math.max(0,1-s/350);if(e.globalAlpha=Math.min(1,(.5+this.z*.5)*Math.min(this.scale,1)*m),!(e.globalAlpha<=.01))if(i==="circle"){const r=Math.max(.1,2*this.scale*this.z);e.beginPath(),e.arc(this.x,this.y,r,0,Math.PI*2),e.fill()}else if(i==="square"){const r=Math.max(.1,2*this.scale*this.z);e.fillRect(this.x-r,this.y-r,r*2,r*2)}else{const o=(6+this.sizeBias*6)*this.scale,h=Math.sin(n*3.5+this.baseX*.1+this.randomSpeed*5),g=.4+.6*((h+1)/2),d=o*.5*g,c=Math.max(.8,this.z*1.5*this.scale*(.8+.2*h)),a=Math.atan2(this.dirY,this.dirX);e.beginPath(),e.ellipse(this.x,this.y,d,c,a,0,Math.PI*2),e.fill()}}}function j({config:t,backgroundColor:e}){const n=f.useRef(null),i=f.useRef(null),s=f.useRef([]),m=f.useRef(0),r=f.useRef({x:-1e3,y:-1e3,isDown:!1,active:!1}),o=f.useRef({x:0,y:0,initialized:!1});f.useEffect(()=>{const c=l=>{if(!n.current)return;const u=n.current.getBoundingClientRect();r.current.x=l.clientX-u.left,r.current.y=l.clientY-u.top,r.current.active=!0},a=()=>{r.current.active=!1};return window.addEventListener("pointermove",c),window.addEventListener("pointerleave",a),()=>{window.removeEventListener("pointermove",c),window.removeEventListener("pointerleave",a)}},[]);const h=f.useRef(t),g=f.useRef(null),d=f.useRef(0);return f.useEffect(()=>{h.current=t},[t]),f.useEffect(()=>{if(g.current&&(clearInterval(g.current),g.current=null),!t.colors||t.colors.length===0){const a=t.color||"#8B5CF6";s.current.forEach(l=>{l.targetColor=a,l.colorDelay=Math.random()*20});return}const c=a=>{const l=r.current.active?r.current.x:o.current.x,u=r.current.active?r.current.y:o.current.y;s.current.forEach(R=>{const E=R.x-l,T=R.y-u,b=Math.sqrt(E*E+T*T);R.targetColor=a,R.colorDelay=Math.max(0,b*.15)})};return t.colorMode==="mixed"?s.current.forEach((a,l)=>{a.targetColor=t.colors[l%t.colors.length],a.colorDelay=Math.random()*20}):(d.current=0,c(t.colors[0]),t.colors.length>1&&(g.current=setInterval(()=>{const a=t.colors;d.current=(d.current+1)%a.length,c(a[d.current])},3e3))),()=>{g.current&&clearInterval(g.current)}},[t.colors,t.color,t.colorMode]),f.useEffect(()=>{if(!n.current||!i.current||h.current.name==="NONE")return;const c=i.current,a=n.current,l=b=>{for(const C of b){const{width:y,height:w}=C.contentRect,P=window.devicePixelRatio||1;c.width=y*P,c.height=w*P,c.style.width=`${y}px`,c.style.height=`${w}px`;const S=c.getContext("2d");S&&S.scale(P,P),s.current=[];const z=Math.floor(350*(h.current.density??1)),M=h.current.colors,D=h.current.colorMode||"wave";for(let x=0;x<z;x++){const p=Math.sqrt(Math.random())*350,X=Math.random()*Math.PI*2,Y=Math.cos(X)*p,A=Math.sin(X)*p;let F;if(M&&M.length>0)F=D==="mixed"?M[x%M.length]:M[0];else if(h.current.color)F=h.current.color;else{const B=210+Math.max(0,Math.min(1,(Y+350)/700))*130+(Math.random()*15-7.5),$=75+Math.random()*25,N=60+Math.random()*15;F=`hsl(${B}, ${$}%, ${N}%)`}s.current.push(new nt(Y,A,F))}}},u=new ResizeObserver(l);u.observe(a);const R=c.getContext("2d");let E=0;const T=()=>{const b=a.getBoundingClientRect();if(R.clearRect(0,0,b.width,b.height),e!=="transparent"&&(R.fillStyle=e,R.fillRect(0,0,b.width,b.height)),o.current.initialized||(o.current.x=b.width/2,o.current.y=b.height/2,o.current.initialized=!0),h.current.name==="FOLLOW_POINTER"){let C=b.width/2,y=b.height/2;const w=r.current.active?r.current.x:null,P=r.current.active?r.current.y:null;w!==null&&P!==null&&(C=w,y=P);const S=h.current.pointerTrackingSpeed??.06;o.current.x+=(C-o.current.x)*S,o.current.y+=(y-o.current.y)*S;const I=h.current.particleSpeed??1;E+=.012*I;const z=s.current,M=18,D=M*M;for(let x=0;x<z.length;x++){const p=z[x];for(let X=x+1;X<z.length;X++){const Y=z[X],A=p.x-Y.x,F=p.y-Y.y,W=A*A+F*F;if(W<D&&W>0){const B=Math.sqrt(W),$=(M-B)/M,N=A/B*$*.8,O=F/B*$*.8;p.x+=N,p.y+=O,Y.x-=N,Y.y-=O,p.vx+=N*.1,p.vy+=O*.1,Y.vx-=N*.1,Y.vy-=O*.1}}}for(const x of z){x.update(w,P,o.current.x,o.current.y,b.width,b.height,E,h.current.orientation);const p=h.current.shape||"bean";x.draw(R,E,p)}}m.current=requestAnimationFrame(T)};return T(),()=>{u.disconnect(),cancelAnimationFrame(m.current)}},[e,t.density]),t.name==="NONE"?null:q.jsx("div",{ref:n,style:{position:"absolute",inset:0,zIndex:0,overflow:"hidden"},children:q.jsx("canvas",{ref:i,style:{display:"block",width:"100%",height:"100%"}})})}class rt{constructor(e,n){v(this,"x");v(this,"y");v(this,"vx");v(this,"vy");v(this,"radius");v(this,"currentColor","#8B5CF6");v(this,"targetColor",null);v(this,"colorDelay",0);this.x=e,this.y=n;const i=Math.random()*Math.PI*2,s=Math.random()*.5+.1;this.vx=Math.cos(i)*s,this.vy=Math.sin(i)*s,this.radius=Math.random()*1.5+1}update(e,n,i,s,m=1,r=.06){if(this.x+=this.vx*m,this.y+=this.vy*m,this.x<0?(this.x=0,this.vx*=-1):this.x>e&&(this.x=e,this.vx*=-1),this.y<0?(this.y=0,this.vy*=-1):this.y>n&&(this.y=n,this.vy*=-1),i!==null&&s!==null){const o=i-this.x,h=s-this.y,g=o*o+h*h,d=150,c=d*d;if(g<c){const a=Math.sqrt(g),l=(d-a)/d,u=r/.06;this.x-=o/a*l*2*u,this.y-=h/a*l*2*u}}this.targetColor&&(this.colorDelay-=1*m,this.colorDelay<=0&&(this.currentColor=this.targetColor,this.targetColor=null))}draw(e,n="circle",i=0){if(e.fillStyle=this.currentColor,n==="square")e.fillRect(this.x-this.radius,this.y-this.radius,this.radius*2,this.radius*2);else if(n==="bean"){const s=Math.sin(i*.05+this.x*.01+this.y*.01),m=this.radius*2*(.8+.4*s),r=this.radius*(.8+.2*s),o=this.vx!==0||this.vy!==0?Math.atan2(this.vy,this.vx):0;e.beginPath(),e.ellipse(this.x,this.y,m,r,o,0,Math.PI*2),e.fill()}else e.beginPath(),e.arc(this.x,this.y,this.radius,0,Math.PI*2),e.fill()}}function H({config:t,backgroundColor:e}){const n=f.useRef(null),i=f.useRef(null),s=f.useRef([]),m=f.useRef(0),r=f.useRef({x:-1e3,y:-1e3,active:!1});f.useEffect(()=>{const d=a=>{if(!n.current)return;const l=n.current.getBoundingClientRect();r.current.x=a.clientX-l.left,r.current.y=a.clientY-l.top,r.current.active=!0},c=()=>{r.current.active=!1};return window.addEventListener("pointermove",d),window.addEventListener("pointerleave",c),()=>{window.removeEventListener("pointermove",d),window.removeEventListener("pointerleave",c)}},[]);const o=f.useRef(t),h=f.useRef(0),g=f.useRef(null);return f.useEffect(()=>{o.current=t},[t]),f.useEffect(()=>{if(g.current&&(clearInterval(g.current),g.current=null),!t.colors||t.colors.length===0){const l=t.color||"#8B5CF6";s.current.forEach(u=>{u.targetColor=l,u.colorDelay=Math.random()*20});return}const d=r.current.active?r.current.x:n.current?n.current.clientWidth/2:0,c=r.current.active?r.current.y:n.current?n.current.clientHeight/2:0,a=l=>{s.current.forEach(u=>{const R=u.x-d,E=u.y-c,T=Math.sqrt(R*R+E*E);u.targetColor=l,u.colorDelay=Math.max(0,T*.2)})};return t.colorMode==="mixed"?s.current.forEach((l,u)=>{l.targetColor=t.colors[u%t.colors.length],l.colorDelay=Math.random()*20}):(h.current=0,a(t.colors[0]),t.colors.length>1&&(g.current=setInterval(()=>{const l=t.colors;h.current=(h.current+1)%l.length,a(l[h.current])},3e3))),()=>{g.current&&clearInterval(g.current)}},[t.colors,t.color,t.colorMode]),f.useEffect(()=>{if(!n.current||!i.current)return;const d=i.current,c=n.current,a=T=>{for(const b of T){const{width:C,height:y}=b.contentRect,w=window.devicePixelRatio||1;d.width=C*w,d.height=y*w,d.style.width=`${C}px`,d.style.height=`${y}px`;const P=d.getContext("2d");P&&P.scale(w,w),s.current=[];const S=C*y,I=o.current.density??1,z=Math.min(Math.floor(300*I),Math.floor(S/6e3*I)),M=o.current.colors,D=o.current.colorMode||"wave";for(let x=0;x<z;x++){const p=new rt(Math.random()*C,Math.random()*y);M&&M.length>0?p.currentColor=D==="mixed"?M[x%M.length]:M[0]:o.current.color&&(p.currentColor=o.current.color),s.current.push(p)}}},l=new ResizeObserver(a);l.observe(c);const u=d.getContext("2d");let R=0;const E=()=>{R+=1;const T=c.getBoundingClientRect();u.clearRect(0,0,T.width,T.height),e!=="transparent"&&(u.fillStyle=e,u.fillRect(0,0,T.width,T.height));const b=r.current.active?r.current.x:null,C=r.current.active?r.current.y:null,y=s.current,w=o.current.shape||"circle",P=120,S=P*P,I=o.current.particleSpeed??1,z=o.current.pointerTrackingSpeed??.06;for(let M=0;M<y.length;M++){const D=y[M];D.update(T.width,T.height,b,C,I,z),u.globalAlpha=1,D.draw(u,w,R);for(let x=M+1;x<y.length;x++){const p=y[x],X=D.x-p.x,Y=D.y-p.y,A=X*X+Y*Y;if(A<S){const F=1-Math.sqrt(A)/P;u.beginPath(),u.moveTo(D.x,D.y),u.lineTo(p.x,p.y),u.strokeStyle=D.currentColor,u.globalAlpha=F*.5,u.lineWidth=1,u.stroke()}}}if(u.globalAlpha=1,b!==null&&C!==null)for(let x=0;x<y.length;x++){const p=y[x],X=p.x-b,Y=p.y-C,A=X*X+Y*Y;if(A<22500){const F=1-Math.sqrt(A)/150;u.beginPath(),u.moveTo(p.x,p.y),u.lineTo(b,C),u.strokeStyle=p.currentColor,u.globalAlpha=F*.8,u.lineWidth=1.5,u.stroke()}}u.globalAlpha=1,m.current=requestAnimationFrame(E)};return E(),()=>{l.disconnect(),cancelAnimationFrame(m.current)}},[e,t.density]),q.jsx("div",{ref:n,style:{position:"absolute",inset:0,zIndex:0,overflow:"hidden"},children:q.jsx("canvas",{ref:i,style:{display:"block",width:"100%",height:"100%"}})})}class st{constructor(e,n,i,s){v(this,"x",0);v(this,"y",0);v(this,"baseX");v(this,"baseY");v(this,"vx",0);v(this,"vy",0);v(this,"color");v(this,"size");v(this,"angle");v(this,"dist");v(this,"spring");v(this,"friction");v(this,"targetColor",null);v(this,"colorDelay",0);this.baseX=e,this.baseY=n,this.color=i,this.size=s,this.angle=Math.atan2(n,e),this.dist=Math.sqrt(e*e+n*n);const m=Math.min(1,this.dist/350);this.spring=.15-m*.13,this.friction=.85+m*.1}update(e,n,i,s){const r=1+Math.sin(this.angle*3+i*1.2)*.05+Math.cos(this.angle*5-i*.6)*.03,o=this.dist*s*r,h=Math.cos(this.angle)*o,g=Math.sin(this.angle)*o,d=this.dist*.1*r,c=Math.sin(i*3+this.dist*.05)*d,a=e+h+Math.cos(this.angle+Math.PI/2)*c,l=n+g+Math.sin(this.angle+Math.PI/2)*c,u=a-this.x,R=l-this.y;this.vx+=u*this.spring,this.vy+=R*this.spring,this.vx*=this.friction,this.vy*=this.friction,this.x+=this.vx,this.y+=this.vy,this.targetColor&&(this.colorDelay-=1,this.colorDelay<=0&&(this.color=this.targetColor,this.targetColor=null))}draw(e,n,i){const s=Math.min(1,this.dist/350),m=1+(1-s)*(i-1)*1.5,r=this.size*m;e.globalAlpha=Math.max(.15,1-s*.7),e.fillStyle=this.color,e.beginPath(),n==="circle"?e.arc(this.x,this.y,r,0,Math.PI*2):n==="square"?e.rect(this.x-r,this.y-r,r*2,r*2):e.ellipse(this.x,this.y,r*1.5,r,0,0,Math.PI*2),e.fill()}}function J({config:t,backgroundColor:e}){const n=f.useRef(null),i=f.useRef(null),s=f.useRef([]),m=f.useRef(0),r=f.useRef({x:-1e3,y:-1e3,active:!1}),o=f.useRef({x:0,y:0,initialized:!1,angle:-Math.PI/2});f.useEffect(()=>{const c=a=>{if(!n.current)return;const l=n.current.getBoundingClientRect();r.current.x=a.clientX-l.left,r.current.y=a.clientY-l.top,r.current.active=!0};return window.addEventListener("pointermove",c),()=>{window.removeEventListener("pointermove",c)}},[]);const h=f.useRef(t),g=f.useRef(0),d=f.useRef(null);return f.useEffect(()=>{h.current=t},[t]),f.useEffect(()=>{if(d.current&&(clearInterval(d.current),d.current=null),!t.colors||t.colors.length===0){const a=t.color||"#8B5CF6";s.current.forEach(l=>{l.targetColor=a,l.colorDelay=Math.random()*20});return}const c=a=>{s.current.forEach(l=>{l.targetColor=a,l.colorDelay=Math.max(0,l.dist*.2)})};return t.colorMode==="mixed"?s.current.forEach((a,l)=>{a.targetColor=t.colors[l%t.colors.length],a.colorDelay=Math.max(0,a.dist*.2)+Math.random()*10}):(g.current=0,c(t.colors[0]),t.colors.length>1&&(d.current=setInterval(()=>{const a=t.colors;g.current=(g.current+1)%a.length,c(a[g.current])},3e3))),()=>{d.current&&clearInterval(d.current)}},[t.colors,t.color,t.colorMode]),f.useEffect(()=>{if(!n.current||!i.current)return;const c=i.current,a=n.current,l=b=>{for(const C of b){const{width:y,height:w}=C.contentRect,P=window.devicePixelRatio||1;c.width=y*P,c.height=w*P,c.style.width=`${y}px`,c.style.height=`${w}px`;const S=c.getContext("2d");S&&S.scale(P,P),s.current=[];const z=Math.floor(350*(h.current.density??1)),M=h.current.colors,D=h.current.colorMode||"wave";for(let x=0;x<z;x++){const p=Math.random()*Math.PI*2,X=Math.sqrt(Math.random())*350,Y=Math.cos(p)*X,A=Math.sin(p)*X;let F;if(M&&M.length>0)F=D==="mixed"?M[x%M.length]:M[0];else if(h.current.color)F=h.current.color;else{const $=260+Math.random()*60,N=70+Math.random()*30,O=60+Math.random()*20;F=`hsl(${$}, ${N}%, ${O}%)`}let W=2+Math.random()*2;X>200&&(W*=.6),X<80&&(W*=1.5);const B=new st(Y,A,F,W);B.x=y/2,B.y=w/2,s.current.push(B)}}},u=new ResizeObserver(l);u.observe(a);const R=c.getContext("2d");let E=0;const T=()=>{const b=a.getBoundingClientRect();R.globalCompositeOperation="source-over",e==="transparent"?R.clearRect(0,0,b.width,b.height):(R.fillStyle=e,R.fillRect(0,0,b.width,b.height)),o.current.initialized||(o.current.x=b.width/2,o.current.y=b.height/2,o.current.initialized=!0);let C=b.width/2,y=b.height/2;const w=r.current.active?r.current.x:null,P=r.current.active?r.current.y:null;w!==null&&P!==null&&(C=w,y=P);const S=C-o.current.x,I=y-o.current.y,z=Math.sqrt(S*S+I*I);let M=0,D=0;z>1?(M=S/z,D=I/z,o.current.angle=Math.atan2(D,M)):(M=Math.cos(o.current.angle),D=Math.sin(o.current.angle));const x=h.current.particleSpeed??1;E+=.02*x;const p=E%4;let X=1,Y=1,A=0;if(p<1){const O=p;X=1-Math.sin(O*Math.PI)*.3,Y=1+Math.sin(O*Math.PI)*.15,A=Math.sin(O*Math.PI)*12}else{const O=(p-1)/3;X=.7+.3*Math.sin(O*Math.PI/2),Y=1,A=0}const F=h.current.pointerTrackingSpeed??.02;o.current.x+=S*F,o.current.y+=I*F,o.current.x+=M*A*x,o.current.y+=D*A*x;const B=200*(Y+(p<1?p*.15:0)),$=R.createRadialGradient(o.current.x,o.current.y,0,o.current.x,o.current.y,B);$.addColorStop(0,"rgba(80, 150, 255, 0.05)"),$.addColorStop(1,"rgba(80, 150, 255, 0)"),R.fillStyle=$,R.beginPath(),R.arc(o.current.x,o.current.y,B,0,Math.PI*2),R.fill();const N=s.current;R.globalCompositeOperation="source-over";for(const O of N){O.update(o.current.x,o.current.y,E,X);const at=h.current.shape||"circle";O.draw(R,at,Y)}m.current=requestAnimationFrame(T)};return T(),()=>{u.disconnect(),cancelAnimationFrame(m.current)}},[e,t.density]),q.jsx("div",{ref:n,style:{position:"absolute",inset:0,zIndex:0,overflow:"hidden",pointerEvents:"none"},children:q.jsx("canvas",{ref:i,style:{display:"block",width:"100%",height:"100%",pointerEvents:"none"}})})}const ot={position:"relative",border:"1px solid rgba(255, 255, 255, 0.1)",borderRadius:"1rem",overflow:"hidden",boxShadow:"0 25px 50px -12px rgba(0, 0, 0, 0.25)",transition:"all 300ms ease-out"};function it({children:t,width:e="100%",height:n="60vh",backgroundColor:i="#050505",className:s="",style:m,background:r={name:"NONE"}}){return q.jsxs("div",{className:s,style:{...ot,width:e,height:n,backgroundColor:i,...m},children:[r.name==="FOLLOW_POINTER"&&q.jsx(j,{config:r,backgroundColor:i}),r.name==="NET"&&q.jsx(H,{config:r,backgroundColor:i}),r.name==="JELLYFISH"&&q.jsx(J,{config:r,backgroundColor:i}),q.jsx("div",{style:{position:"relative",zIndex:10,width:"100%",height:"100%"},children:t})]})}function G(t){const e=f.useRef({x:-1e3,y:-1e3,isDown:!1,active:!1});return f.useEffect(()=>{const n=t.current;if(!n)return;const i=o=>{const h=n.getBoundingClientRect();e.current.x=o.clientX-h.left,e.current.y=o.clientY-h.top,e.current.active=!0},s=()=>{e.current.active=!1},m=()=>{e.current.isDown=!0},r=()=>{e.current.isDown=!1};return n.addEventListener("pointermove",i),n.addEventListener("pointerleave",s),n.addEventListener("pointerdown",m),n.addEventListener("pointerup",r),n.style.touchAction="none",()=>{n.removeEventListener("pointermove",i),n.removeEventListener("pointerleave",s),n.removeEventListener("pointerdown",m),n.removeEventListener("pointerup",r)}},[t]),e}function K(t,e,n,i,s,m,r,o,h){if(s===null||m===null||!o&&h==="none")return{x:n,y:i};let g=n,d=i;const c=s-t,a=m-e,l=c*c+a*a,u=Math.sqrt(l);if(o&&!r&&l<3e4){const E=(3e4-l)/3e4;g+=c*E*.15,d+=a*E*.15}if(r&&h!=="none"){if(h==="attract"){if(l<3e4){const E=(3e4-l)/3e4;g+=c*E*.8,d+=a*E*.8}}else if(h==="repel"&&l<5e4&&u>0){const E=Math.pow(Math.max(0,5e4-l)/5e4,1.2);g-=c/u*E*400,d-=a/u*E*400}}return{x:g,y:d}}class _{constructor(e,n,i="255, 255, 255"){v(this,"x");v(this,"y");v(this,"vx");v(this,"vy");v(this,"targetX");v(this,"targetY");v(this,"baseColor");v(this,"opacity");v(this,"size");v(this,"sizeMultiplier");v(this,"friction");v(this,"ease");v(this,"easeMultiplier");v(this,"floatSpeed");v(this,"floatOffset");v(this,"randomSpeed");this.x=Math.random()*e,this.y=Math.random()*n,this.targetX=this.x,this.targetY=this.y,this.vx=0,this.vy=0,this.size=Math.random()*1.8+.5,this.sizeMultiplier=1,this.baseColor=Array.isArray(i)?i[Math.floor(Math.random()*i.length)]:i,this.opacity=.4+Math.random()*.6,this.friction=.82+Math.random()*.1,this.ease=.03+Math.random()*.05,this.easeMultiplier=1,this.floatSpeed=Math.random()*.02+.005,this.floatOffset=Math.random()*Math.PI*2,this.randomSpeed=Math.random()}update(e,n,i=null,s=null,m=!1,r=!0,o="none"){const{x:h,y:g}=K(this.x,this.y,this.targetX,this.targetY,i,s,m,r,o),d=h-this.x,c=g-this.y,a=n?0:Math.cos(e*.01+this.y*.01)*.5,l=n?0:Math.sin(e*.01+this.x*.01)*.5;this.vx+=d*(this.ease*this.easeMultiplier)+a,this.vy+=c*(this.ease*this.easeMultiplier)+l,this.vx*=this.friction,this.vy*=this.friction,this.x+=this.vx,this.y+=this.vy;const u=n?.2:2;this.x+=Math.cos(e*this.floatSpeed+this.floatOffset)*u,this.y+=Math.sin(e*this.floatSpeed+this.floatOffset)*u}draw(e,n="circle",i=0){e.fillStyle=`rgba(${this.baseColor}, ${this.opacity})`;const s=Math.max(.1,this.size*this.sizeMultiplier);if(n==="square")e.fillRect(this.x-s,this.y-s,s*2,s*2);else if(n==="bean"){const m=6+this.size*2*this.sizeMultiplier,r=Math.sin(i*.05+this.x*.01+this.randomSpeed*5),o=.4+.6*((r+1)/2),h=m*.5*o,g=Math.max(.8,s*1.5*(.8+.2*r)),d=this.vx!==0||this.vy!==0?Math.atan2(this.vy,this.vx):0;e.beginPath(),e.ellipse(this.x,this.y,h,g,d,0,Math.PI*2),e.fill()}else e.beginPath(),e.arc(this.x,this.y,s,0,Math.PI*2),e.fill()}}function U(t,e,n){if(e<=0||n<=0)return[];const i=document.createElement("canvas");i.width=e,i.height=n;const s=i.getContext("2d",{willReadFrequently:!0});if(!s)return[];s.clearRect(0,0,e,n);let m=Math.min(e,n)*.65;s.font=`bold ${m}px "Georgia", serif`;const r=s.measureText(t);r.width>e*.9&&(m=m*(e*.9)/r.width,s.font=`bold ${m}px "Georgia", serif`),s.fillStyle="white",s.textAlign="center",s.textBaseline="middle",s.fillText(t,e/2,n/2.05);const h=s.getImageData(0,0,e,n).data,g=[],d=window.innerWidth<600?6:8;for(let c=0;c<n;c+=d)for(let a=0;a<e;a+=d){const l=(c*e+a)*4;h[l+3]>128&&g.push({x:a+(Math.random()-.5)*4,y:c+(Math.random()-.5)*4})}return g}function Q(t,e,n){const i=f.useRef(t);return f.useEffect(()=>{i.current=t},[t]),{getPixelsForText:U,updateTextTargets:(m,r,o)=>{var T,b;const h=r||((T=n.current)==null?void 0:T.offsetWidth)||window.innerWidth,g=o||((b=n.current)==null?void 0:b.offsetHeight)||window.innerHeight;if(!m){e.current.forEach(y=>{const w=50+Math.random()*(h-100),P=50+Math.random()*(g-100);(Math.abs(w-y.x)>20||Math.abs(P-y.y)>20)&&(y.vx+=(Math.random()-.5)*20,y.vy+=(Math.random()-.5)*20),y.targetX=w,y.targetY=P});return}const d=U(m,h,g);if(d.length===0)return;const c=h*.15,a=d.map(C=>({pt:C,key:C.x+(Math.random()-.5)*c}));a.sort((C,y)=>C.key-y.key);const l=a.map(C=>C.pt),u=e.current.map((C,y)=>({i:y,key:C.x+(Math.random()-.5)*c}));u.sort((C,y)=>C.key-y.key);const R=u.map(C=>C.i),E=Math.ceil(Math.sqrt(R.length));for(let C=0;C<R.length;C+=E){const y=Math.min(C+E,R.length),w=R.slice(C,y),P=[];for(let S=C;S<y;S++)P.push(l[S%l.length]);w.sort((S,I)=>e.current[S].y-e.current[I].y),P.sort((S,I)=>S.y-I.y);for(let S=0;S<w.length;S++){const I=w[S],z=e.current[I],M=P[S],D=M.x-z.x,x=M.y-z.y;if(Math.abs(D)>20||Math.abs(x)>20){z.vx+=(Math.random()-.5)*20,z.vy+=(Math.random()-.5)*20;const p=(Math.random()>.5?1:-1)*(Math.random()*10+5);z.vx+=Math.sign(x)*p,z.vy-=Math.sign(D)*p}z.targetX=M.x,z.targetY=M.y}}},textRef:i}}const k=t=>{if(!t||typeof t!="string")return"0, 0, 0";try{let e=0,n=0,i=0;if(t.length===4)e=parseInt(t[1]+t[1],16),n=parseInt(t[2]+t[2],16),i=parseInt(t[3]+t[3],16);else if(t.length===7)e=parseInt(t.substring(1,3),16),n=parseInt(t.substring(3,5),16),i=parseInt(t.substring(5,7),16);else return t;return`${e}, ${n}, ${i}`}catch{return"0, 0, 0"}};function ct({text:t,particleColor:e="255, 255, 255",particleSize:n=1,particleDensity:i=1,particleEase:s=1,isMagnet:m=!0,clickMode:r="none",particleShape:o="circle",backgroundColor:h="#050505"}){const g=f.useRef(null),d=f.useRef(null),c=f.useRef([]),a=f.useRef(0),l=f.useRef(0),u=G(d),R=f.useRef({isMagnet:m,clickMode:r,particleShape:o,backgroundColor:h});R.current={isMagnet:m,clickMode:r,particleShape:o,backgroundColor:h};const{updateTextTargets:E,textRef:T}=Q(t,c,d),b=()=>Array.isArray(e)?e.map(y=>k(y)):k(e),C=(y,w,P=1)=>{const S=window.innerWidth<600?1500:3e3,I=Math.floor(S*P),z=[],M=b();for(let D=0;D<I;D++){const x=new _(y,w,M);x.sizeMultiplier=n,x.easeMultiplier=s,z.push(x)}c.current=z};return f.useEffect(()=>{if(c.current.length>0){const y=b();c.current.forEach(w=>{w.baseColor=Array.isArray(y)?y[Math.floor(Math.random()*y.length)]:y})}},[e]),f.useEffect(()=>{c.current.length>0&&c.current.forEach(y=>{y.sizeMultiplier=n})},[n]),f.useEffect(()=>{c.current.length>0&&c.current.forEach(y=>{y.easeMultiplier=s})},[s]),f.useEffect(()=>{if(c.current.length>0&&g.current&&d.current){const y=window.innerWidth<600?1500:3e3,w=Math.floor(y*i),P=c.current.length;if(w>P){const S=d.current.getBoundingClientRect(),I=b();for(let z=0;z<w-P;z++){const M=new _(S.width,S.height,I);M.sizeMultiplier=n,c.current.push(M)}E(t)}else w<P&&c.current.splice(w)}},[i]),f.useEffect(()=>{const y=d.current,w=g.current;if(!y||!w)return;const P=M=>{for(const D of M){const{width:x,height:p}=D.contentRect,X=window.devicePixelRatio||1;w.width=x*X,w.height=p*X,w.style.width=`${x}px`,w.style.height=`${p}px`;const Y=w.getContext("2d");Y&&Y.scale(X,X),c.current.length===0&&C(x,p,i),E(t,x,p)}},S=new ResizeObserver(P);S.observe(y);const I=w.getContext("2d"),z=()=>{l.current++;const M=y.getBoundingClientRect(),{isMagnet:D,clickMode:x,particleShape:p,backgroundColor:X}=R.current;if(X==="transparent")I.clearRect(0,0,M.width,M.height),I.globalCompositeOperation="source-over";else{const B=k(X);I.fillStyle=`rgba(${B}, 0.25)`,I.fillRect(0,0,M.width,M.height),I.globalCompositeOperation="screen"}const Y=T.current!=="",A=u.current.active?u.current.x:null,F=u.current.active?u.current.y:null,W=u.current.isDown;for(let B=0;B<c.current.length;B++){const $=c.current[B];$.update(l.current,!!Y,A,F,W,D,x),$.draw(I,p,l.current)}I.globalCompositeOperation="source-over",a.current=requestAnimationFrame(z)};return z(),()=>{S.disconnect(),cancelAnimationFrame(a.current)}},[]),f.useEffect(()=>{E(t)},[t]),q.jsx("div",{ref:d,style:{position:"absolute",inset:0,zIndex:0,overflow:"hidden"},children:q.jsx("canvas",{ref:g,style:{display:"block",width:"100%",height:"100%"}})})}L.BackgroundParticleEngine=j,L.JellyfishParticleEngine=J,L.NetParticleEngine=H,L.ParticleCanvas=it,L.TextParticleEngine=ct,L.getMagnetTarget=K,L.useParticleInteraction=G,L.useTextParticles=Q,Object.defineProperty(L,Symbol.toStringTag,{value:"Module"})}));
package/package.json CHANGED
@@ -1,10 +1,21 @@
1
1
  {
2
2
  "name": "jl-particle-interactive",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "private": false,
5
- "description": "Interactive particle text animation components for React",
6
- "author": "JL",
5
+ "description": "Canvas-based React library for particle text animations and interactive backgrounds — spring physics, magnetic hover, click attract/repel, NET/JELLYFISH modes. TypeScript. Zero dependencies.",
6
+ "author": {
7
+ "name": "cjorgeluis122333",
8
+ "url": "https://github.com/cjorgeluis122333"
9
+ },
7
10
  "license": "MIT",
11
+ "homepage": "https://cjorgeluis122333.github.io/jl-particles-interactive",
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "https://github.com/cjorgeluis122333/jl-particles-interactive"
15
+ },
16
+ "bugs": {
17
+ "url": "https://github.com/cjorgeluis122333/jl-particles-interactive/issues"
18
+ },
8
19
  "type": "module",
9
20
  "main": "./dist/jl-particle-interactive.umd.cjs",
10
21
  "module": "./dist/jl-particle-interactive.js",
@@ -17,7 +28,8 @@
17
28
  }
18
29
  },
19
30
  "files": [
20
- "dist"
31
+ "dist",
32
+ "README.md"
21
33
  ],
22
34
  "scripts": {
23
35
  "build": "vite build",
@@ -29,7 +41,21 @@
29
41
  "animation",
30
42
  "canvas",
31
43
  "interactive",
32
- "text"
44
+ "text",
45
+ "particle-text",
46
+ "text-animation",
47
+ "particle-background",
48
+ "canvas-animation",
49
+ "react-particles",
50
+ "magnetic",
51
+ "hover-effect",
52
+ "cursor-effect",
53
+ "spring-physics",
54
+ "hero",
55
+ "hero-section",
56
+ "typescript",
57
+ "zero-dependencies",
58
+ "particle-network"
33
59
  ],
34
60
  "peerDependencies": {
35
61
  "react": ">=18.0.0",