react-smokey-fluid-cursor 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +181 -0
- package/dist/index.d.ts +55 -0
- package/dist/index.js +292 -0
- package/dist/index.mjs +293 -0
- package/package.json +53 -0
package/README.md
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
<h1 align="center">💨 React Smokey Fluid Cursor</h1>
|
|
2
|
+
|
|
3
|
+
<p align="center">
|
|
4
|
+
A beautiful, interactive fluid simulation that creates stunning visual effects following your cursor movements for your React and Next.js application on web. Built with WebGL for high-performance real-time fluid dynamics.
|
|
5
|
+
</p>
|
|
6
|
+
|
|
7
|
+

|
|
8
|
+

|
|
9
|
+
[](https://www.jsdelivr.com/package/npm/react-smokey-fluid-cursor)
|
|
10
|
+
[](https://standardjs.com)
|
|
11
|
+
|
|
12
|
+

|
|
13
|
+

|
|
14
|
+

|
|
15
|
+

|
|
16
|
+

|
|
17
|
+
|
|
18
|
+
[](https://npmjs.org/package/react-smokey-fluid-cursor)
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## 📦 Installation
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npm i react-smokey-fluid-cursor
|
|
26
|
+
|
|
27
|
+
yarn add react-smokey-fluid-cursor
|
|
28
|
+
|
|
29
|
+
pnpm i react-smokey-fluid-cursor
|
|
30
|
+
|
|
31
|
+
bun add react-smokey-fluid-cursor
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## 📸 Demo
|
|
37
|
+
|
|
38
|
+
Also see more details in [Example](https://github.com/faraasat/react-smokey-fluid-cursor/tree/main/example):
|
|
39
|
+
|
|
40
|
+

|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## 🚀 Quick Start
|
|
45
|
+
|
|
46
|
+
### **React (CRA)**
|
|
47
|
+
|
|
48
|
+
```tsx
|
|
49
|
+
// src/App.tsx|jsx
|
|
50
|
+
import React from "react";
|
|
51
|
+
|
|
52
|
+
import { SmokeyFluidCursor } from "react-smokey-fluid-cursor";
|
|
53
|
+
|
|
54
|
+
function App() {
|
|
55
|
+
return (
|
|
56
|
+
<div className="App">
|
|
57
|
+
{/* Place observer once globally */}
|
|
58
|
+
<SmokeyFluidCursor />
|
|
59
|
+
</div>
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### **Vite + React**
|
|
65
|
+
|
|
66
|
+
```tsx
|
|
67
|
+
// src/main.tsx|jsx
|
|
68
|
+
import React from "react";
|
|
69
|
+
|
|
70
|
+
import { SmokeyFluidCursor } from "react-smokey-fluid-cursor";
|
|
71
|
+
|
|
72
|
+
function Main() {
|
|
73
|
+
return (
|
|
74
|
+
<>
|
|
75
|
+
<SmokeyFluidCursor />
|
|
76
|
+
</>
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export default Main;
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### **Next.js Pages Router**
|
|
84
|
+
|
|
85
|
+
```tsx
|
|
86
|
+
// pages/_app.tsx|jsx
|
|
87
|
+
import type { AppProps } from "next/app";
|
|
88
|
+
|
|
89
|
+
import { SmokeyFluidCursor } from "react-smokey-fluid-cursor";
|
|
90
|
+
|
|
91
|
+
function MyApp({ Component, pageProps }: AppProps) {
|
|
92
|
+
return (
|
|
93
|
+
<>
|
|
94
|
+
{/* Global ad observer */}
|
|
95
|
+
<SmokeyFluidCursor />
|
|
96
|
+
<Component {...pageProps} />
|
|
97
|
+
</>
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export default MyApp;
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### **Next.js (App Router)**
|
|
105
|
+
|
|
106
|
+
```tsx
|
|
107
|
+
// app/layout.tsx|jsx
|
|
108
|
+
import "./globals.css";
|
|
109
|
+
|
|
110
|
+
import { SmokeyFluidCursor } from "react-smokey-fluid-cursor";
|
|
111
|
+
|
|
112
|
+
export default function RootLayout({
|
|
113
|
+
children,
|
|
114
|
+
}: {
|
|
115
|
+
children: React.ReactNode;
|
|
116
|
+
}) {
|
|
117
|
+
return (
|
|
118
|
+
<html lang="en">
|
|
119
|
+
<body>
|
|
120
|
+
<SmokeyFluidCursor />
|
|
121
|
+
{children}
|
|
122
|
+
</body>
|
|
123
|
+
</html>
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## ⚙️ Configuration Options
|
|
131
|
+
|
|
132
|
+
Customize the fluid simulation with these configuration options:
|
|
133
|
+
|
|
134
|
+
| Property | Default Value | Description |
|
|
135
|
+
| --------------------- | ----------------------------- | ---------------------------------------------- |
|
|
136
|
+
| `id` | `"react-smokey-fluid-cursor"` | Canvas element ID |
|
|
137
|
+
| `simResolution` | `128` | Simulation resolution (higher = more detailed) |
|
|
138
|
+
| `dyeResolution` | `512` | Dye/color resolution |
|
|
139
|
+
| `densityDissipation` | `0.98` | How quickly colors fade (0–1) |
|
|
140
|
+
| `velocityDissipation` | `0.98` | How quickly movement slows down |
|
|
141
|
+
| `pressureIteration` | `10` | Pressure solver iterations |
|
|
142
|
+
| `curl` | `30` | Vorticity/swirl intensity |
|
|
143
|
+
| `splatRadius` | `0.25` | Size of cursor splats |
|
|
144
|
+
| `splatForce` | `6000` | Force of cursor movements |
|
|
145
|
+
| `shading` | `true` | Enable 3D lighting effects |
|
|
146
|
+
| `colorUpdateSpeed` | `0.5` | Speed of color transitions |
|
|
147
|
+
| `transparent` | `false` | Transparent background |
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
## 🌟 Features
|
|
152
|
+
|
|
153
|
+
- **Real-time Fluid Dynamics**: Physics-based simulation using Navier-Stokes equations
|
|
154
|
+
- **WebGL Accelerated**: High-performance rendering for smooth 60fps
|
|
155
|
+
- **Interactive**: Responds to mouse and touch movements
|
|
156
|
+
- **Customizable**: Extensive configuration options
|
|
157
|
+
- **Mobile Support**: Touch-optimized interactions
|
|
158
|
+
- **Auto-scaling**: Adapts to screen size and pixel ratio
|
|
159
|
+
- **Color Cycling**: Dynamic, evolving color palettes
|
|
160
|
+
- **3D Lighting**: Optional shading for depth perception
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## 🎯 Use Cases
|
|
165
|
+
|
|
166
|
+
- **Website Backgrounds**: Immersive animated backgrounds
|
|
167
|
+
- **Cursor Effects**: Enhanced user interaction feedback
|
|
168
|
+
- **Data Visualization**: Fluid-based data representations
|
|
169
|
+
- **Art Installations**: Digital art and creative coding
|
|
170
|
+
- **Game Effects**: Atmospheric and UI effects
|
|
171
|
+
- **Product Demos**: Eye-catching technology showcases
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## 🧑💻 Author
|
|
176
|
+
|
|
177
|
+
Built and maintained by [**Farasat Ali**](https://www.farasat.me)
|
|
178
|
+
|
|
179
|
+
- Website: [www.farasat.me](https://www.farasat.me)
|
|
180
|
+
- LinkedIn: [linkedin.com/in/faraasat](https://linkedin.com/in/faraasat)
|
|
181
|
+
- GitHub: [github.com/faraasat](https://github.com/faraasat)
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
interface ISmokeyFluidConfig {
|
|
4
|
+
/** Simulation grid resolution for velocity/pressure fields (lower = faster but coarser) */
|
|
5
|
+
simResolution: number;
|
|
6
|
+
/** Visual color/dye buffer resolution (affects rendering detail) */
|
|
7
|
+
dyeResolution: number;
|
|
8
|
+
/** Resolution used for high-quality screenshots or recording */
|
|
9
|
+
captureResolution: number;
|
|
10
|
+
/** Rate at which color fades from the fluid (higher = faster fade) */
|
|
11
|
+
densityDissipation: number;
|
|
12
|
+
/** Rate at which velocity energy dissipates (higher = faster slowdown) */
|
|
13
|
+
velocityDissipation: number;
|
|
14
|
+
/** Initial pressure clear multiplier (affects stability of solver) */
|
|
15
|
+
pressure: number;
|
|
16
|
+
/** Number of Jacobi iterations when solving pressure (higher = more accurate but slower) */
|
|
17
|
+
pressureIteration: number;
|
|
18
|
+
/** Strength of vorticity confinement (adds swirling, curl-like motion) */
|
|
19
|
+
curl: number;
|
|
20
|
+
/** Base normalized radius of user splats (0–1 range relative to screen size) */
|
|
21
|
+
splatRadius: number;
|
|
22
|
+
/** Force multiplier applied when user interacts (e.g., mouse/touch input) */
|
|
23
|
+
splatForce: number;
|
|
24
|
+
/** Enable or disable lighting/shading effects for visual depth */
|
|
25
|
+
shading: boolean;
|
|
26
|
+
/** Speed at which the dynamic color palette rotates during simulation */
|
|
27
|
+
colorUpdateSpeed: number;
|
|
28
|
+
/** If true, pauses the main simulation loop (used for debugging or static rendering) */
|
|
29
|
+
paused: boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Canvas background color (can be in 0–1 normalized range or 0–255)
|
|
32
|
+
* Example: `{ r: 0, g: 0, b: 0 }` for black.
|
|
33
|
+
*/
|
|
34
|
+
backColor: {
|
|
35
|
+
r: number;
|
|
36
|
+
g: number;
|
|
37
|
+
b: number;
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Determines whether the canvas should preserve alpha transparency.
|
|
41
|
+
* If `true`, blending with HTML backgrounds is allowed.
|
|
42
|
+
*/
|
|
43
|
+
transparent: boolean;
|
|
44
|
+
/**
|
|
45
|
+
* ID assigned to the canvas element.
|
|
46
|
+
* Default is "smokey-fluid-canvas".
|
|
47
|
+
*/
|
|
48
|
+
id: string;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
declare const SmokeyFluidCursor: React.FC<{
|
|
52
|
+
config: Partial<ISmokeyFluidConfig>;
|
|
53
|
+
}>;
|
|
54
|
+
|
|
55
|
+
export { SmokeyFluidCursor };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
"use client";"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }var Me=Object.defineProperty;var ie=Object.getOwnPropertySymbols;var Oe=Object.prototype.hasOwnProperty,Ye=Object.prototype.propertyIsEnumerable;var oe=(f,c,v)=>c in f?Me(f,c,{enumerable:!0,configurable:!0,writable:!0,value:v}):f[c]=v,N=(f,c)=>{for(var v in c||(c={}))Oe.call(c,v)&&oe(f,v,c[v]);if(ie)for(var v of ie(c))Ye.call(c,v)&&oe(f,v,c[v]);return f};var _react = require('react'); var _react2 = _interopRequireDefault(_react);var ne=f=>{let c=document.getElementById(f.id);if(!c)return;let v=document.createElement("style");v.textContent=`
|
|
2
|
+
#${f.id} {
|
|
3
|
+
position: fixed;
|
|
4
|
+
top: 0;
|
|
5
|
+
left: 0;
|
|
6
|
+
width: 100%;
|
|
7
|
+
height: 100%;
|
|
8
|
+
pointer-events: none;
|
|
9
|
+
z-index: -9999;
|
|
10
|
+
}
|
|
11
|
+
`,document.head.appendChild(v),j();class ue{constructor(){this.id=-1;this.texcoordX=0;this.texcoordY=0;this.prevTexcoordX=0;this.prevTexcoordY=0;this.deltaX=0;this.deltaY=0;this.down=!1;this.moved=!1;this.color=[30,0,300]}}let S=[];S.push(new ue);let{gl:t,ext:T}=ce(c);T.supportLinearFiltering||(f.dyeResolution=512,f.shading=!1);function ce(e){let r={alpha:!0,depth:!1,stencil:!1,antialias:!1,preserveDrawingBuffer:!1},i=e.getContext("webgl2",r),o=!!i;if(o||(i=e.getContext("webgl",r)||e.getContext("experimental-webgl",r)),!i)throw new Error("WebGL not supported");let n=null,u,a;if(o){let l=i;l.getExtension("EXT_color_buffer_float"),u=!!l.getExtension("OES_texture_float_linear"),a=l.HALF_FLOAT}else{let l=i;if(n=l.getExtension("OES_texture_half_float"),u=!!l.getExtension("OES_texture_half_float_linear"),!n)throw new Error("OES_texture_half_float not supported on WebGL1");a=n.HALF_FLOAT_OES}i.clearColor(0,0,0,1);let m=null,d=null,y=null;if(o){let l=i;m=F(l,l.RGBA16F,l.RGBA,a),d=F(l,l.RG16F,l.RG,a),y=F(l,l.R16F,l.RED,a)}else{let l=i;m=F(l,l.RGBA,l.RGBA,a),d=F(l,l.RGBA,l.RGBA,a),y=F(l,l.RGBA,l.RGBA,a)}return{gl:i,ext:{formatRGBA:m,formatRG:d,formatR:y,halfFloatTexType:a,supportLinearFiltering:u,isWebGL2:o}}}function F(e,r,i,o){if(!se(e,r,i,o)){if(e.RGBA16F!==void 0){let n=e;switch(r){case n.R16F:return F(n,n.RG16F,n.RG,o);case n.RG16F:return F(n,n.RGBA16F,n.RGBA,o);default:return null}}return null}return{internalFormat:r,format:i}}function se(e,r,i,o){let n=e.createTexture();if(!n)return!1;e.bindTexture(e.TEXTURE_2D,n),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MIN_FILTER,e.NEAREST),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MAG_FILTER,e.NEAREST),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_S,e.CLAMP_TO_EDGE),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_T,e.CLAMP_TO_EDGE),e.texImage2D(e.TEXTURE_2D,0,r,4,4,0,i,o,null);let u=e.createFramebuffer();if(!u)return!1;e.bindFramebuffer(e.FRAMEBUFFER,u),e.framebufferTexture2D(e.FRAMEBUFFER,e.COLOR_ATTACHMENT0,e.TEXTURE_2D,n,0);let a=e.checkFramebufferStatus(e.FRAMEBUFFER);return e.bindFramebuffer(e.FRAMEBUFFER,null),e.deleteFramebuffer(u),e.deleteTexture(n),a===e.FRAMEBUFFER_COMPLETE}class le{constructor(r,i){this.vertexShader=r,this.fragmentShaderSource=i,this.programs={},this.activeProgram=null,this.uniforms={}}setKeywords(r){let i=0;for(let n=0;n<r.length;n++)i+=ze(r[n]);let o=this.programs[i];if(!o){let n=x(t,t.FRAGMENT_SHADER,V(this.fragmentShaderSource,r));o=I(t,this.vertexShader,n),this.programs[i]=o}o!==this.activeProgram&&(this.uniforms=W(t,o),this.activeProgram=o)}bind(){this.activeProgram&&t.useProgram(this.activeProgram)}}class E{constructor(r,i){this.program=I(t,r,i),this.uniforms=W(t,this.program)}bind(){this.program&&t.useProgram(this.program)}}function I(e,r,i){let o=e.createProgram();return e.attachShader(o,r),e.attachShader(o,i),e.bindAttribLocation(o,0,"aPosition"),e.linkProgram(o),e.getProgramParameter(o,e.LINK_STATUS)||console.trace(e.getProgramInfoLog(o)),o}function W(e,r){let i={},o=e.getProgramParameter(r,e.ACTIVE_UNIFORMS);for(let n=0;n<o;n++){let u=e.getActiveUniform(r,n);if(!u)continue;let a=u.name,m=e.getUniformLocation(r,a);m&&(i[a]=m)}return i}function x(e,r,i){let o=e.createShader(r);return e.shaderSource(o,i),e.compileShader(o),e.getShaderParameter(o,e.COMPILE_STATUS)||console.trace(e.getShaderInfoLog(o)),o}function V(e,r){if(!r||r.length===0)return e;let i="";return r.forEach(o=>{i+="#define "+o+`
|
|
12
|
+
`}),i+e}let b=x(t,t.VERTEX_SHADER,`
|
|
13
|
+
precision highp float;
|
|
14
|
+
|
|
15
|
+
attribute vec2 aPosition;
|
|
16
|
+
varying vec2 vUv;
|
|
17
|
+
varying vec2 vL;
|
|
18
|
+
varying vec2 vR;
|
|
19
|
+
varying vec2 vT;
|
|
20
|
+
varying vec2 vB;
|
|
21
|
+
uniform vec2 texelSize;
|
|
22
|
+
|
|
23
|
+
void main () {
|
|
24
|
+
vUv = aPosition * 0.5 + 0.5;
|
|
25
|
+
vL = vUv - vec2(texelSize.x, 0.0);
|
|
26
|
+
vR = vUv + vec2(texelSize.x, 0.0);
|
|
27
|
+
vT = vUv + vec2(0.0, texelSize.y);
|
|
28
|
+
vB = vUv - vec2(0.0, texelSize.y);
|
|
29
|
+
gl_Position = vec4(aPosition, 0.0, 1.0);
|
|
30
|
+
}
|
|
31
|
+
`),fe=x(t,t.FRAGMENT_SHADER,`
|
|
32
|
+
precision mediump float;
|
|
33
|
+
precision mediump sampler2D;
|
|
34
|
+
|
|
35
|
+
varying highp vec2 vUv;
|
|
36
|
+
uniform sampler2D uTexture;
|
|
37
|
+
|
|
38
|
+
void main () {
|
|
39
|
+
gl_FragColor = texture2D(uTexture, vUv);
|
|
40
|
+
}
|
|
41
|
+
`),me=x(t,t.FRAGMENT_SHADER,`
|
|
42
|
+
precision mediump float;
|
|
43
|
+
precision mediump sampler2D;
|
|
44
|
+
|
|
45
|
+
varying highp vec2 vUv;
|
|
46
|
+
uniform sampler2D uTexture;
|
|
47
|
+
uniform float value;
|
|
48
|
+
|
|
49
|
+
void main () {
|
|
50
|
+
gl_FragColor = value * texture2D(uTexture, vUv);
|
|
51
|
+
}
|
|
52
|
+
`),de=`
|
|
53
|
+
precision highp float;
|
|
54
|
+
precision highp sampler2D;
|
|
55
|
+
|
|
56
|
+
varying vec2 vUv;
|
|
57
|
+
varying vec2 vL;
|
|
58
|
+
varying vec2 vR;
|
|
59
|
+
varying vec2 vT;
|
|
60
|
+
varying vec2 vB;
|
|
61
|
+
uniform sampler2D uTexture;
|
|
62
|
+
uniform vec2 texelSize;
|
|
63
|
+
|
|
64
|
+
void main () {
|
|
65
|
+
vec3 c = texture2D(uTexture, vUv).rgb;
|
|
66
|
+
|
|
67
|
+
#ifdef shading
|
|
68
|
+
// Calculate normal from neighboring pixels for lighting
|
|
69
|
+
vec3 lc = texture2D(uTexture, vL).rgb;
|
|
70
|
+
vec3 rc = texture2D(uTexture, vR).rgb;
|
|
71
|
+
vec3 tc = texture2D(uTexture, vT).rgb;
|
|
72
|
+
vec3 bc = texture2D(uTexture, vB).rgb;
|
|
73
|
+
|
|
74
|
+
float dx = length(rc) - length(lc);
|
|
75
|
+
float dy = length(tc) - length(bc);
|
|
76
|
+
|
|
77
|
+
vec3 n = normalize(vec3(dx, dy, length(texelSize)));
|
|
78
|
+
vec3 l = vec3(0.0, 0.0, 1.0);
|
|
79
|
+
|
|
80
|
+
// Apply diffuse lighting
|
|
81
|
+
float diffuse = clamp(dot(n, l) + 0.7, 0.7, 1.0);
|
|
82
|
+
c *= diffuse;
|
|
83
|
+
#endif
|
|
84
|
+
|
|
85
|
+
// Use brightest channel for alpha
|
|
86
|
+
float a = max(c.r, max(c.g, c.b));
|
|
87
|
+
gl_FragColor = vec4(c, a);
|
|
88
|
+
}
|
|
89
|
+
`,ve=x(t,t.FRAGMENT_SHADER,`
|
|
90
|
+
precision highp float;
|
|
91
|
+
precision highp sampler2D;
|
|
92
|
+
|
|
93
|
+
varying vec2 vUv;
|
|
94
|
+
uniform sampler2D uTarget;
|
|
95
|
+
uniform float aspectRatio;
|
|
96
|
+
uniform vec3 color;
|
|
97
|
+
uniform vec2 point;
|
|
98
|
+
uniform float radius;
|
|
99
|
+
|
|
100
|
+
void main () {
|
|
101
|
+
// Calculate Gaussian splat
|
|
102
|
+
vec2 p = vUv - point.xy;
|
|
103
|
+
p.x *= aspectRatio;
|
|
104
|
+
vec3 splat = exp(-dot(p, p) / radius) * color;
|
|
105
|
+
vec3 base = texture2D(uTarget, vUv).xyz;
|
|
106
|
+
gl_FragColor = vec4(base + splat, 1.0);
|
|
107
|
+
}
|
|
108
|
+
`),he=x(t,t.FRAGMENT_SHADER,V(`
|
|
109
|
+
precision highp float;
|
|
110
|
+
precision highp sampler2D;
|
|
111
|
+
|
|
112
|
+
varying vec2 vUv;
|
|
113
|
+
uniform sampler2D uVelocity;
|
|
114
|
+
uniform sampler2D uSource;
|
|
115
|
+
uniform vec2 texelSize;
|
|
116
|
+
uniform vec2 dyeTexelSize;
|
|
117
|
+
uniform float dt;
|
|
118
|
+
uniform float dissipation;
|
|
119
|
+
|
|
120
|
+
// Manual bilinear filtering for WebGL1 without linear filtering support
|
|
121
|
+
vec4 bilerp (sampler2D sam, vec2 uv, vec2 tsize) {
|
|
122
|
+
vec2 st = uv / tsize - 0.5;
|
|
123
|
+
|
|
124
|
+
vec2 iuv = floor(st);
|
|
125
|
+
vec2 fuv = fract(st);
|
|
126
|
+
|
|
127
|
+
vec4 a = texture2D(sam, (iuv + vec2(0.5, 0.5)) * tsize);
|
|
128
|
+
vec4 b = texture2D(sam, (iuv + vec2(1.5, 0.5)) * tsize);
|
|
129
|
+
vec4 c = texture2D(sam, (iuv + vec2(0.5, 1.5)) * tsize);
|
|
130
|
+
vec4 d = texture2D(sam, (iuv + vec2(1.5, 1.5)) * tsize);
|
|
131
|
+
|
|
132
|
+
return mix(mix(a, b, fuv.x), mix(c, d, fuv.x), fuv.y);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
void main () {
|
|
136
|
+
#ifdef MANUAL_FILTERING
|
|
137
|
+
// Manual filtering for devices without linear filtering
|
|
138
|
+
vec2 coord = vUv - dt * bilerp(uVelocity, vUv, texelSize).xy * texelSize;
|
|
139
|
+
vec4 result = bilerp(uSource, coord, dyeTexelSize);
|
|
140
|
+
#else
|
|
141
|
+
// Standard texture lookup with linear filtering
|
|
142
|
+
vec2 coord = vUv - dt * texture2D(uVelocity, vUv).xy * texelSize;
|
|
143
|
+
vec4 result = texture2D(uSource, coord);
|
|
144
|
+
#endif
|
|
145
|
+
// Apply dissipation (fade over time)
|
|
146
|
+
float decay = 1.0 + dissipation * dt;
|
|
147
|
+
gl_FragColor = result / decay;
|
|
148
|
+
}
|
|
149
|
+
`,T.supportLinearFiltering?null:["MANUAL_FILTERING"])),pe=x(t,t.FRAGMENT_SHADER,`
|
|
150
|
+
precision mediump float;
|
|
151
|
+
precision mediump sampler2D;
|
|
152
|
+
|
|
153
|
+
varying highp vec2 vUv;
|
|
154
|
+
varying highp vec2 vL;
|
|
155
|
+
varying highp vec2 vR;
|
|
156
|
+
varying highp vec2 vT;
|
|
157
|
+
varying highp vec2 vB;
|
|
158
|
+
uniform sampler2D uVelocity;
|
|
159
|
+
|
|
160
|
+
void main () {
|
|
161
|
+
// Sample neighboring velocities
|
|
162
|
+
float L = texture2D(uVelocity, vL).x;
|
|
163
|
+
float R = texture2D(uVelocity, vR).x;
|
|
164
|
+
float T = texture2D(uVelocity, vT).y;
|
|
165
|
+
float B = texture2D(uVelocity, vB).y;
|
|
166
|
+
|
|
167
|
+
vec2 C = texture2D(uVelocity, vUv).xy;
|
|
168
|
+
|
|
169
|
+
// Boundary conditions
|
|
170
|
+
if (vL.x < 0.0) { L = -C.x; }
|
|
171
|
+
if (vR.x > 1.0) { R = -C.x; }
|
|
172
|
+
if (vT.y > 1.0) { T = -C.y; }
|
|
173
|
+
if (vB.y < 0.0) { B = -C.y; }
|
|
174
|
+
|
|
175
|
+
// Calculate divergence
|
|
176
|
+
float div = 0.5 * (R - L + T - B);
|
|
177
|
+
gl_FragColor = vec4(div, 0.0, 0.0, 1.0);
|
|
178
|
+
}
|
|
179
|
+
`),xe=x(t,t.FRAGMENT_SHADER,`
|
|
180
|
+
precision mediump float;
|
|
181
|
+
precision mediump sampler2D;
|
|
182
|
+
|
|
183
|
+
varying highp vec2 vUv;
|
|
184
|
+
varying highp vec2 vL;
|
|
185
|
+
varying highp vec2 vR;
|
|
186
|
+
varying highp vec2 vT;
|
|
187
|
+
varying highp vec2 vB;
|
|
188
|
+
uniform sampler2D uVelocity;
|
|
189
|
+
|
|
190
|
+
void main () {
|
|
191
|
+
// Sample velocity components for curl calculation
|
|
192
|
+
float L = texture2D(uVelocity, vL).y;
|
|
193
|
+
float R = texture2D(uVelocity, vR).y;
|
|
194
|
+
float T = texture2D(uVelocity, vT).x;
|
|
195
|
+
float B = texture2D(uVelocity, vB).x;
|
|
196
|
+
float vorticity = R - L - T + B;
|
|
197
|
+
gl_FragColor = vec4(0.5 * vorticity, 0.0, 0.0, 1.0);
|
|
198
|
+
}
|
|
199
|
+
`),ge=x(t,t.FRAGMENT_SHADER,`
|
|
200
|
+
precision highp float;
|
|
201
|
+
precision highp sampler2D;
|
|
202
|
+
|
|
203
|
+
varying vec2 vUv;
|
|
204
|
+
varying vec2 vL;
|
|
205
|
+
varying vec2 vR;
|
|
206
|
+
varying vec2 vT;
|
|
207
|
+
varying vec2 vB;
|
|
208
|
+
uniform sampler2D uVelocity;
|
|
209
|
+
uniform sampler2D uCurl;
|
|
210
|
+
uniform float curl;
|
|
211
|
+
uniform float dt;
|
|
212
|
+
|
|
213
|
+
void main () {
|
|
214
|
+
// Sample curl from neighbors
|
|
215
|
+
float L = texture2D(uCurl, vL).x;
|
|
216
|
+
float R = texture2D(uCurl, vR).x;
|
|
217
|
+
float T = texture2D(uCurl, vT).x;
|
|
218
|
+
float B = texture2D(uCurl, vB).x;
|
|
219
|
+
float C = texture2D(uCurl, vUv).x;
|
|
220
|
+
|
|
221
|
+
// Calculate vorticity force
|
|
222
|
+
vec2 force = 0.5 * vec2(abs(T) - abs(B), abs(R) - abs(L));
|
|
223
|
+
force /= length(force) + 0.0001; // Normalize
|
|
224
|
+
force *= curl * C; // Scale by curl strength
|
|
225
|
+
force.y *= -1.0; // Adjust coordinate system
|
|
226
|
+
|
|
227
|
+
// Apply force to velocity
|
|
228
|
+
vec2 velocity = texture2D(uVelocity, vUv).xy;
|
|
229
|
+
velocity += force * dt;
|
|
230
|
+
velocity = min(max(velocity, -1000.0), 1000.0); // Clamp to prevent explosion
|
|
231
|
+
gl_FragColor = vec4(velocity, 0.0, 1.0);
|
|
232
|
+
}
|
|
233
|
+
`),be=x(t,t.FRAGMENT_SHADER,`
|
|
234
|
+
precision mediump float;
|
|
235
|
+
precision mediump sampler2D;
|
|
236
|
+
|
|
237
|
+
varying highp vec2 vUv;
|
|
238
|
+
varying highp vec2 vL;
|
|
239
|
+
varying highp vec2 vR;
|
|
240
|
+
varying highp vec2 vT;
|
|
241
|
+
varying highp vec2 vB;
|
|
242
|
+
uniform sampler2D uPressure;
|
|
243
|
+
uniform sampler2D uDivergence;
|
|
244
|
+
|
|
245
|
+
void main () {
|
|
246
|
+
// Sample pressure from neighbors
|
|
247
|
+
float L = texture2D(uPressure, vL).x;
|
|
248
|
+
float R = texture2D(uPressure, vR).x;
|
|
249
|
+
float T = texture2D(uPressure, vT).x;
|
|
250
|
+
float B = texture2D(uPressure, vB).x;
|
|
251
|
+
float C = texture2D(uPressure, vUv).x;
|
|
252
|
+
float divergence = texture2D(uDivergence, vUv).x;
|
|
253
|
+
|
|
254
|
+
// Jacobi iteration for pressure solve
|
|
255
|
+
float pressure = (L + R + B + T - divergence) * 0.25;
|
|
256
|
+
gl_FragColor = vec4(pressure, 0.0, 0.0, 1.0);
|
|
257
|
+
}
|
|
258
|
+
`),Re=x(t,t.FRAGMENT_SHADER,`
|
|
259
|
+
precision mediump float;
|
|
260
|
+
precision mediump sampler2D;
|
|
261
|
+
|
|
262
|
+
varying highp vec2 vUv;
|
|
263
|
+
varying highp vec2 vL;
|
|
264
|
+
varying highp vec2 vR;
|
|
265
|
+
varying highp vec2 vT;
|
|
266
|
+
varying highp vec2 vB;
|
|
267
|
+
uniform sampler2D uPressure;
|
|
268
|
+
uniform sampler2D uVelocity;
|
|
269
|
+
|
|
270
|
+
void main () {
|
|
271
|
+
// Calculate pressure gradient
|
|
272
|
+
float L = texture2D(uPressure, vL).x;
|
|
273
|
+
float R = texture2D(uPressure, vR).x;
|
|
274
|
+
float T = texture2D(uPressure, vT).x;
|
|
275
|
+
float B = texture2D(uPressure, vB).x;
|
|
276
|
+
vec2 velocity = texture2D(uVelocity, vUv).xy;
|
|
277
|
+
|
|
278
|
+
// Subtract gradient to make velocity divergence-free
|
|
279
|
+
velocity.xy -= vec2(R - L, T - B);
|
|
280
|
+
gl_FragColor = vec4(velocity, 0.0, 1.0);
|
|
281
|
+
}
|
|
282
|
+
`),p=(()=>{let e=t.createBuffer();t.bindBuffer(t.ARRAY_BUFFER,e),t.bufferData(t.ARRAY_BUFFER,new Float32Array([-1,-1,-1,1,1,1,1,-1]),t.STATIC_DRAW);let r=t.createBuffer();return t.bindBuffer(t.ELEMENT_ARRAY_BUFFER,r),t.bufferData(t.ELEMENT_ARRAY_BUFFER,new Uint16Array([0,1,2,0,2,3]),t.STATIC_DRAW),t.vertexAttribPointer(0,2,t.FLOAT,!1,0,0),t.enableVertexAttribArray(0),(i,o=!1)=>{i==null?(t.viewport(0,0,t.drawingBufferWidth,t.drawingBufferHeight),t.bindFramebuffer(t.FRAMEBUFFER,null)):(t.viewport(0,0,i.width,i.height),t.bindFramebuffer(t.FRAMEBUFFER,i.fbo)),o&&(t.clearColor(0,0,0,1),t.clear(t.COLOR_BUFFER_BIT)),t.drawElements(t.TRIANGLES,6,t.UNSIGNED_SHORT,0)}})(),h,s,P,G,D,H=new E(b,fe),X=new E(b,me),L=new E(b,ve),g=new E(b,he),z=new E(b,pe),M=new E(b,xe),_=new E(b,ge),B=new E(b,be),w=new E(b,Re),U=new le(b,de);function k(){let e=re(f.simResolution),r=re(f.dyeResolution),i=T.halfFloatTexType,o=T.formatRGBA,n=T.formatRG,u=T.formatR,a=T.supportLinearFiltering?t.LINEAR:t.NEAREST;t.disable(t.BLEND),h==null?h=O(r.width,r.height,o.internalFormat,o.format,i,a):h=K(h,r.width,r.height,o.internalFormat,o.format,i,a),s==null?s=O(e.width,e.height,n.internalFormat,n.format,i,a):s=K(s,e.width,e.height,n.internalFormat,n.format,i,a),P=A(e.width,e.height,u.internalFormat,u.format,i,t.NEAREST),G=A(e.width,e.height,u.internalFormat,u.format,i,t.NEAREST),D=O(e.width,e.height,u.internalFormat,u.format,i,t.NEAREST)}function A(e,r,i,o,n,u){t.activeTexture(t.TEXTURE0);let a=t.createTexture();t.bindTexture(t.TEXTURE_2D,a),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_MIN_FILTER,u),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_MAG_FILTER,u),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_WRAP_S,t.CLAMP_TO_EDGE),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_WRAP_T,t.CLAMP_TO_EDGE),t.texImage2D(t.TEXTURE_2D,0,i,e,r,0,o,n,null);let m=t.createFramebuffer();t.bindFramebuffer(t.FRAMEBUFFER,m),t.framebufferTexture2D(t.FRAMEBUFFER,t.COLOR_ATTACHMENT0,t.TEXTURE_2D,a,0),t.viewport(0,0,e,r),t.clear(t.COLOR_BUFFER_BIT);let d=1/e,y=1/r;return{texture:a,fbo:m,width:e,height:r,texelSizeX:d,texelSizeY:y,attach(l){return t.activeTexture(t.TEXTURE0+l),t.bindTexture(t.TEXTURE_2D,a),l}}}function O(e,r,i,o,n,u){let a=A(e,r,i,o,n,u),m=A(e,r,i,o,n,u);return{width:e,height:r,texelSizeX:a.texelSizeX,texelSizeY:a.texelSizeY,get read(){return a},set read(d){a=d},get write(){return m},set write(d){m=d},swap(){let d=a;a=m,m=d}}}function Te(e,r,i,o,n,u,a){let m=A(r,i,o,n,u,a);return H.bind(),t.uniform1i(H.uniforms.uTexture,e.attach(0)),p(m),m}function K(e,r,i,o,n,u,a){return e.width===r&&e.height===i||(e.read=Te(e.read,r,i,o,n,u,a),e.write=A(r,i,o,n,u,a),e.width=r,e.height=i,e.texelSizeX=1/r,e.texelSizeY=1/i),e}function Ee(){let e=[];f.shading&&e.push("shading"),U.setKeywords(e)}Ee(),k();let q=Date.now(),C=0;function $(){let e=ye();j()&&k(),Se(e),Fe(),De(e),Le(null),requestAnimationFrame($)}function ye(){let e=Date.now(),r=(e-q)/1e3;return r=Math.min(r,.016666),q=e,r}function j(){let e=R(c.clientWidth),r=R(c.clientHeight);return c.width!==e||c.height!==r?(c.width=e,c.height=r,!0):!1}function Se(e){C+=e*f.colorUpdateSpeed,C>=1&&(C=Xe(C,0,1),S.forEach(r=>{r.color=te(Y())}))}function Fe(){S.forEach(e=>{e.moved&&(e.moved=!1,Ae(e))})}function De(e){t.disable(t.BLEND),M.bind(),t.uniform2f(M.uniforms.texelSize,s.texelSizeX,s.texelSizeY),t.uniform1i(M.uniforms.uVelocity,s.read.attach(0)),p(G),_.bind(),t.uniform2f(_.uniforms.texelSize,s.texelSizeX,s.texelSizeY),t.uniform1i(_.uniforms.uVelocity,s.read.attach(0)),t.uniform1i(_.uniforms.uCurl,G.attach(1)),t.uniform1f(_.uniforms.curl,f.curl),t.uniform1f(_.uniforms.dt,e),p(s.write),s.swap(),z.bind(),t.uniform2f(z.uniforms.texelSize,s.texelSizeX,s.texelSizeY),t.uniform1i(z.uniforms.uVelocity,s.read.attach(0)),p(P),X.bind(),t.uniform1i(X.uniforms.uTexture,D.read.attach(0)),t.uniform1f(X.uniforms.value,f.pressure),p(D.write),D.swap(),B.bind(),t.uniform2f(B.uniforms.texelSize,s.texelSizeX,s.texelSizeY),t.uniform1i(B.uniforms.uDivergence,P.attach(0));for(let i=0;i<f.pressureIteration;i++)t.uniform1i(B.uniforms.uPressure,D.read.attach(1)),p(D.write),D.swap();w.bind(),t.uniform2f(w.uniforms.texelSize,s.texelSizeX,s.texelSizeY),t.uniform1i(w.uniforms.uPressure,D.read.attach(0)),t.uniform1i(w.uniforms.uVelocity,s.read.attach(1)),p(s.write),s.swap(),g.bind(),t.uniform2f(g.uniforms.texelSize,s.texelSizeX,s.texelSizeY),T.supportLinearFiltering||t.uniform2f(g.uniforms.dyeTexelSize,s.texelSizeX,s.texelSizeY);let r=s.read.attach(0);t.uniform1i(g.uniforms.uVelocity,r),t.uniform1i(g.uniforms.uSource,r),t.uniform1f(g.uniforms.dt,e),t.uniform1f(g.uniforms.dissipation,f.velocityDissipation),p(s.write),s.swap(),T.supportLinearFiltering||t.uniform2f(g.uniforms.dyeTexelSize,h.texelSizeX,h.texelSizeY),t.uniform1i(g.uniforms.uVelocity,s.read.attach(0)),t.uniform1i(g.uniforms.uSource,h.read.attach(1)),t.uniform1f(g.uniforms.dissipation,f.densityDissipation),p(h.write),h.swap()}function Le(e){t.blendFunc(t.ONE,t.ONE_MINUS_SRC_ALPHA),t.enable(t.BLEND),_e(e)}function _e(e){let r=e==null?t.drawingBufferWidth:e.width,i=e==null?t.drawingBufferHeight:e.height;U.bind(),f.shading&&t.uniform2f(U.uniforms.texelSize,1/r,1/i),t.uniform1i(U.uniforms.uTexture,h.read.attach(0)),p(e)}function Ae(e){let r=e.deltaX*f.splatForce,i=e.deltaY*f.splatForce,o=Pe(e.color);Q(e.texcoordX,e.texcoordY,r,i,o)}function J(e){let r=Y();r.r*=10,r.g*=10,r.b*=10;let i=10*(Math.random()-.5),o=30*(Math.random()-.5);Q(e.texcoordX,e.texcoordY,i,o,r)}function Q(e,r,i,o,n){L.bind(),t.uniform1i(L.uniforms.uTarget,s.read.attach(0)),t.uniform1f(L.uniforms.aspectRatio,c.width/c.height),t.uniform2f(L.uniforms.point,e,r),t.uniform3f(L.uniforms.color,i,o,0),t.uniform1f(L.uniforms.radius,Be(f.splatRadius/100)),p(s.write),s.swap(),t.uniform1i(L.uniforms.uTarget,h.read.attach(0)),t.uniform3f(L.uniforms.color,n.r,n.g,n.b),p(h.write),h.swap()}function Be(e){let r=c.width/c.height;return r>1&&(e*=r),e}window.addEventListener("mousedown",e=>{let r=S[0],i=c.getBoundingClientRect(),o=R(e.clientX-i.left),n=R(e.clientY-i.top);Z(r,-1,o,n),J(r)}),window.addEventListener("mousemove",e=>{let r=S[0],i=c.getBoundingClientRect(),o=R(e.clientX-i.left),n=R(e.clientY-i.top),u=r.color;ee(r,o,n,u)}),window.addEventListener("touchstart",e=>{let r=e.targetTouches,i=c.getBoundingClientRect(),o=S[0];for(let n=0;n<r.length;n++){let u=R(r[n].clientX-i.left),a=R(r[n].clientY-i.top);Z(o,r[n].identifier,u,a),J(o)}}),window.addEventListener("touchmove",e=>{e.preventDefault();let r=e.targetTouches,i=c.getBoundingClientRect(),o=S[0];for(let n=0;n<r.length;n++){let u=R(r[n].clientX-i.left),a=R(r[n].clientY-i.top);ee(o,u,a,o.color)}},{passive:!1}),window.addEventListener("touchend",e=>{let r=e.changedTouches,i=S[0];for(let o=0;o<r.length;o++)we(i)});function Z(e,r,i,o){e.id=r,e.down=!0,e.moved=!1,e.texcoordX=i/c.width,e.texcoordY=1-o/c.height,e.prevTexcoordX=e.texcoordX,e.prevTexcoordY=e.texcoordY,e.deltaX=0,e.deltaY=0,e.color=te(Y())}function ee(e,r,i,o){e.prevTexcoordX=e.texcoordX,e.prevTexcoordY=e.texcoordY,e.texcoordX=r/c.width,e.texcoordY=1-i/c.height,e.deltaX=Ue(e.texcoordX-e.prevTexcoordX),e.deltaY=Ce(e.texcoordY-e.prevTexcoordY),e.moved=Math.abs(e.deltaX)>0||Math.abs(e.deltaY)>0,e.color=o}function we(e){e.down=!1}function Ue(e){let r=c.width/c.height;return r<1&&(e*=r),e}function Ce(e){let r=c.width/c.height;return r>1&&(e/=r),e}function Y(){let e=Ge(Math.random(),1,1);return e.r*=.15,e.g*=.15,e.b*=.15,e}function te(e){return[e.r,e.g,e.b]}function Pe(e){return{r:e[0],g:e[1],b:e[2]}}function Ge(e,r,i){let o=0,n=0,u=0,a=Math.floor(e*6),m=e*6-a,d=i*(1-r),y=i*(1-m*r),l=i*(1-(1-m)*r);switch(a%6){case 0:o=i,n=l,u=d;break;case 1:o=y,n=i,u=d;break;case 2:o=d,n=i,u=l;break;case 3:o=d,n=y,u=i;break;case 4:o=l,n=d,u=i;break;case 5:o=i,n=d,u=y;break}return{r:o,g:n,b:u}}function Xe(e,r,i){let o=i-r;return o===0?r:(e-r)%o+r}function re(e){let r=t.drawingBufferWidth/t.drawingBufferHeight;r<1&&(r=1/r);let i=Math.round(e),o=Math.round(e*r);return t.drawingBufferWidth>t.drawingBufferHeight?{width:o,height:i}:{width:i,height:o}}function R(e){let r=window.devicePixelRatio||1;return Math.floor(e*r)}function ze(e){if(e.length===0)return 0;let r=0;for(let i=0;i<e.length;i++)r=(r<<5)-r+e.charCodeAt(i),r|=0;return r}$()};var Ie={simResolution:128,dyeResolution:1440,captureResolution:512,densityDissipation:3.5,velocityDissipation:2,pressure:.1,pressureIteration:20,curl:10,splatRadius:.5,splatForce:6e3,shading:!0,colorUpdateSpeed:10,paused:!1,backColor:{r:0,g:0,b:0},transparent:!0,id:"smokey-fluid-canvas"},qe= exports.SmokeyFluidCursor =({config:f})=>{let c=N(N({},Ie),f);return _react2.default.useEffect(()=>{if(document){let v=document.createElement("style");v.textContent=`
|
|
283
|
+
#${c.id} {
|
|
284
|
+
position: fixed;
|
|
285
|
+
top: 0;
|
|
286
|
+
left: 0;
|
|
287
|
+
width: 100%;
|
|
288
|
+
height: 100%;
|
|
289
|
+
pointer-events: none;
|
|
290
|
+
z-index: -9999;
|
|
291
|
+
}
|
|
292
|
+
`,document.head.appendChild(v),ne(c)}},[]),_react2.default.createElement("canvas",{id:c.id})};exports.SmokeyFluidCursor = qe;
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
var Me=Object.defineProperty;var ie=Object.getOwnPropertySymbols;var Oe=Object.prototype.hasOwnProperty,Ye=Object.prototype.propertyIsEnumerable;var oe=(f,c,v)=>c in f?Me(f,c,{enumerable:!0,configurable:!0,writable:!0,value:v}):f[c]=v,N=(f,c)=>{for(var v in c||(c={}))Oe.call(c,v)&&oe(f,v,c[v]);if(ie)for(var v of ie(c))Ye.call(c,v)&&oe(f,v,c[v]);return f};import ae from"react";var ne=f=>{let c=document.getElementById(f.id);if(!c)return;let v=document.createElement("style");v.textContent=`
|
|
3
|
+
#${f.id} {
|
|
4
|
+
position: fixed;
|
|
5
|
+
top: 0;
|
|
6
|
+
left: 0;
|
|
7
|
+
width: 100%;
|
|
8
|
+
height: 100%;
|
|
9
|
+
pointer-events: none;
|
|
10
|
+
z-index: -9999;
|
|
11
|
+
}
|
|
12
|
+
`,document.head.appendChild(v),j();class ue{constructor(){this.id=-1;this.texcoordX=0;this.texcoordY=0;this.prevTexcoordX=0;this.prevTexcoordY=0;this.deltaX=0;this.deltaY=0;this.down=!1;this.moved=!1;this.color=[30,0,300]}}let S=[];S.push(new ue);let{gl:t,ext:T}=ce(c);T.supportLinearFiltering||(f.dyeResolution=512,f.shading=!1);function ce(e){let r={alpha:!0,depth:!1,stencil:!1,antialias:!1,preserveDrawingBuffer:!1},i=e.getContext("webgl2",r),o=!!i;if(o||(i=e.getContext("webgl",r)||e.getContext("experimental-webgl",r)),!i)throw new Error("WebGL not supported");let n=null,u,a;if(o){let l=i;l.getExtension("EXT_color_buffer_float"),u=!!l.getExtension("OES_texture_float_linear"),a=l.HALF_FLOAT}else{let l=i;if(n=l.getExtension("OES_texture_half_float"),u=!!l.getExtension("OES_texture_half_float_linear"),!n)throw new Error("OES_texture_half_float not supported on WebGL1");a=n.HALF_FLOAT_OES}i.clearColor(0,0,0,1);let m=null,d=null,y=null;if(o){let l=i;m=F(l,l.RGBA16F,l.RGBA,a),d=F(l,l.RG16F,l.RG,a),y=F(l,l.R16F,l.RED,a)}else{let l=i;m=F(l,l.RGBA,l.RGBA,a),d=F(l,l.RGBA,l.RGBA,a),y=F(l,l.RGBA,l.RGBA,a)}return{gl:i,ext:{formatRGBA:m,formatRG:d,formatR:y,halfFloatTexType:a,supportLinearFiltering:u,isWebGL2:o}}}function F(e,r,i,o){if(!se(e,r,i,o)){if(e.RGBA16F!==void 0){let n=e;switch(r){case n.R16F:return F(n,n.RG16F,n.RG,o);case n.RG16F:return F(n,n.RGBA16F,n.RGBA,o);default:return null}}return null}return{internalFormat:r,format:i}}function se(e,r,i,o){let n=e.createTexture();if(!n)return!1;e.bindTexture(e.TEXTURE_2D,n),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MIN_FILTER,e.NEAREST),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MAG_FILTER,e.NEAREST),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_S,e.CLAMP_TO_EDGE),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_T,e.CLAMP_TO_EDGE),e.texImage2D(e.TEXTURE_2D,0,r,4,4,0,i,o,null);let u=e.createFramebuffer();if(!u)return!1;e.bindFramebuffer(e.FRAMEBUFFER,u),e.framebufferTexture2D(e.FRAMEBUFFER,e.COLOR_ATTACHMENT0,e.TEXTURE_2D,n,0);let a=e.checkFramebufferStatus(e.FRAMEBUFFER);return e.bindFramebuffer(e.FRAMEBUFFER,null),e.deleteFramebuffer(u),e.deleteTexture(n),a===e.FRAMEBUFFER_COMPLETE}class le{constructor(r,i){this.vertexShader=r,this.fragmentShaderSource=i,this.programs={},this.activeProgram=null,this.uniforms={}}setKeywords(r){let i=0;for(let n=0;n<r.length;n++)i+=ze(r[n]);let o=this.programs[i];if(!o){let n=x(t,t.FRAGMENT_SHADER,V(this.fragmentShaderSource,r));o=I(t,this.vertexShader,n),this.programs[i]=o}o!==this.activeProgram&&(this.uniforms=W(t,o),this.activeProgram=o)}bind(){this.activeProgram&&t.useProgram(this.activeProgram)}}class E{constructor(r,i){this.program=I(t,r,i),this.uniforms=W(t,this.program)}bind(){this.program&&t.useProgram(this.program)}}function I(e,r,i){let o=e.createProgram();return e.attachShader(o,r),e.attachShader(o,i),e.bindAttribLocation(o,0,"aPosition"),e.linkProgram(o),e.getProgramParameter(o,e.LINK_STATUS)||console.trace(e.getProgramInfoLog(o)),o}function W(e,r){let i={},o=e.getProgramParameter(r,e.ACTIVE_UNIFORMS);for(let n=0;n<o;n++){let u=e.getActiveUniform(r,n);if(!u)continue;let a=u.name,m=e.getUniformLocation(r,a);m&&(i[a]=m)}return i}function x(e,r,i){let o=e.createShader(r);return e.shaderSource(o,i),e.compileShader(o),e.getShaderParameter(o,e.COMPILE_STATUS)||console.trace(e.getShaderInfoLog(o)),o}function V(e,r){if(!r||r.length===0)return e;let i="";return r.forEach(o=>{i+="#define "+o+`
|
|
13
|
+
`}),i+e}let b=x(t,t.VERTEX_SHADER,`
|
|
14
|
+
precision highp float;
|
|
15
|
+
|
|
16
|
+
attribute vec2 aPosition;
|
|
17
|
+
varying vec2 vUv;
|
|
18
|
+
varying vec2 vL;
|
|
19
|
+
varying vec2 vR;
|
|
20
|
+
varying vec2 vT;
|
|
21
|
+
varying vec2 vB;
|
|
22
|
+
uniform vec2 texelSize;
|
|
23
|
+
|
|
24
|
+
void main () {
|
|
25
|
+
vUv = aPosition * 0.5 + 0.5;
|
|
26
|
+
vL = vUv - vec2(texelSize.x, 0.0);
|
|
27
|
+
vR = vUv + vec2(texelSize.x, 0.0);
|
|
28
|
+
vT = vUv + vec2(0.0, texelSize.y);
|
|
29
|
+
vB = vUv - vec2(0.0, texelSize.y);
|
|
30
|
+
gl_Position = vec4(aPosition, 0.0, 1.0);
|
|
31
|
+
}
|
|
32
|
+
`),fe=x(t,t.FRAGMENT_SHADER,`
|
|
33
|
+
precision mediump float;
|
|
34
|
+
precision mediump sampler2D;
|
|
35
|
+
|
|
36
|
+
varying highp vec2 vUv;
|
|
37
|
+
uniform sampler2D uTexture;
|
|
38
|
+
|
|
39
|
+
void main () {
|
|
40
|
+
gl_FragColor = texture2D(uTexture, vUv);
|
|
41
|
+
}
|
|
42
|
+
`),me=x(t,t.FRAGMENT_SHADER,`
|
|
43
|
+
precision mediump float;
|
|
44
|
+
precision mediump sampler2D;
|
|
45
|
+
|
|
46
|
+
varying highp vec2 vUv;
|
|
47
|
+
uniform sampler2D uTexture;
|
|
48
|
+
uniform float value;
|
|
49
|
+
|
|
50
|
+
void main () {
|
|
51
|
+
gl_FragColor = value * texture2D(uTexture, vUv);
|
|
52
|
+
}
|
|
53
|
+
`),de=`
|
|
54
|
+
precision highp float;
|
|
55
|
+
precision highp sampler2D;
|
|
56
|
+
|
|
57
|
+
varying vec2 vUv;
|
|
58
|
+
varying vec2 vL;
|
|
59
|
+
varying vec2 vR;
|
|
60
|
+
varying vec2 vT;
|
|
61
|
+
varying vec2 vB;
|
|
62
|
+
uniform sampler2D uTexture;
|
|
63
|
+
uniform vec2 texelSize;
|
|
64
|
+
|
|
65
|
+
void main () {
|
|
66
|
+
vec3 c = texture2D(uTexture, vUv).rgb;
|
|
67
|
+
|
|
68
|
+
#ifdef shading
|
|
69
|
+
// Calculate normal from neighboring pixels for lighting
|
|
70
|
+
vec3 lc = texture2D(uTexture, vL).rgb;
|
|
71
|
+
vec3 rc = texture2D(uTexture, vR).rgb;
|
|
72
|
+
vec3 tc = texture2D(uTexture, vT).rgb;
|
|
73
|
+
vec3 bc = texture2D(uTexture, vB).rgb;
|
|
74
|
+
|
|
75
|
+
float dx = length(rc) - length(lc);
|
|
76
|
+
float dy = length(tc) - length(bc);
|
|
77
|
+
|
|
78
|
+
vec3 n = normalize(vec3(dx, dy, length(texelSize)));
|
|
79
|
+
vec3 l = vec3(0.0, 0.0, 1.0);
|
|
80
|
+
|
|
81
|
+
// Apply diffuse lighting
|
|
82
|
+
float diffuse = clamp(dot(n, l) + 0.7, 0.7, 1.0);
|
|
83
|
+
c *= diffuse;
|
|
84
|
+
#endif
|
|
85
|
+
|
|
86
|
+
// Use brightest channel for alpha
|
|
87
|
+
float a = max(c.r, max(c.g, c.b));
|
|
88
|
+
gl_FragColor = vec4(c, a);
|
|
89
|
+
}
|
|
90
|
+
`,ve=x(t,t.FRAGMENT_SHADER,`
|
|
91
|
+
precision highp float;
|
|
92
|
+
precision highp sampler2D;
|
|
93
|
+
|
|
94
|
+
varying vec2 vUv;
|
|
95
|
+
uniform sampler2D uTarget;
|
|
96
|
+
uniform float aspectRatio;
|
|
97
|
+
uniform vec3 color;
|
|
98
|
+
uniform vec2 point;
|
|
99
|
+
uniform float radius;
|
|
100
|
+
|
|
101
|
+
void main () {
|
|
102
|
+
// Calculate Gaussian splat
|
|
103
|
+
vec2 p = vUv - point.xy;
|
|
104
|
+
p.x *= aspectRatio;
|
|
105
|
+
vec3 splat = exp(-dot(p, p) / radius) * color;
|
|
106
|
+
vec3 base = texture2D(uTarget, vUv).xyz;
|
|
107
|
+
gl_FragColor = vec4(base + splat, 1.0);
|
|
108
|
+
}
|
|
109
|
+
`),he=x(t,t.FRAGMENT_SHADER,V(`
|
|
110
|
+
precision highp float;
|
|
111
|
+
precision highp sampler2D;
|
|
112
|
+
|
|
113
|
+
varying vec2 vUv;
|
|
114
|
+
uniform sampler2D uVelocity;
|
|
115
|
+
uniform sampler2D uSource;
|
|
116
|
+
uniform vec2 texelSize;
|
|
117
|
+
uniform vec2 dyeTexelSize;
|
|
118
|
+
uniform float dt;
|
|
119
|
+
uniform float dissipation;
|
|
120
|
+
|
|
121
|
+
// Manual bilinear filtering for WebGL1 without linear filtering support
|
|
122
|
+
vec4 bilerp (sampler2D sam, vec2 uv, vec2 tsize) {
|
|
123
|
+
vec2 st = uv / tsize - 0.5;
|
|
124
|
+
|
|
125
|
+
vec2 iuv = floor(st);
|
|
126
|
+
vec2 fuv = fract(st);
|
|
127
|
+
|
|
128
|
+
vec4 a = texture2D(sam, (iuv + vec2(0.5, 0.5)) * tsize);
|
|
129
|
+
vec4 b = texture2D(sam, (iuv + vec2(1.5, 0.5)) * tsize);
|
|
130
|
+
vec4 c = texture2D(sam, (iuv + vec2(0.5, 1.5)) * tsize);
|
|
131
|
+
vec4 d = texture2D(sam, (iuv + vec2(1.5, 1.5)) * tsize);
|
|
132
|
+
|
|
133
|
+
return mix(mix(a, b, fuv.x), mix(c, d, fuv.x), fuv.y);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
void main () {
|
|
137
|
+
#ifdef MANUAL_FILTERING
|
|
138
|
+
// Manual filtering for devices without linear filtering
|
|
139
|
+
vec2 coord = vUv - dt * bilerp(uVelocity, vUv, texelSize).xy * texelSize;
|
|
140
|
+
vec4 result = bilerp(uSource, coord, dyeTexelSize);
|
|
141
|
+
#else
|
|
142
|
+
// Standard texture lookup with linear filtering
|
|
143
|
+
vec2 coord = vUv - dt * texture2D(uVelocity, vUv).xy * texelSize;
|
|
144
|
+
vec4 result = texture2D(uSource, coord);
|
|
145
|
+
#endif
|
|
146
|
+
// Apply dissipation (fade over time)
|
|
147
|
+
float decay = 1.0 + dissipation * dt;
|
|
148
|
+
gl_FragColor = result / decay;
|
|
149
|
+
}
|
|
150
|
+
`,T.supportLinearFiltering?null:["MANUAL_FILTERING"])),pe=x(t,t.FRAGMENT_SHADER,`
|
|
151
|
+
precision mediump float;
|
|
152
|
+
precision mediump sampler2D;
|
|
153
|
+
|
|
154
|
+
varying highp vec2 vUv;
|
|
155
|
+
varying highp vec2 vL;
|
|
156
|
+
varying highp vec2 vR;
|
|
157
|
+
varying highp vec2 vT;
|
|
158
|
+
varying highp vec2 vB;
|
|
159
|
+
uniform sampler2D uVelocity;
|
|
160
|
+
|
|
161
|
+
void main () {
|
|
162
|
+
// Sample neighboring velocities
|
|
163
|
+
float L = texture2D(uVelocity, vL).x;
|
|
164
|
+
float R = texture2D(uVelocity, vR).x;
|
|
165
|
+
float T = texture2D(uVelocity, vT).y;
|
|
166
|
+
float B = texture2D(uVelocity, vB).y;
|
|
167
|
+
|
|
168
|
+
vec2 C = texture2D(uVelocity, vUv).xy;
|
|
169
|
+
|
|
170
|
+
// Boundary conditions
|
|
171
|
+
if (vL.x < 0.0) { L = -C.x; }
|
|
172
|
+
if (vR.x > 1.0) { R = -C.x; }
|
|
173
|
+
if (vT.y > 1.0) { T = -C.y; }
|
|
174
|
+
if (vB.y < 0.0) { B = -C.y; }
|
|
175
|
+
|
|
176
|
+
// Calculate divergence
|
|
177
|
+
float div = 0.5 * (R - L + T - B);
|
|
178
|
+
gl_FragColor = vec4(div, 0.0, 0.0, 1.0);
|
|
179
|
+
}
|
|
180
|
+
`),xe=x(t,t.FRAGMENT_SHADER,`
|
|
181
|
+
precision mediump float;
|
|
182
|
+
precision mediump sampler2D;
|
|
183
|
+
|
|
184
|
+
varying highp vec2 vUv;
|
|
185
|
+
varying highp vec2 vL;
|
|
186
|
+
varying highp vec2 vR;
|
|
187
|
+
varying highp vec2 vT;
|
|
188
|
+
varying highp vec2 vB;
|
|
189
|
+
uniform sampler2D uVelocity;
|
|
190
|
+
|
|
191
|
+
void main () {
|
|
192
|
+
// Sample velocity components for curl calculation
|
|
193
|
+
float L = texture2D(uVelocity, vL).y;
|
|
194
|
+
float R = texture2D(uVelocity, vR).y;
|
|
195
|
+
float T = texture2D(uVelocity, vT).x;
|
|
196
|
+
float B = texture2D(uVelocity, vB).x;
|
|
197
|
+
float vorticity = R - L - T + B;
|
|
198
|
+
gl_FragColor = vec4(0.5 * vorticity, 0.0, 0.0, 1.0);
|
|
199
|
+
}
|
|
200
|
+
`),ge=x(t,t.FRAGMENT_SHADER,`
|
|
201
|
+
precision highp float;
|
|
202
|
+
precision highp sampler2D;
|
|
203
|
+
|
|
204
|
+
varying vec2 vUv;
|
|
205
|
+
varying vec2 vL;
|
|
206
|
+
varying vec2 vR;
|
|
207
|
+
varying vec2 vT;
|
|
208
|
+
varying vec2 vB;
|
|
209
|
+
uniform sampler2D uVelocity;
|
|
210
|
+
uniform sampler2D uCurl;
|
|
211
|
+
uniform float curl;
|
|
212
|
+
uniform float dt;
|
|
213
|
+
|
|
214
|
+
void main () {
|
|
215
|
+
// Sample curl from neighbors
|
|
216
|
+
float L = texture2D(uCurl, vL).x;
|
|
217
|
+
float R = texture2D(uCurl, vR).x;
|
|
218
|
+
float T = texture2D(uCurl, vT).x;
|
|
219
|
+
float B = texture2D(uCurl, vB).x;
|
|
220
|
+
float C = texture2D(uCurl, vUv).x;
|
|
221
|
+
|
|
222
|
+
// Calculate vorticity force
|
|
223
|
+
vec2 force = 0.5 * vec2(abs(T) - abs(B), abs(R) - abs(L));
|
|
224
|
+
force /= length(force) + 0.0001; // Normalize
|
|
225
|
+
force *= curl * C; // Scale by curl strength
|
|
226
|
+
force.y *= -1.0; // Adjust coordinate system
|
|
227
|
+
|
|
228
|
+
// Apply force to velocity
|
|
229
|
+
vec2 velocity = texture2D(uVelocity, vUv).xy;
|
|
230
|
+
velocity += force * dt;
|
|
231
|
+
velocity = min(max(velocity, -1000.0), 1000.0); // Clamp to prevent explosion
|
|
232
|
+
gl_FragColor = vec4(velocity, 0.0, 1.0);
|
|
233
|
+
}
|
|
234
|
+
`),be=x(t,t.FRAGMENT_SHADER,`
|
|
235
|
+
precision mediump float;
|
|
236
|
+
precision mediump sampler2D;
|
|
237
|
+
|
|
238
|
+
varying highp vec2 vUv;
|
|
239
|
+
varying highp vec2 vL;
|
|
240
|
+
varying highp vec2 vR;
|
|
241
|
+
varying highp vec2 vT;
|
|
242
|
+
varying highp vec2 vB;
|
|
243
|
+
uniform sampler2D uPressure;
|
|
244
|
+
uniform sampler2D uDivergence;
|
|
245
|
+
|
|
246
|
+
void main () {
|
|
247
|
+
// Sample pressure from neighbors
|
|
248
|
+
float L = texture2D(uPressure, vL).x;
|
|
249
|
+
float R = texture2D(uPressure, vR).x;
|
|
250
|
+
float T = texture2D(uPressure, vT).x;
|
|
251
|
+
float B = texture2D(uPressure, vB).x;
|
|
252
|
+
float C = texture2D(uPressure, vUv).x;
|
|
253
|
+
float divergence = texture2D(uDivergence, vUv).x;
|
|
254
|
+
|
|
255
|
+
// Jacobi iteration for pressure solve
|
|
256
|
+
float pressure = (L + R + B + T - divergence) * 0.25;
|
|
257
|
+
gl_FragColor = vec4(pressure, 0.0, 0.0, 1.0);
|
|
258
|
+
}
|
|
259
|
+
`),Re=x(t,t.FRAGMENT_SHADER,`
|
|
260
|
+
precision mediump float;
|
|
261
|
+
precision mediump sampler2D;
|
|
262
|
+
|
|
263
|
+
varying highp vec2 vUv;
|
|
264
|
+
varying highp vec2 vL;
|
|
265
|
+
varying highp vec2 vR;
|
|
266
|
+
varying highp vec2 vT;
|
|
267
|
+
varying highp vec2 vB;
|
|
268
|
+
uniform sampler2D uPressure;
|
|
269
|
+
uniform sampler2D uVelocity;
|
|
270
|
+
|
|
271
|
+
void main () {
|
|
272
|
+
// Calculate pressure gradient
|
|
273
|
+
float L = texture2D(uPressure, vL).x;
|
|
274
|
+
float R = texture2D(uPressure, vR).x;
|
|
275
|
+
float T = texture2D(uPressure, vT).x;
|
|
276
|
+
float B = texture2D(uPressure, vB).x;
|
|
277
|
+
vec2 velocity = texture2D(uVelocity, vUv).xy;
|
|
278
|
+
|
|
279
|
+
// Subtract gradient to make velocity divergence-free
|
|
280
|
+
velocity.xy -= vec2(R - L, T - B);
|
|
281
|
+
gl_FragColor = vec4(velocity, 0.0, 1.0);
|
|
282
|
+
}
|
|
283
|
+
`),p=(()=>{let e=t.createBuffer();t.bindBuffer(t.ARRAY_BUFFER,e),t.bufferData(t.ARRAY_BUFFER,new Float32Array([-1,-1,-1,1,1,1,1,-1]),t.STATIC_DRAW);let r=t.createBuffer();return t.bindBuffer(t.ELEMENT_ARRAY_BUFFER,r),t.bufferData(t.ELEMENT_ARRAY_BUFFER,new Uint16Array([0,1,2,0,2,3]),t.STATIC_DRAW),t.vertexAttribPointer(0,2,t.FLOAT,!1,0,0),t.enableVertexAttribArray(0),(i,o=!1)=>{i==null?(t.viewport(0,0,t.drawingBufferWidth,t.drawingBufferHeight),t.bindFramebuffer(t.FRAMEBUFFER,null)):(t.viewport(0,0,i.width,i.height),t.bindFramebuffer(t.FRAMEBUFFER,i.fbo)),o&&(t.clearColor(0,0,0,1),t.clear(t.COLOR_BUFFER_BIT)),t.drawElements(t.TRIANGLES,6,t.UNSIGNED_SHORT,0)}})(),h,s,P,G,D,H=new E(b,fe),X=new E(b,me),L=new E(b,ve),g=new E(b,he),z=new E(b,pe),M=new E(b,xe),_=new E(b,ge),B=new E(b,be),w=new E(b,Re),U=new le(b,de);function k(){let e=re(f.simResolution),r=re(f.dyeResolution),i=T.halfFloatTexType,o=T.formatRGBA,n=T.formatRG,u=T.formatR,a=T.supportLinearFiltering?t.LINEAR:t.NEAREST;t.disable(t.BLEND),h==null?h=O(r.width,r.height,o.internalFormat,o.format,i,a):h=K(h,r.width,r.height,o.internalFormat,o.format,i,a),s==null?s=O(e.width,e.height,n.internalFormat,n.format,i,a):s=K(s,e.width,e.height,n.internalFormat,n.format,i,a),P=A(e.width,e.height,u.internalFormat,u.format,i,t.NEAREST),G=A(e.width,e.height,u.internalFormat,u.format,i,t.NEAREST),D=O(e.width,e.height,u.internalFormat,u.format,i,t.NEAREST)}function A(e,r,i,o,n,u){t.activeTexture(t.TEXTURE0);let a=t.createTexture();t.bindTexture(t.TEXTURE_2D,a),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_MIN_FILTER,u),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_MAG_FILTER,u),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_WRAP_S,t.CLAMP_TO_EDGE),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_WRAP_T,t.CLAMP_TO_EDGE),t.texImage2D(t.TEXTURE_2D,0,i,e,r,0,o,n,null);let m=t.createFramebuffer();t.bindFramebuffer(t.FRAMEBUFFER,m),t.framebufferTexture2D(t.FRAMEBUFFER,t.COLOR_ATTACHMENT0,t.TEXTURE_2D,a,0),t.viewport(0,0,e,r),t.clear(t.COLOR_BUFFER_BIT);let d=1/e,y=1/r;return{texture:a,fbo:m,width:e,height:r,texelSizeX:d,texelSizeY:y,attach(l){return t.activeTexture(t.TEXTURE0+l),t.bindTexture(t.TEXTURE_2D,a),l}}}function O(e,r,i,o,n,u){let a=A(e,r,i,o,n,u),m=A(e,r,i,o,n,u);return{width:e,height:r,texelSizeX:a.texelSizeX,texelSizeY:a.texelSizeY,get read(){return a},set read(d){a=d},get write(){return m},set write(d){m=d},swap(){let d=a;a=m,m=d}}}function Te(e,r,i,o,n,u,a){let m=A(r,i,o,n,u,a);return H.bind(),t.uniform1i(H.uniforms.uTexture,e.attach(0)),p(m),m}function K(e,r,i,o,n,u,a){return e.width===r&&e.height===i||(e.read=Te(e.read,r,i,o,n,u,a),e.write=A(r,i,o,n,u,a),e.width=r,e.height=i,e.texelSizeX=1/r,e.texelSizeY=1/i),e}function Ee(){let e=[];f.shading&&e.push("shading"),U.setKeywords(e)}Ee(),k();let q=Date.now(),C=0;function $(){let e=ye();j()&&k(),Se(e),Fe(),De(e),Le(null),requestAnimationFrame($)}function ye(){let e=Date.now(),r=(e-q)/1e3;return r=Math.min(r,.016666),q=e,r}function j(){let e=R(c.clientWidth),r=R(c.clientHeight);return c.width!==e||c.height!==r?(c.width=e,c.height=r,!0):!1}function Se(e){C+=e*f.colorUpdateSpeed,C>=1&&(C=Xe(C,0,1),S.forEach(r=>{r.color=te(Y())}))}function Fe(){S.forEach(e=>{e.moved&&(e.moved=!1,Ae(e))})}function De(e){t.disable(t.BLEND),M.bind(),t.uniform2f(M.uniforms.texelSize,s.texelSizeX,s.texelSizeY),t.uniform1i(M.uniforms.uVelocity,s.read.attach(0)),p(G),_.bind(),t.uniform2f(_.uniforms.texelSize,s.texelSizeX,s.texelSizeY),t.uniform1i(_.uniforms.uVelocity,s.read.attach(0)),t.uniform1i(_.uniforms.uCurl,G.attach(1)),t.uniform1f(_.uniforms.curl,f.curl),t.uniform1f(_.uniforms.dt,e),p(s.write),s.swap(),z.bind(),t.uniform2f(z.uniforms.texelSize,s.texelSizeX,s.texelSizeY),t.uniform1i(z.uniforms.uVelocity,s.read.attach(0)),p(P),X.bind(),t.uniform1i(X.uniforms.uTexture,D.read.attach(0)),t.uniform1f(X.uniforms.value,f.pressure),p(D.write),D.swap(),B.bind(),t.uniform2f(B.uniforms.texelSize,s.texelSizeX,s.texelSizeY),t.uniform1i(B.uniforms.uDivergence,P.attach(0));for(let i=0;i<f.pressureIteration;i++)t.uniform1i(B.uniforms.uPressure,D.read.attach(1)),p(D.write),D.swap();w.bind(),t.uniform2f(w.uniforms.texelSize,s.texelSizeX,s.texelSizeY),t.uniform1i(w.uniforms.uPressure,D.read.attach(0)),t.uniform1i(w.uniforms.uVelocity,s.read.attach(1)),p(s.write),s.swap(),g.bind(),t.uniform2f(g.uniforms.texelSize,s.texelSizeX,s.texelSizeY),T.supportLinearFiltering||t.uniform2f(g.uniforms.dyeTexelSize,s.texelSizeX,s.texelSizeY);let r=s.read.attach(0);t.uniform1i(g.uniforms.uVelocity,r),t.uniform1i(g.uniforms.uSource,r),t.uniform1f(g.uniforms.dt,e),t.uniform1f(g.uniforms.dissipation,f.velocityDissipation),p(s.write),s.swap(),T.supportLinearFiltering||t.uniform2f(g.uniforms.dyeTexelSize,h.texelSizeX,h.texelSizeY),t.uniform1i(g.uniforms.uVelocity,s.read.attach(0)),t.uniform1i(g.uniforms.uSource,h.read.attach(1)),t.uniform1f(g.uniforms.dissipation,f.densityDissipation),p(h.write),h.swap()}function Le(e){t.blendFunc(t.ONE,t.ONE_MINUS_SRC_ALPHA),t.enable(t.BLEND),_e(e)}function _e(e){let r=e==null?t.drawingBufferWidth:e.width,i=e==null?t.drawingBufferHeight:e.height;U.bind(),f.shading&&t.uniform2f(U.uniforms.texelSize,1/r,1/i),t.uniform1i(U.uniforms.uTexture,h.read.attach(0)),p(e)}function Ae(e){let r=e.deltaX*f.splatForce,i=e.deltaY*f.splatForce,o=Pe(e.color);Q(e.texcoordX,e.texcoordY,r,i,o)}function J(e){let r=Y();r.r*=10,r.g*=10,r.b*=10;let i=10*(Math.random()-.5),o=30*(Math.random()-.5);Q(e.texcoordX,e.texcoordY,i,o,r)}function Q(e,r,i,o,n){L.bind(),t.uniform1i(L.uniforms.uTarget,s.read.attach(0)),t.uniform1f(L.uniforms.aspectRatio,c.width/c.height),t.uniform2f(L.uniforms.point,e,r),t.uniform3f(L.uniforms.color,i,o,0),t.uniform1f(L.uniforms.radius,Be(f.splatRadius/100)),p(s.write),s.swap(),t.uniform1i(L.uniforms.uTarget,h.read.attach(0)),t.uniform3f(L.uniforms.color,n.r,n.g,n.b),p(h.write),h.swap()}function Be(e){let r=c.width/c.height;return r>1&&(e*=r),e}window.addEventListener("mousedown",e=>{let r=S[0],i=c.getBoundingClientRect(),o=R(e.clientX-i.left),n=R(e.clientY-i.top);Z(r,-1,o,n),J(r)}),window.addEventListener("mousemove",e=>{let r=S[0],i=c.getBoundingClientRect(),o=R(e.clientX-i.left),n=R(e.clientY-i.top),u=r.color;ee(r,o,n,u)}),window.addEventListener("touchstart",e=>{let r=e.targetTouches,i=c.getBoundingClientRect(),o=S[0];for(let n=0;n<r.length;n++){let u=R(r[n].clientX-i.left),a=R(r[n].clientY-i.top);Z(o,r[n].identifier,u,a),J(o)}}),window.addEventListener("touchmove",e=>{e.preventDefault();let r=e.targetTouches,i=c.getBoundingClientRect(),o=S[0];for(let n=0;n<r.length;n++){let u=R(r[n].clientX-i.left),a=R(r[n].clientY-i.top);ee(o,u,a,o.color)}},{passive:!1}),window.addEventListener("touchend",e=>{let r=e.changedTouches,i=S[0];for(let o=0;o<r.length;o++)we(i)});function Z(e,r,i,o){e.id=r,e.down=!0,e.moved=!1,e.texcoordX=i/c.width,e.texcoordY=1-o/c.height,e.prevTexcoordX=e.texcoordX,e.prevTexcoordY=e.texcoordY,e.deltaX=0,e.deltaY=0,e.color=te(Y())}function ee(e,r,i,o){e.prevTexcoordX=e.texcoordX,e.prevTexcoordY=e.texcoordY,e.texcoordX=r/c.width,e.texcoordY=1-i/c.height,e.deltaX=Ue(e.texcoordX-e.prevTexcoordX),e.deltaY=Ce(e.texcoordY-e.prevTexcoordY),e.moved=Math.abs(e.deltaX)>0||Math.abs(e.deltaY)>0,e.color=o}function we(e){e.down=!1}function Ue(e){let r=c.width/c.height;return r<1&&(e*=r),e}function Ce(e){let r=c.width/c.height;return r>1&&(e/=r),e}function Y(){let e=Ge(Math.random(),1,1);return e.r*=.15,e.g*=.15,e.b*=.15,e}function te(e){return[e.r,e.g,e.b]}function Pe(e){return{r:e[0],g:e[1],b:e[2]}}function Ge(e,r,i){let o=0,n=0,u=0,a=Math.floor(e*6),m=e*6-a,d=i*(1-r),y=i*(1-m*r),l=i*(1-(1-m)*r);switch(a%6){case 0:o=i,n=l,u=d;break;case 1:o=y,n=i,u=d;break;case 2:o=d,n=i,u=l;break;case 3:o=d,n=y,u=i;break;case 4:o=l,n=d,u=i;break;case 5:o=i,n=d,u=y;break}return{r:o,g:n,b:u}}function Xe(e,r,i){let o=i-r;return o===0?r:(e-r)%o+r}function re(e){let r=t.drawingBufferWidth/t.drawingBufferHeight;r<1&&(r=1/r);let i=Math.round(e),o=Math.round(e*r);return t.drawingBufferWidth>t.drawingBufferHeight?{width:o,height:i}:{width:i,height:o}}function R(e){let r=window.devicePixelRatio||1;return Math.floor(e*r)}function ze(e){if(e.length===0)return 0;let r=0;for(let i=0;i<e.length;i++)r=(r<<5)-r+e.charCodeAt(i),r|=0;return r}$()};var We={simResolution:128,dyeResolution:1440,captureResolution:512,densityDissipation:3.5,velocityDissipation:2,pressure:.1,pressureIteration:20,curl:10,splatRadius:.5,splatForce:6e3,shading:!0,colorUpdateSpeed:10,paused:!1,backColor:{r:0,g:0,b:0},transparent:!0,id:"smokey-fluid-canvas"},$e=({config:f})=>{let c=N(N({},We),f);return ae.useEffect(()=>{if(document){let v=document.createElement("style");v.textContent=`
|
|
284
|
+
#${c.id} {
|
|
285
|
+
position: fixed;
|
|
286
|
+
top: 0;
|
|
287
|
+
left: 0;
|
|
288
|
+
width: 100%;
|
|
289
|
+
height: 100%;
|
|
290
|
+
pointer-events: none;
|
|
291
|
+
z-index: -9999;
|
|
292
|
+
}
|
|
293
|
+
`,document.head.appendChild(v),ne(c)}},[]),ae.createElement("canvas",{id:c.id})};export{$e as SmokeyFluidCursor};
|
package/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "react-smokey-fluid-cursor",
|
|
3
|
+
"version": "1.0.2",
|
|
4
|
+
"description": "A customizable fluid cursor effect for React & Next.js Apps running on the web. Enhance user experience with smooth, interactive cursor animations. Perfect for modern web designs.",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "git+https://github.com/faraasat/react-smokey-fluid-cursor.git"
|
|
8
|
+
},
|
|
9
|
+
"main": "./dist/index.js",
|
|
10
|
+
"module": "./dist/index.mjs",
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"files": [
|
|
13
|
+
"dist"
|
|
14
|
+
],
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "tsup && rm -rf dist/*.d.mts",
|
|
17
|
+
"release": "standard-version"
|
|
18
|
+
},
|
|
19
|
+
"keywords": [
|
|
20
|
+
"react",
|
|
21
|
+
"nextjs",
|
|
22
|
+
"next.js",
|
|
23
|
+
"smokey cursor",
|
|
24
|
+
"fluid cursor",
|
|
25
|
+
"animated cursor",
|
|
26
|
+
"smokey fluid",
|
|
27
|
+
"javascript",
|
|
28
|
+
"typescript",
|
|
29
|
+
"animated smoke cursor",
|
|
30
|
+
"react cursor",
|
|
31
|
+
"cursor effects",
|
|
32
|
+
"custom cursor",
|
|
33
|
+
"react component",
|
|
34
|
+
"next.js cursor"
|
|
35
|
+
],
|
|
36
|
+
"author": "Farasat Ali <63093876+faraasat@users.noreply.github.com>",
|
|
37
|
+
"license": "MIT",
|
|
38
|
+
"peerDependencies": {
|
|
39
|
+
"react": ">=17.0.0",
|
|
40
|
+
"react-dom": ">=17.0.0"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"@types/node": "^24.0.15",
|
|
44
|
+
"@types/react": "^19.1.8",
|
|
45
|
+
"@types/react-dom": "^19.1.6",
|
|
46
|
+
"standard-version": "^9.5.0",
|
|
47
|
+
"tsup": "^8.5.0",
|
|
48
|
+
"typescript": "^5.0.0"
|
|
49
|
+
},
|
|
50
|
+
"publishConfig": {
|
|
51
|
+
"access": "public"
|
|
52
|
+
}
|
|
53
|
+
}
|