@fjandin/react-shader 0.0.1
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/dist/ReactShader.d.ts +3 -0
- package/dist/ReactShader.d.ts.map +1 -0
- package/dist/example/frontend.d.ts +9 -0
- package/dist/example/frontend.d.ts.map +1 -0
- package/dist/example/glsl/circles.glsl.d.ts +3 -0
- package/dist/example/glsl/circles.glsl.d.ts.map +1 -0
- package/dist/example/glsl/noise.glsl.d.ts +3 -0
- package/dist/example/glsl/noise.glsl.d.ts.map +1 -0
- package/dist/example/glsl/palette.glsl.d.ts +2 -0
- package/dist/example/glsl/palette.glsl.d.ts.map +1 -0
- package/dist/example/index.d.ts +2 -0
- package/dist/example/index.d.ts.map +1 -0
- package/dist/example/shader.d.ts +2 -0
- package/dist/example/shader.d.ts.map +1 -0
- package/dist/hooks/useWebGL.d.ts +22 -0
- package/dist/hooks/useWebGL.d.ts.map +1 -0
- package/dist/index.cjs +451 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +419 -0
- package/dist/types.d.ts +22 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/utils/shader.d.ts +6 -0
- package/dist/utils/shader.d.ts.map +1 -0
- package/dist/utils/uniforms.d.ts +8 -0
- package/dist/utils/uniforms.d.ts.map +1 -0
- package/package.json +60 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ReactShader.d.ts","sourceRoot":"","sources":["../src/ReactShader.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAQ,MAAM,SAAS,CAAA;AAerD,wBAAgB,WAAW,CAAC,EAC1B,SAAS,EACT,QAAQ,EACR,MAAuB,EACvB,QAAQ,EACR,KAAa,EACb,UAAkB,EAClB,SAAa,EACb,OAAO,GACR,EAAE,gBAAgB,2CAiHlB"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is the entry point for the React app, it sets up the root
|
|
3
|
+
* element and renders the App component to the DOM.
|
|
4
|
+
*
|
|
5
|
+
* It is included in `src/index.html`.
|
|
6
|
+
*/
|
|
7
|
+
import "./style.css";
|
|
8
|
+
export declare function App(): import("react/jsx-runtime").JSX.Element;
|
|
9
|
+
//# sourceMappingURL=frontend.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frontend.d.ts","sourceRoot":"","sources":["../../src/example/frontend.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,aAAa,CAAA;AAsBpB,wBAAgB,GAAG,4CAyBlB"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
declare const _default: "\nvec3 Circles(\n vec2 uv0,\n float iterations,\n float fractMultiplier,\n float time,\n float waveLength,\n float edgeBlur,\n float contrast\n) {\n vec3 col = vec3(0.0);\n vec2 uv = uv0;\n\n for (float i = 0.0; i < iterations; i++) {\n uv = fract(uv * fractMultiplier) - 0.5;\n\n float d = length(uv) * exp(-length(uv0));\n\n vec3 color = circlesPalette(length(uv0) + i * 0.4 + time * 0.4);\n\n d = sin(d * waveLength + time) / waveLength;\n d = abs(d);\n d = pow(edgeBlur / d, contrast);\n\n col += color * d;\n }\n return col;\n}\n";
|
|
2
|
+
export default _default;
|
|
3
|
+
//# sourceMappingURL=circles.glsl.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"circles.glsl.d.ts","sourceRoot":"","sources":["../../../src/example/glsl/circles.glsl.ts"],"names":[],"mappings":";AAAA,wBA4BC"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
declare const _default: "\n\nvec4 mod289(vec4 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; }\n\nvec3 mod289(vec3 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; }\n\nfloat mod289(float x) { return x - floor(x * (1.0 / 289.0)) * 289.0; }\n\nvec4 permute(vec4 x) { return mod289(((x*34.0)+10.0)*x); }\n\nfloat permute(float x) { return mod289(((x*34.0)+10.0)*x); }\n\nvec4 taylorInvSqrt(vec4 r) { return 1.79284291400159 - 0.85373472095314 * r; }\n\nfloat taylorInvSqrt(float r) { return 1.79284291400159 - 0.85373472095314 * r; }\n\nvec4 grad4(float j, vec4 ip) {\n const vec4 ones = vec4(1.0, 1.0, 1.0, -1.0);\n vec4 p,s;\n\n p.xyz = floor( fract (vec3(j) * ip.xyz) * 7.0) * ip.z - 1.0;\n p.w = 1.5 - dot(abs(p.xyz), ones.xyz);\n s = vec4(lessThan(p, vec4(0.0)));\n p.xyz = p.xyz + (s.xyz*2.0 - 1.0) * s.www;\n\n return p;\n}\n\n// (sqrt(5) - 1)/4 = F4, used once below\n#define F4 0.309016994374947451\n\nfloat SimplexNoise4D(vec4 v) {\n const vec4 C = vec4( 0.138196601125011, // (5 - sqrt(5))/20 G4\n 0.276393202250021, // 2 * G4\n 0.414589803375032, // 3 * G4\n -0.447213595499958); // -1 + 4 * G4\n\n // First corner\n vec4 i = floor(v + dot(v, vec4(F4)) );\n vec4 x0 = v - i + dot(i, C.xxxx);\n\n // Other corners\n\n // Rank sorting originally contributed by Bill Licea-Kane, AMD (formerly ATI)\n vec4 i0;\n vec3 isX = step( x0.yzw, x0.xxx );\n vec3 isYZ = step( x0.zww, x0.yyz );\n // i0.x = dot( isX, vec3( 1.0 ) );\n i0.x = isX.x + isX.y + isX.z;\n i0.yzw = 1.0 - isX;\n // i0.y += dot( isYZ.xy, vec2( 1.0 ) );\n i0.y += isYZ.x + isYZ.y;\n i0.zw += 1.0 - isYZ.xy;\n i0.z += isYZ.z;\n i0.w += 1.0 - isYZ.z;\n\n // i0 now contains the unique values 0,1,2,3 in each channel\n vec4 i3 = clamp( i0, 0.0, 1.0 );\n vec4 i2 = clamp( i0-1.0, 0.0, 1.0 );\n vec4 i1 = clamp( i0-2.0, 0.0, 1.0 );\n\n // x0 = x0 - 0.0 + 0.0 * C.xxxx\n // x1 = x0 - i1 + 1.0 * C.xxxx\n // x2 = x0 - i2 + 2.0 * C.xxxx\n // x3 = x0 - i3 + 3.0 * C.xxxx\n // x4 = x0 - 1.0 + 4.0 * C.xxxx\n vec4 x1 = x0 - i1 + C.xxxx;\n vec4 x2 = x0 - i2 + C.yyyy;\n vec4 x3 = x0 - i3 + C.zzzz;\n vec4 x4 = x0 + C.wwww;\n\n // Permutations\n i = mod289(i);\n float j0 = permute( permute( permute( permute(i.w) + i.z) + i.y) + i.x);\n vec4 j1 = permute( permute( permute( permute (\n i.w + vec4(i1.w, i2.w, i3.w, 1.0 ))\n + i.z + vec4(i1.z, i2.z, i3.z, 1.0 ))\n + i.y + vec4(i1.y, i2.y, i3.y, 1.0 ))\n + i.x + vec4(i1.x, i2.x, i3.x, 1.0 ));\n\n // Gradients: 7x7x6 points over a cube, mapped onto a 4-cross polytope\n // 7*7*6 = 294, which is close to the ring size 17*17 = 289.\n vec4 ip = vec4(1.0/294.0, 1.0/49.0, 1.0/7.0, 0.0) ;\n\n vec4 p0 = grad4(j0, ip);\n vec4 p1 = grad4(j1.x, ip);\n vec4 p2 = grad4(j1.y, ip);\n vec4 p3 = grad4(j1.z, ip);\n vec4 p4 = grad4(j1.w, ip);\n\n // Normalise gradients\n vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3)));\n p0 *= norm.x;\n p1 *= norm.y;\n p2 *= norm.z;\n p3 *= norm.w;\n p4 *= taylorInvSqrt(dot(p4,p4));\n\n // Mix contributions from the five corners\n vec3 m0 = max(0.6 - vec3(dot(x0,x0), dot(x1,x1), dot(x2,x2)), 0.0);\n vec2 m1 = max(0.6 - vec2(dot(x3,x3), dot(x4,x4) ), 0.0);\n m0 = m0 * m0;\n m1 = m1 * m1;\n return 49.0 * ( dot(m0*m0, vec3( dot( p0, x0 ), dot( p1, x1 ), dot( p2, x2 )))\n + dot(m1*m1, vec2( dot( p3, x3 ), dot( p4, x4 ) ) ) ) ;\n}\n\nfloat SimplexNoise(vec3 v) {\n const vec2 C = vec2(1.0/6.0, 1.0/3.0) ;\n const vec4 D = vec4(0.0, 0.5, 1.0, 2.0);\n\n // First corner\n vec3 i = floor(v + dot(v, C.yyy) );\n vec3 x0 = v - i + dot(i, C.xxx) ;\n\n // Other corners\n vec3 g = step(x0.yzx, x0.xyz);\n vec3 l = 1.0 - g;\n vec3 i1 = min( g.xyz, l.zxy );\n vec3 i2 = max( g.xyz, l.zxy );\n\n // x0 = x0 - 0.0 + 0.0 * C.xxx;\n // x1 = x0 - i1 + 1.0 * C.xxx;\n // x2 = x0 - i2 + 2.0 * C.xxx;\n // x3 = x0 - 1.0 + 3.0 * C.xxx;\n vec3 x1 = x0 - i1 + C.xxx;\n vec3 x2 = x0 - i2 + C.yyy; // 2.0*C.x = 1/3 = C.y\n vec3 x3 = x0 - D.yyy; // -1.0+3.0*C.x = -0.5 = -D.y\n\n // Permutations\n i = mod289(i);\n vec4 p = permute( permute( permute(\n i.z + vec4(0.0, i1.z, i2.z, 1.0 ))\n + i.y + vec4(0.0, i1.y, i2.y, 1.0 ))\n + i.x + vec4(0.0, i1.x, i2.x, 1.0 ));\n\n // Gradients: 7x7 points over a square, mapped onto an octahedron.\n // The ring size 17*17 = 289 is close to a multiple of 49 (49*6 = 294)\n float n_ = 0.142857142857; // 1.0/7.0\n vec3 ns = n_ * D.wyz - D.xzx;\n\n vec4 j = p - 49.0 * floor(p * ns.z * ns.z); // mod(p,7*7)\n\n vec4 x_ = floor(j * ns.z);\n vec4 y_ = floor(j - 7.0 * x_ ); // mod(j,N)\n\n vec4 x = x_ *ns.x + ns.yyyy;\n vec4 y = y_ *ns.x + ns.yyyy;\n vec4 h = 1.0 - abs(x) - abs(y);\n\n vec4 b0 = vec4( x.xy, y.xy );\n vec4 b1 = vec4( x.zw, y.zw );\n\n //vec4 s0 = vec4(lessThan(b0,0.0))*2.0 - 1.0;\n //vec4 s1 = vec4(lessThan(b1,0.0))*2.0 - 1.0;\n vec4 s0 = floor(b0)*2.0 + 1.0;\n vec4 s1 = floor(b1)*2.0 + 1.0;\n vec4 sh = -step(h, vec4(0.0));\n\n vec4 a0 = b0.xzyw + s0.xzyw*sh.xxyy ;\n vec4 a1 = b1.xzyw + s1.xzyw*sh.zzww ;\n\n vec3 p0 = vec3(a0.xy,h.x);\n vec3 p1 = vec3(a0.zw,h.y);\n vec3 p2 = vec3(a1.xy,h.z);\n vec3 p3 = vec3(a1.zw,h.w);\n\n // Normalise gradients\n vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3)));\n p0 *= norm.x;\n p1 *= norm.y;\n p2 *= norm.z;\n p3 *= norm.w;\n\n // Mix final noise value\n vec4 m = max(0.5 - vec4(dot(x0,x0), dot(x1,x1), dot(x2,x2), dot(x3,x3)), 0.0);\n m = m * m;\n return 105.0 * dot( m*m, vec4( dot(p0,x0), dot(p1,x1),\n dot(p2,x2), dot(p3,x3) ) );\n}\n";
|
|
2
|
+
export default _default;
|
|
3
|
+
//# sourceMappingURL=noise.glsl.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"noise.glsl.d.ts","sourceRoot":"","sources":["../../../src/example/glsl/noise.glsl.ts"],"names":[],"mappings":";AAWA,wBAoLC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"palette.glsl.d.ts","sourceRoot":"","sources":["../../../src/example/glsl/palette.glsl.ts"],"names":[],"mappings":"AAQA,eAAO,MAAM,uBAAuB,GAAI,MAAM,MAAM,EAAE,eAAe,MAAM,WAgB1E,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/example/index.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shader.d.ts","sourceRoot":"","sources":["../../src/example/shader.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,QAAQ,QAsFnB,CAAA"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { UniformValue } from "../types";
|
|
2
|
+
export interface FrameInfo {
|
|
3
|
+
deltaTime: number;
|
|
4
|
+
time: number;
|
|
5
|
+
resolution: [number, number];
|
|
6
|
+
mouse: [number, number];
|
|
7
|
+
}
|
|
8
|
+
interface UseWebGLOptions {
|
|
9
|
+
fragment: string;
|
|
10
|
+
vertex: string;
|
|
11
|
+
uniforms?: Record<string, UniformValue>;
|
|
12
|
+
onError?: (error: Error) => void;
|
|
13
|
+
onFrame?: (info: FrameInfo) => void;
|
|
14
|
+
running?: boolean;
|
|
15
|
+
timeScale?: number;
|
|
16
|
+
}
|
|
17
|
+
export declare function useWebGL(options: UseWebGLOptions): {
|
|
18
|
+
canvasRef: import("react").RefObject<HTMLCanvasElement>;
|
|
19
|
+
mouseRef: import("react").MutableRefObject<[number, number]>;
|
|
20
|
+
};
|
|
21
|
+
export {};
|
|
22
|
+
//# sourceMappingURL=useWebGL.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useWebGL.d.ts","sourceRoot":"","sources":["../../src/hooks/useWebGL.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AAI5C,MAAM,WAAW,SAAS;IACxB,SAAS,EAAE,MAAM,CAAA;IACjB,IAAI,EAAE,MAAM,CAAA;IACZ,UAAU,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC5B,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CACxB;AAED,UAAU,eAAe;IACvB,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAA;IACvC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAA;IAChC,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,IAAI,CAAA;IACnC,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAoDD,wBAAgB,QAAQ,CAAC,OAAO,EAAE,eAAe;;;EAiNhD"}
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,451 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
var __moduleCache = /* @__PURE__ */ new WeakMap;
|
|
6
|
+
var __toCommonJS = (from) => {
|
|
7
|
+
var entry = __moduleCache.get(from), desc;
|
|
8
|
+
if (entry)
|
|
9
|
+
return entry;
|
|
10
|
+
entry = __defProp({}, "__esModule", { value: true });
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function")
|
|
12
|
+
__getOwnPropNames(from).map((key) => !__hasOwnProp.call(entry, key) && __defProp(entry, key, {
|
|
13
|
+
get: () => from[key],
|
|
14
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
15
|
+
}));
|
|
16
|
+
__moduleCache.set(from, entry);
|
|
17
|
+
return entry;
|
|
18
|
+
};
|
|
19
|
+
var __export = (target, all) => {
|
|
20
|
+
for (var name in all)
|
|
21
|
+
__defProp(target, name, {
|
|
22
|
+
get: all[name],
|
|
23
|
+
enumerable: true,
|
|
24
|
+
configurable: true,
|
|
25
|
+
set: (newValue) => all[name] = () => newValue
|
|
26
|
+
});
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
// src/index.ts
|
|
30
|
+
var exports_src = {};
|
|
31
|
+
__export(exports_src, {
|
|
32
|
+
ReactShader: () => ReactShader
|
|
33
|
+
});
|
|
34
|
+
module.exports = __toCommonJS(exports_src);
|
|
35
|
+
|
|
36
|
+
// src/ReactShader.tsx
|
|
37
|
+
var import_react2 = require("react");
|
|
38
|
+
|
|
39
|
+
// src/hooks/useWebGL.ts
|
|
40
|
+
var import_react = require("react");
|
|
41
|
+
|
|
42
|
+
// src/utils/shader.ts
|
|
43
|
+
function compileShader(gl, type, source) {
|
|
44
|
+
const shader = gl.createShader(type);
|
|
45
|
+
if (!shader) {
|
|
46
|
+
throw new Error(`Failed to create shader of type ${type === gl.VERTEX_SHADER ? "VERTEX" : "FRAGMENT"}`);
|
|
47
|
+
}
|
|
48
|
+
gl.shaderSource(shader, source);
|
|
49
|
+
gl.compileShader(shader);
|
|
50
|
+
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
|
|
51
|
+
const info = gl.getShaderInfoLog(shader);
|
|
52
|
+
gl.deleteShader(shader);
|
|
53
|
+
const shaderType = type === gl.VERTEX_SHADER ? "Vertex" : "Fragment";
|
|
54
|
+
throw new Error(`${shaderType} shader compilation failed:
|
|
55
|
+
${info}`);
|
|
56
|
+
}
|
|
57
|
+
return shader;
|
|
58
|
+
}
|
|
59
|
+
function createProgram(gl, vertexShader, fragmentShader) {
|
|
60
|
+
const program = gl.createProgram();
|
|
61
|
+
if (!program) {
|
|
62
|
+
throw new Error("Failed to create WebGL program");
|
|
63
|
+
}
|
|
64
|
+
gl.attachShader(program, vertexShader);
|
|
65
|
+
gl.attachShader(program, fragmentShader);
|
|
66
|
+
gl.linkProgram(program);
|
|
67
|
+
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
|
|
68
|
+
const info = gl.getProgramInfoLog(program);
|
|
69
|
+
gl.deleteProgram(program);
|
|
70
|
+
throw new Error(`Program linking failed:
|
|
71
|
+
${info}`);
|
|
72
|
+
}
|
|
73
|
+
return program;
|
|
74
|
+
}
|
|
75
|
+
function createShaderProgram(gl, vertexSource, fragmentSource) {
|
|
76
|
+
const vertexShader = compileShader(gl, gl.VERTEX_SHADER, vertexSource);
|
|
77
|
+
const fragmentShader = compileShader(gl, gl.FRAGMENT_SHADER, fragmentSource);
|
|
78
|
+
try {
|
|
79
|
+
return createProgram(gl, vertexShader, fragmentShader);
|
|
80
|
+
} finally {
|
|
81
|
+
gl.deleteShader(vertexShader);
|
|
82
|
+
gl.deleteShader(fragmentShader);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// src/utils/uniforms.ts
|
|
87
|
+
function isVec2(value) {
|
|
88
|
+
return Array.isArray(value) && value.length === 2;
|
|
89
|
+
}
|
|
90
|
+
function isVec3(value) {
|
|
91
|
+
return Array.isArray(value) && value.length === 3;
|
|
92
|
+
}
|
|
93
|
+
function isVec4(value) {
|
|
94
|
+
return Array.isArray(value) && value.length === 4;
|
|
95
|
+
}
|
|
96
|
+
function setUniform(gl, location, value) {
|
|
97
|
+
if (location === null) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
if (typeof value === "number") {
|
|
101
|
+
gl.uniform1f(location, value);
|
|
102
|
+
} else if (isVec4(value)) {
|
|
103
|
+
gl.uniform4f(location, value[0], value[1], value[2], value[3]);
|
|
104
|
+
} else if (isVec3(value)) {
|
|
105
|
+
gl.uniform3f(location, value[0], value[1], value[2]);
|
|
106
|
+
} else if (isVec2(value)) {
|
|
107
|
+
gl.uniform2f(location, value[0], value[1]);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
function getUniformLocation(gl, program, name) {
|
|
111
|
+
return gl.getUniformLocation(program, name);
|
|
112
|
+
}
|
|
113
|
+
function setUniforms(gl, program, uniforms, locationCache) {
|
|
114
|
+
for (const [name, value] of Object.entries(uniforms)) {
|
|
115
|
+
let location = locationCache.get(name);
|
|
116
|
+
if (location === undefined) {
|
|
117
|
+
location = getUniformLocation(gl, program, name);
|
|
118
|
+
locationCache.set(name, location);
|
|
119
|
+
}
|
|
120
|
+
setUniform(gl, location, value);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
function createUniformLocationCache() {
|
|
124
|
+
return new Map;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// src/hooks/useWebGL.ts
|
|
128
|
+
function initializeWebGL(canvas, vertexSource, fragmentSource) {
|
|
129
|
+
const gl = canvas.getContext("webgl2") || canvas.getContext("webgl");
|
|
130
|
+
if (!gl) {
|
|
131
|
+
throw new Error("WebGL not supported");
|
|
132
|
+
}
|
|
133
|
+
const program = createShaderProgram(gl, vertexSource, fragmentSource);
|
|
134
|
+
const positionBuffer = gl.createBuffer();
|
|
135
|
+
if (!positionBuffer) {
|
|
136
|
+
throw new Error("Failed to create position buffer");
|
|
137
|
+
}
|
|
138
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
|
|
139
|
+
const positions = new Float32Array([-1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1]);
|
|
140
|
+
gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW);
|
|
141
|
+
const positionAttributeLocation = gl.getAttribLocation(program, "a_position");
|
|
142
|
+
if (positionAttributeLocation === -1) {
|
|
143
|
+
throw new Error('Vertex shader must have an "a_position" attribute');
|
|
144
|
+
}
|
|
145
|
+
return {
|
|
146
|
+
gl,
|
|
147
|
+
program,
|
|
148
|
+
positionBuffer,
|
|
149
|
+
positionAttributeLocation,
|
|
150
|
+
uniformLocationCache: createUniformLocationCache()
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
function cleanupWebGL(gl, state) {
|
|
154
|
+
gl.deleteBuffer(state.positionBuffer);
|
|
155
|
+
gl.deleteProgram(state.program);
|
|
156
|
+
}
|
|
157
|
+
function useWebGL(options) {
|
|
158
|
+
const canvasRef = import_react.useRef(null);
|
|
159
|
+
const stateRef = import_react.useRef(null);
|
|
160
|
+
const animationFrameRef = import_react.useRef(0);
|
|
161
|
+
const elapsedTimeRef = import_react.useRef(0);
|
|
162
|
+
const lastFrameTimeRef = import_react.useRef(0);
|
|
163
|
+
const mouseRef = import_react.useRef([0, 0]);
|
|
164
|
+
const mouseLeftDownRef = import_react.useRef(false);
|
|
165
|
+
const canvasRectRef = import_react.useRef(null);
|
|
166
|
+
const contextLostRef = import_react.useRef(false);
|
|
167
|
+
const uniformsRef = import_react.useRef(options.uniforms);
|
|
168
|
+
const onErrorRef = import_react.useRef(options.onError);
|
|
169
|
+
const onFrameRef = import_react.useRef(options.onFrame);
|
|
170
|
+
const timeScaleRef = import_react.useRef(options.timeScale ?? 1);
|
|
171
|
+
const vertexRef = import_react.useRef(options.vertex);
|
|
172
|
+
const fragmentRef = import_react.useRef(options.fragment);
|
|
173
|
+
uniformsRef.current = options.uniforms;
|
|
174
|
+
onErrorRef.current = options.onError;
|
|
175
|
+
onFrameRef.current = options.onFrame;
|
|
176
|
+
timeScaleRef.current = options.timeScale ?? 1;
|
|
177
|
+
vertexRef.current = options.vertex;
|
|
178
|
+
fragmentRef.current = options.fragment;
|
|
179
|
+
const render = import_react.useCallback((time) => {
|
|
180
|
+
if (contextLostRef.current)
|
|
181
|
+
return;
|
|
182
|
+
const state = stateRef.current;
|
|
183
|
+
const canvas = canvasRef.current;
|
|
184
|
+
if (!state || !canvas)
|
|
185
|
+
return;
|
|
186
|
+
const deltaTime = lastFrameTimeRef.current === 0 ? 0 : (time - lastFrameTimeRef.current) / 1000;
|
|
187
|
+
lastFrameTimeRef.current = time;
|
|
188
|
+
elapsedTimeRef.current += deltaTime * timeScaleRef.current;
|
|
189
|
+
const { gl, program, positionAttributeLocation, uniformLocationCache } = state;
|
|
190
|
+
const elapsedTime = elapsedTimeRef.current;
|
|
191
|
+
const dpr = window.devicePixelRatio || 1;
|
|
192
|
+
const displayWidth = canvas.clientWidth;
|
|
193
|
+
const displayHeight = canvas.clientHeight;
|
|
194
|
+
if (displayWidth === 0 || displayHeight === 0) {
|
|
195
|
+
animationFrameRef.current = requestAnimationFrame(render);
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
const bufferWidth = Math.round(displayWidth * dpr);
|
|
199
|
+
const bufferHeight = Math.round(displayHeight * dpr);
|
|
200
|
+
if (canvas.width !== bufferWidth || canvas.height !== bufferHeight) {
|
|
201
|
+
canvas.width = bufferWidth;
|
|
202
|
+
canvas.height = bufferHeight;
|
|
203
|
+
gl.viewport(0, 0, bufferWidth, bufferHeight);
|
|
204
|
+
}
|
|
205
|
+
gl.clearColor(0, 0, 0, 1);
|
|
206
|
+
gl.clear(gl.COLOR_BUFFER_BIT);
|
|
207
|
+
gl.useProgram(program);
|
|
208
|
+
gl.enableVertexAttribArray(positionAttributeLocation);
|
|
209
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, state.positionBuffer);
|
|
210
|
+
gl.vertexAttribPointer(positionAttributeLocation, 2, gl.FLOAT, false, 0, 0);
|
|
211
|
+
const defaultUniforms = {
|
|
212
|
+
iTime: elapsedTime,
|
|
213
|
+
iMouse: mouseRef.current,
|
|
214
|
+
iMouseLeftDown: mouseLeftDownRef.current ? 1 : 0,
|
|
215
|
+
iResolution: [canvas.width, canvas.height]
|
|
216
|
+
};
|
|
217
|
+
setUniforms(gl, program, defaultUniforms, uniformLocationCache);
|
|
218
|
+
if (uniformsRef.current) {
|
|
219
|
+
setUniforms(gl, program, uniformsRef.current, uniformLocationCache);
|
|
220
|
+
}
|
|
221
|
+
gl.drawArrays(gl.TRIANGLES, 0, 6);
|
|
222
|
+
if (onFrameRef.current) {
|
|
223
|
+
onFrameRef.current({
|
|
224
|
+
deltaTime,
|
|
225
|
+
time: elapsedTime,
|
|
226
|
+
resolution: [canvas.width, canvas.height],
|
|
227
|
+
mouse: mouseRef.current
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
animationFrameRef.current = requestAnimationFrame(render);
|
|
231
|
+
}, []);
|
|
232
|
+
import_react.useEffect(() => {
|
|
233
|
+
const canvas = canvasRef.current;
|
|
234
|
+
if (!canvas)
|
|
235
|
+
return;
|
|
236
|
+
const initialize = () => {
|
|
237
|
+
try {
|
|
238
|
+
stateRef.current = initializeWebGL(canvas, vertexRef.current, fragmentRef.current);
|
|
239
|
+
elapsedTimeRef.current = 0;
|
|
240
|
+
lastFrameTimeRef.current = 0;
|
|
241
|
+
contextLostRef.current = false;
|
|
242
|
+
animationFrameRef.current = requestAnimationFrame(render);
|
|
243
|
+
} catch (err) {
|
|
244
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
245
|
+
if (onErrorRef.current) {
|
|
246
|
+
onErrorRef.current(error);
|
|
247
|
+
} else {
|
|
248
|
+
console.error("WebGL initialization failed:", error);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
};
|
|
252
|
+
const handleContextLost = (event) => {
|
|
253
|
+
event.preventDefault();
|
|
254
|
+
contextLostRef.current = true;
|
|
255
|
+
cancelAnimationFrame(animationFrameRef.current);
|
|
256
|
+
stateRef.current = null;
|
|
257
|
+
};
|
|
258
|
+
const handleContextRestored = () => {
|
|
259
|
+
initialize();
|
|
260
|
+
};
|
|
261
|
+
canvas.addEventListener("webglcontextlost", handleContextLost);
|
|
262
|
+
canvas.addEventListener("webglcontextrestored", handleContextRestored);
|
|
263
|
+
initialize();
|
|
264
|
+
return () => {
|
|
265
|
+
canvas.removeEventListener("webglcontextlost", handleContextLost);
|
|
266
|
+
canvas.removeEventListener("webglcontextrestored", handleContextRestored);
|
|
267
|
+
cancelAnimationFrame(animationFrameRef.current);
|
|
268
|
+
if (stateRef.current) {
|
|
269
|
+
cleanupWebGL(stateRef.current.gl, stateRef.current);
|
|
270
|
+
stateRef.current = null;
|
|
271
|
+
}
|
|
272
|
+
};
|
|
273
|
+
}, [render]);
|
|
274
|
+
import_react.useEffect(() => {
|
|
275
|
+
const canvas = canvasRef.current;
|
|
276
|
+
if (!canvas)
|
|
277
|
+
return;
|
|
278
|
+
const updateRect = () => {
|
|
279
|
+
canvasRectRef.current = canvas.getBoundingClientRect();
|
|
280
|
+
};
|
|
281
|
+
updateRect();
|
|
282
|
+
const resizeObserver = new ResizeObserver(updateRect);
|
|
283
|
+
resizeObserver.observe(canvas);
|
|
284
|
+
window.addEventListener("scroll", updateRect, { passive: true });
|
|
285
|
+
const handleMouseMove = (event) => {
|
|
286
|
+
const rect = canvasRectRef.current;
|
|
287
|
+
if (!rect)
|
|
288
|
+
return;
|
|
289
|
+
const dpr = window.devicePixelRatio || 1;
|
|
290
|
+
const x = (event.clientX - rect.left) * dpr;
|
|
291
|
+
const y = (rect.height - (event.clientY - rect.top)) * dpr;
|
|
292
|
+
mouseRef.current = [x, y];
|
|
293
|
+
};
|
|
294
|
+
const handleMouseDown = (event) => {
|
|
295
|
+
if (event.button === 0) {
|
|
296
|
+
mouseLeftDownRef.current = true;
|
|
297
|
+
}
|
|
298
|
+
};
|
|
299
|
+
const handleMouseUp = (event) => {
|
|
300
|
+
if (event.button === 0) {
|
|
301
|
+
mouseLeftDownRef.current = false;
|
|
302
|
+
}
|
|
303
|
+
};
|
|
304
|
+
window.addEventListener("mousemove", handleMouseMove);
|
|
305
|
+
window.addEventListener("mousedown", handleMouseDown);
|
|
306
|
+
window.addEventListener("mouseup", handleMouseUp);
|
|
307
|
+
return () => {
|
|
308
|
+
resizeObserver.disconnect();
|
|
309
|
+
window.removeEventListener("scroll", updateRect);
|
|
310
|
+
window.removeEventListener("mousemove", handleMouseMove);
|
|
311
|
+
window.removeEventListener("mousedown", handleMouseDown);
|
|
312
|
+
window.removeEventListener("mouseup", handleMouseUp);
|
|
313
|
+
};
|
|
314
|
+
}, []);
|
|
315
|
+
return { canvasRef, mouseRef };
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// src/ReactShader.tsx
|
|
319
|
+
var jsx_dev_runtime = require("react/jsx-dev-runtime");
|
|
320
|
+
var DEFAULT_VERTEX = `#version 300 es
|
|
321
|
+
in vec2 a_position;
|
|
322
|
+
|
|
323
|
+
void main() {
|
|
324
|
+
gl_Position = vec4(a_position, 0.0, 1.0);
|
|
325
|
+
}
|
|
326
|
+
`;
|
|
327
|
+
function ReactShader({
|
|
328
|
+
className,
|
|
329
|
+
fragment,
|
|
330
|
+
vertex = DEFAULT_VERTEX,
|
|
331
|
+
uniforms,
|
|
332
|
+
debug = false,
|
|
333
|
+
fullscreen = false,
|
|
334
|
+
timeScale = 1,
|
|
335
|
+
onFrame
|
|
336
|
+
}) {
|
|
337
|
+
const [error, setError] = import_react2.useState(null);
|
|
338
|
+
const [debugInfo, setDebugInfo] = import_react2.useState({
|
|
339
|
+
iResolution: [0, 0],
|
|
340
|
+
iMouse: [0, 0]
|
|
341
|
+
});
|
|
342
|
+
const lastDebugUpdateRef = import_react2.useRef(0);
|
|
343
|
+
const handleError = import_react2.useCallback((err) => {
|
|
344
|
+
setError(err.message);
|
|
345
|
+
console.error("ReactShader error:", err);
|
|
346
|
+
}, []);
|
|
347
|
+
const handleFrame = import_react2.useCallback((info) => {
|
|
348
|
+
if (onFrame) {
|
|
349
|
+
onFrame(info);
|
|
350
|
+
}
|
|
351
|
+
if (!debug)
|
|
352
|
+
return;
|
|
353
|
+
const now = performance.now();
|
|
354
|
+
if (now - lastDebugUpdateRef.current < 100)
|
|
355
|
+
return;
|
|
356
|
+
lastDebugUpdateRef.current = now;
|
|
357
|
+
setDebugInfo({
|
|
358
|
+
iResolution: info.resolution,
|
|
359
|
+
iMouse: info.mouse
|
|
360
|
+
});
|
|
361
|
+
}, [debug, onFrame]);
|
|
362
|
+
import_react2.useEffect(() => {
|
|
363
|
+
setError(null);
|
|
364
|
+
}, [fragment, vertex]);
|
|
365
|
+
const { canvasRef } = useWebGL({
|
|
366
|
+
fragment,
|
|
367
|
+
vertex,
|
|
368
|
+
uniforms,
|
|
369
|
+
onError: handleError,
|
|
370
|
+
onFrame: handleFrame,
|
|
371
|
+
timeScale
|
|
372
|
+
});
|
|
373
|
+
const containerStyle = fullscreen ? {
|
|
374
|
+
position: "fixed",
|
|
375
|
+
top: 0,
|
|
376
|
+
left: 0,
|
|
377
|
+
width: "100vw",
|
|
378
|
+
height: "100vh",
|
|
379
|
+
zIndex: 9000
|
|
380
|
+
} : {
|
|
381
|
+
position: "relative",
|
|
382
|
+
width: "100%",
|
|
383
|
+
height: "100%"
|
|
384
|
+
};
|
|
385
|
+
if (error) {
|
|
386
|
+
return /* @__PURE__ */ jsx_dev_runtime.jsxDEV("div", {
|
|
387
|
+
className,
|
|
388
|
+
style: {
|
|
389
|
+
...containerStyle,
|
|
390
|
+
display: "flex",
|
|
391
|
+
alignItems: "center",
|
|
392
|
+
justifyContent: "center",
|
|
393
|
+
backgroundColor: "#1a1a1a",
|
|
394
|
+
color: "#ff6b6b",
|
|
395
|
+
fontFamily: "monospace",
|
|
396
|
+
fontSize: "12px",
|
|
397
|
+
padding: "16px",
|
|
398
|
+
overflow: "auto",
|
|
399
|
+
boxSizing: "border-box"
|
|
400
|
+
},
|
|
401
|
+
children: /* @__PURE__ */ jsx_dev_runtime.jsxDEV("pre", {
|
|
402
|
+
style: { margin: 0, whiteSpace: "pre-wrap" },
|
|
403
|
+
children: error
|
|
404
|
+
}, undefined, false, undefined, this)
|
|
405
|
+
}, undefined, false, undefined, this);
|
|
406
|
+
}
|
|
407
|
+
return /* @__PURE__ */ jsx_dev_runtime.jsxDEV("div", {
|
|
408
|
+
style: containerStyle,
|
|
409
|
+
children: [
|
|
410
|
+
/* @__PURE__ */ jsx_dev_runtime.jsxDEV("canvas", {
|
|
411
|
+
ref: canvasRef,
|
|
412
|
+
className,
|
|
413
|
+
style: { display: "block", width: "100%", height: "100%" }
|
|
414
|
+
}, undefined, false, undefined, this),
|
|
415
|
+
debug && /* @__PURE__ */ jsx_dev_runtime.jsxDEV("div", {
|
|
416
|
+
style: {
|
|
417
|
+
position: "absolute",
|
|
418
|
+
top: 8,
|
|
419
|
+
left: 8,
|
|
420
|
+
backgroundColor: "rgba(0, 0, 0, 0.7)",
|
|
421
|
+
color: "#fff",
|
|
422
|
+
fontFamily: "monospace",
|
|
423
|
+
fontSize: "12px",
|
|
424
|
+
padding: "8px",
|
|
425
|
+
borderRadius: "4px",
|
|
426
|
+
pointerEvents: "none"
|
|
427
|
+
},
|
|
428
|
+
children: [
|
|
429
|
+
/* @__PURE__ */ jsx_dev_runtime.jsxDEV("div", {
|
|
430
|
+
children: [
|
|
431
|
+
"iResolution: [",
|
|
432
|
+
debugInfo.iResolution[0],
|
|
433
|
+
", ",
|
|
434
|
+
debugInfo.iResolution[1],
|
|
435
|
+
"]"
|
|
436
|
+
]
|
|
437
|
+
}, undefined, true, undefined, this),
|
|
438
|
+
/* @__PURE__ */ jsx_dev_runtime.jsxDEV("div", {
|
|
439
|
+
children: [
|
|
440
|
+
"iMouse: [",
|
|
441
|
+
Math.round(debugInfo.iMouse[0]),
|
|
442
|
+
", ",
|
|
443
|
+
Math.round(debugInfo.iMouse[1]),
|
|
444
|
+
"]"
|
|
445
|
+
]
|
|
446
|
+
}, undefined, true, undefined, this)
|
|
447
|
+
]
|
|
448
|
+
}, undefined, true, undefined, this)
|
|
449
|
+
]
|
|
450
|
+
}, undefined, true, undefined, this);
|
|
451
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAC3C,YAAY,EACV,eAAe,EACf,gBAAgB,EAChB,YAAY,EACZ,IAAI,EACJ,IAAI,EACJ,IAAI,GACL,MAAM,SAAS,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,419 @@
|
|
|
1
|
+
// src/ReactShader.tsx
|
|
2
|
+
import { useCallback as useCallback2, useEffect as useEffect2, useRef as useRef2, useState } from "react";
|
|
3
|
+
|
|
4
|
+
// src/hooks/useWebGL.ts
|
|
5
|
+
import { useCallback, useEffect, useRef } from "react";
|
|
6
|
+
|
|
7
|
+
// src/utils/shader.ts
|
|
8
|
+
function compileShader(gl, type, source) {
|
|
9
|
+
const shader = gl.createShader(type);
|
|
10
|
+
if (!shader) {
|
|
11
|
+
throw new Error(`Failed to create shader of type ${type === gl.VERTEX_SHADER ? "VERTEX" : "FRAGMENT"}`);
|
|
12
|
+
}
|
|
13
|
+
gl.shaderSource(shader, source);
|
|
14
|
+
gl.compileShader(shader);
|
|
15
|
+
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
|
|
16
|
+
const info = gl.getShaderInfoLog(shader);
|
|
17
|
+
gl.deleteShader(shader);
|
|
18
|
+
const shaderType = type === gl.VERTEX_SHADER ? "Vertex" : "Fragment";
|
|
19
|
+
throw new Error(`${shaderType} shader compilation failed:
|
|
20
|
+
${info}`);
|
|
21
|
+
}
|
|
22
|
+
return shader;
|
|
23
|
+
}
|
|
24
|
+
function createProgram(gl, vertexShader, fragmentShader) {
|
|
25
|
+
const program = gl.createProgram();
|
|
26
|
+
if (!program) {
|
|
27
|
+
throw new Error("Failed to create WebGL program");
|
|
28
|
+
}
|
|
29
|
+
gl.attachShader(program, vertexShader);
|
|
30
|
+
gl.attachShader(program, fragmentShader);
|
|
31
|
+
gl.linkProgram(program);
|
|
32
|
+
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
|
|
33
|
+
const info = gl.getProgramInfoLog(program);
|
|
34
|
+
gl.deleteProgram(program);
|
|
35
|
+
throw new Error(`Program linking failed:
|
|
36
|
+
${info}`);
|
|
37
|
+
}
|
|
38
|
+
return program;
|
|
39
|
+
}
|
|
40
|
+
function createShaderProgram(gl, vertexSource, fragmentSource) {
|
|
41
|
+
const vertexShader = compileShader(gl, gl.VERTEX_SHADER, vertexSource);
|
|
42
|
+
const fragmentShader = compileShader(gl, gl.FRAGMENT_SHADER, fragmentSource);
|
|
43
|
+
try {
|
|
44
|
+
return createProgram(gl, vertexShader, fragmentShader);
|
|
45
|
+
} finally {
|
|
46
|
+
gl.deleteShader(vertexShader);
|
|
47
|
+
gl.deleteShader(fragmentShader);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// src/utils/uniforms.ts
|
|
52
|
+
function isVec2(value) {
|
|
53
|
+
return Array.isArray(value) && value.length === 2;
|
|
54
|
+
}
|
|
55
|
+
function isVec3(value) {
|
|
56
|
+
return Array.isArray(value) && value.length === 3;
|
|
57
|
+
}
|
|
58
|
+
function isVec4(value) {
|
|
59
|
+
return Array.isArray(value) && value.length === 4;
|
|
60
|
+
}
|
|
61
|
+
function setUniform(gl, location, value) {
|
|
62
|
+
if (location === null) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
if (typeof value === "number") {
|
|
66
|
+
gl.uniform1f(location, value);
|
|
67
|
+
} else if (isVec4(value)) {
|
|
68
|
+
gl.uniform4f(location, value[0], value[1], value[2], value[3]);
|
|
69
|
+
} else if (isVec3(value)) {
|
|
70
|
+
gl.uniform3f(location, value[0], value[1], value[2]);
|
|
71
|
+
} else if (isVec2(value)) {
|
|
72
|
+
gl.uniform2f(location, value[0], value[1]);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
function getUniformLocation(gl, program, name) {
|
|
76
|
+
return gl.getUniformLocation(program, name);
|
|
77
|
+
}
|
|
78
|
+
function setUniforms(gl, program, uniforms, locationCache) {
|
|
79
|
+
for (const [name, value] of Object.entries(uniforms)) {
|
|
80
|
+
let location = locationCache.get(name);
|
|
81
|
+
if (location === undefined) {
|
|
82
|
+
location = getUniformLocation(gl, program, name);
|
|
83
|
+
locationCache.set(name, location);
|
|
84
|
+
}
|
|
85
|
+
setUniform(gl, location, value);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
function createUniformLocationCache() {
|
|
89
|
+
return new Map;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// src/hooks/useWebGL.ts
|
|
93
|
+
function initializeWebGL(canvas, vertexSource, fragmentSource) {
|
|
94
|
+
const gl = canvas.getContext("webgl2") || canvas.getContext("webgl");
|
|
95
|
+
if (!gl) {
|
|
96
|
+
throw new Error("WebGL not supported");
|
|
97
|
+
}
|
|
98
|
+
const program = createShaderProgram(gl, vertexSource, fragmentSource);
|
|
99
|
+
const positionBuffer = gl.createBuffer();
|
|
100
|
+
if (!positionBuffer) {
|
|
101
|
+
throw new Error("Failed to create position buffer");
|
|
102
|
+
}
|
|
103
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
|
|
104
|
+
const positions = new Float32Array([-1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1]);
|
|
105
|
+
gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW);
|
|
106
|
+
const positionAttributeLocation = gl.getAttribLocation(program, "a_position");
|
|
107
|
+
if (positionAttributeLocation === -1) {
|
|
108
|
+
throw new Error('Vertex shader must have an "a_position" attribute');
|
|
109
|
+
}
|
|
110
|
+
return {
|
|
111
|
+
gl,
|
|
112
|
+
program,
|
|
113
|
+
positionBuffer,
|
|
114
|
+
positionAttributeLocation,
|
|
115
|
+
uniformLocationCache: createUniformLocationCache()
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
function cleanupWebGL(gl, state) {
|
|
119
|
+
gl.deleteBuffer(state.positionBuffer);
|
|
120
|
+
gl.deleteProgram(state.program);
|
|
121
|
+
}
|
|
122
|
+
function useWebGL(options) {
|
|
123
|
+
const canvasRef = useRef(null);
|
|
124
|
+
const stateRef = useRef(null);
|
|
125
|
+
const animationFrameRef = useRef(0);
|
|
126
|
+
const elapsedTimeRef = useRef(0);
|
|
127
|
+
const lastFrameTimeRef = useRef(0);
|
|
128
|
+
const mouseRef = useRef([0, 0]);
|
|
129
|
+
const mouseLeftDownRef = useRef(false);
|
|
130
|
+
const canvasRectRef = useRef(null);
|
|
131
|
+
const contextLostRef = useRef(false);
|
|
132
|
+
const uniformsRef = useRef(options.uniforms);
|
|
133
|
+
const onErrorRef = useRef(options.onError);
|
|
134
|
+
const onFrameRef = useRef(options.onFrame);
|
|
135
|
+
const timeScaleRef = useRef(options.timeScale ?? 1);
|
|
136
|
+
const vertexRef = useRef(options.vertex);
|
|
137
|
+
const fragmentRef = useRef(options.fragment);
|
|
138
|
+
uniformsRef.current = options.uniforms;
|
|
139
|
+
onErrorRef.current = options.onError;
|
|
140
|
+
onFrameRef.current = options.onFrame;
|
|
141
|
+
timeScaleRef.current = options.timeScale ?? 1;
|
|
142
|
+
vertexRef.current = options.vertex;
|
|
143
|
+
fragmentRef.current = options.fragment;
|
|
144
|
+
const render = useCallback((time) => {
|
|
145
|
+
if (contextLostRef.current)
|
|
146
|
+
return;
|
|
147
|
+
const state = stateRef.current;
|
|
148
|
+
const canvas = canvasRef.current;
|
|
149
|
+
if (!state || !canvas)
|
|
150
|
+
return;
|
|
151
|
+
const deltaTime = lastFrameTimeRef.current === 0 ? 0 : (time - lastFrameTimeRef.current) / 1000;
|
|
152
|
+
lastFrameTimeRef.current = time;
|
|
153
|
+
elapsedTimeRef.current += deltaTime * timeScaleRef.current;
|
|
154
|
+
const { gl, program, positionAttributeLocation, uniformLocationCache } = state;
|
|
155
|
+
const elapsedTime = elapsedTimeRef.current;
|
|
156
|
+
const dpr = window.devicePixelRatio || 1;
|
|
157
|
+
const displayWidth = canvas.clientWidth;
|
|
158
|
+
const displayHeight = canvas.clientHeight;
|
|
159
|
+
if (displayWidth === 0 || displayHeight === 0) {
|
|
160
|
+
animationFrameRef.current = requestAnimationFrame(render);
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
const bufferWidth = Math.round(displayWidth * dpr);
|
|
164
|
+
const bufferHeight = Math.round(displayHeight * dpr);
|
|
165
|
+
if (canvas.width !== bufferWidth || canvas.height !== bufferHeight) {
|
|
166
|
+
canvas.width = bufferWidth;
|
|
167
|
+
canvas.height = bufferHeight;
|
|
168
|
+
gl.viewport(0, 0, bufferWidth, bufferHeight);
|
|
169
|
+
}
|
|
170
|
+
gl.clearColor(0, 0, 0, 1);
|
|
171
|
+
gl.clear(gl.COLOR_BUFFER_BIT);
|
|
172
|
+
gl.useProgram(program);
|
|
173
|
+
gl.enableVertexAttribArray(positionAttributeLocation);
|
|
174
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, state.positionBuffer);
|
|
175
|
+
gl.vertexAttribPointer(positionAttributeLocation, 2, gl.FLOAT, false, 0, 0);
|
|
176
|
+
const defaultUniforms = {
|
|
177
|
+
iTime: elapsedTime,
|
|
178
|
+
iMouse: mouseRef.current,
|
|
179
|
+
iMouseLeftDown: mouseLeftDownRef.current ? 1 : 0,
|
|
180
|
+
iResolution: [canvas.width, canvas.height]
|
|
181
|
+
};
|
|
182
|
+
setUniforms(gl, program, defaultUniforms, uniformLocationCache);
|
|
183
|
+
if (uniformsRef.current) {
|
|
184
|
+
setUniforms(gl, program, uniformsRef.current, uniformLocationCache);
|
|
185
|
+
}
|
|
186
|
+
gl.drawArrays(gl.TRIANGLES, 0, 6);
|
|
187
|
+
if (onFrameRef.current) {
|
|
188
|
+
onFrameRef.current({
|
|
189
|
+
deltaTime,
|
|
190
|
+
time: elapsedTime,
|
|
191
|
+
resolution: [canvas.width, canvas.height],
|
|
192
|
+
mouse: mouseRef.current
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
animationFrameRef.current = requestAnimationFrame(render);
|
|
196
|
+
}, []);
|
|
197
|
+
useEffect(() => {
|
|
198
|
+
const canvas = canvasRef.current;
|
|
199
|
+
if (!canvas)
|
|
200
|
+
return;
|
|
201
|
+
const initialize = () => {
|
|
202
|
+
try {
|
|
203
|
+
stateRef.current = initializeWebGL(canvas, vertexRef.current, fragmentRef.current);
|
|
204
|
+
elapsedTimeRef.current = 0;
|
|
205
|
+
lastFrameTimeRef.current = 0;
|
|
206
|
+
contextLostRef.current = false;
|
|
207
|
+
animationFrameRef.current = requestAnimationFrame(render);
|
|
208
|
+
} catch (err) {
|
|
209
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
210
|
+
if (onErrorRef.current) {
|
|
211
|
+
onErrorRef.current(error);
|
|
212
|
+
} else {
|
|
213
|
+
console.error("WebGL initialization failed:", error);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
const handleContextLost = (event) => {
|
|
218
|
+
event.preventDefault();
|
|
219
|
+
contextLostRef.current = true;
|
|
220
|
+
cancelAnimationFrame(animationFrameRef.current);
|
|
221
|
+
stateRef.current = null;
|
|
222
|
+
};
|
|
223
|
+
const handleContextRestored = () => {
|
|
224
|
+
initialize();
|
|
225
|
+
};
|
|
226
|
+
canvas.addEventListener("webglcontextlost", handleContextLost);
|
|
227
|
+
canvas.addEventListener("webglcontextrestored", handleContextRestored);
|
|
228
|
+
initialize();
|
|
229
|
+
return () => {
|
|
230
|
+
canvas.removeEventListener("webglcontextlost", handleContextLost);
|
|
231
|
+
canvas.removeEventListener("webglcontextrestored", handleContextRestored);
|
|
232
|
+
cancelAnimationFrame(animationFrameRef.current);
|
|
233
|
+
if (stateRef.current) {
|
|
234
|
+
cleanupWebGL(stateRef.current.gl, stateRef.current);
|
|
235
|
+
stateRef.current = null;
|
|
236
|
+
}
|
|
237
|
+
};
|
|
238
|
+
}, [render]);
|
|
239
|
+
useEffect(() => {
|
|
240
|
+
const canvas = canvasRef.current;
|
|
241
|
+
if (!canvas)
|
|
242
|
+
return;
|
|
243
|
+
const updateRect = () => {
|
|
244
|
+
canvasRectRef.current = canvas.getBoundingClientRect();
|
|
245
|
+
};
|
|
246
|
+
updateRect();
|
|
247
|
+
const resizeObserver = new ResizeObserver(updateRect);
|
|
248
|
+
resizeObserver.observe(canvas);
|
|
249
|
+
window.addEventListener("scroll", updateRect, { passive: true });
|
|
250
|
+
const handleMouseMove = (event) => {
|
|
251
|
+
const rect = canvasRectRef.current;
|
|
252
|
+
if (!rect)
|
|
253
|
+
return;
|
|
254
|
+
const dpr = window.devicePixelRatio || 1;
|
|
255
|
+
const x = (event.clientX - rect.left) * dpr;
|
|
256
|
+
const y = (rect.height - (event.clientY - rect.top)) * dpr;
|
|
257
|
+
mouseRef.current = [x, y];
|
|
258
|
+
};
|
|
259
|
+
const handleMouseDown = (event) => {
|
|
260
|
+
if (event.button === 0) {
|
|
261
|
+
mouseLeftDownRef.current = true;
|
|
262
|
+
}
|
|
263
|
+
};
|
|
264
|
+
const handleMouseUp = (event) => {
|
|
265
|
+
if (event.button === 0) {
|
|
266
|
+
mouseLeftDownRef.current = false;
|
|
267
|
+
}
|
|
268
|
+
};
|
|
269
|
+
window.addEventListener("mousemove", handleMouseMove);
|
|
270
|
+
window.addEventListener("mousedown", handleMouseDown);
|
|
271
|
+
window.addEventListener("mouseup", handleMouseUp);
|
|
272
|
+
return () => {
|
|
273
|
+
resizeObserver.disconnect();
|
|
274
|
+
window.removeEventListener("scroll", updateRect);
|
|
275
|
+
window.removeEventListener("mousemove", handleMouseMove);
|
|
276
|
+
window.removeEventListener("mousedown", handleMouseDown);
|
|
277
|
+
window.removeEventListener("mouseup", handleMouseUp);
|
|
278
|
+
};
|
|
279
|
+
}, []);
|
|
280
|
+
return { canvasRef, mouseRef };
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// src/ReactShader.tsx
|
|
284
|
+
import { jsxDEV } from "react/jsx-dev-runtime";
|
|
285
|
+
var DEFAULT_VERTEX = `#version 300 es
|
|
286
|
+
in vec2 a_position;
|
|
287
|
+
|
|
288
|
+
void main() {
|
|
289
|
+
gl_Position = vec4(a_position, 0.0, 1.0);
|
|
290
|
+
}
|
|
291
|
+
`;
|
|
292
|
+
function ReactShader({
|
|
293
|
+
className,
|
|
294
|
+
fragment,
|
|
295
|
+
vertex = DEFAULT_VERTEX,
|
|
296
|
+
uniforms,
|
|
297
|
+
debug = false,
|
|
298
|
+
fullscreen = false,
|
|
299
|
+
timeScale = 1,
|
|
300
|
+
onFrame
|
|
301
|
+
}) {
|
|
302
|
+
const [error, setError] = useState(null);
|
|
303
|
+
const [debugInfo, setDebugInfo] = useState({
|
|
304
|
+
iResolution: [0, 0],
|
|
305
|
+
iMouse: [0, 0]
|
|
306
|
+
});
|
|
307
|
+
const lastDebugUpdateRef = useRef2(0);
|
|
308
|
+
const handleError = useCallback2((err) => {
|
|
309
|
+
setError(err.message);
|
|
310
|
+
console.error("ReactShader error:", err);
|
|
311
|
+
}, []);
|
|
312
|
+
const handleFrame = useCallback2((info) => {
|
|
313
|
+
if (onFrame) {
|
|
314
|
+
onFrame(info);
|
|
315
|
+
}
|
|
316
|
+
if (!debug)
|
|
317
|
+
return;
|
|
318
|
+
const now = performance.now();
|
|
319
|
+
if (now - lastDebugUpdateRef.current < 100)
|
|
320
|
+
return;
|
|
321
|
+
lastDebugUpdateRef.current = now;
|
|
322
|
+
setDebugInfo({
|
|
323
|
+
iResolution: info.resolution,
|
|
324
|
+
iMouse: info.mouse
|
|
325
|
+
});
|
|
326
|
+
}, [debug, onFrame]);
|
|
327
|
+
useEffect2(() => {
|
|
328
|
+
setError(null);
|
|
329
|
+
}, [fragment, vertex]);
|
|
330
|
+
const { canvasRef } = useWebGL({
|
|
331
|
+
fragment,
|
|
332
|
+
vertex,
|
|
333
|
+
uniforms,
|
|
334
|
+
onError: handleError,
|
|
335
|
+
onFrame: handleFrame,
|
|
336
|
+
timeScale
|
|
337
|
+
});
|
|
338
|
+
const containerStyle = fullscreen ? {
|
|
339
|
+
position: "fixed",
|
|
340
|
+
top: 0,
|
|
341
|
+
left: 0,
|
|
342
|
+
width: "100vw",
|
|
343
|
+
height: "100vh",
|
|
344
|
+
zIndex: 9000
|
|
345
|
+
} : {
|
|
346
|
+
position: "relative",
|
|
347
|
+
width: "100%",
|
|
348
|
+
height: "100%"
|
|
349
|
+
};
|
|
350
|
+
if (error) {
|
|
351
|
+
return /* @__PURE__ */ jsxDEV("div", {
|
|
352
|
+
className,
|
|
353
|
+
style: {
|
|
354
|
+
...containerStyle,
|
|
355
|
+
display: "flex",
|
|
356
|
+
alignItems: "center",
|
|
357
|
+
justifyContent: "center",
|
|
358
|
+
backgroundColor: "#1a1a1a",
|
|
359
|
+
color: "#ff6b6b",
|
|
360
|
+
fontFamily: "monospace",
|
|
361
|
+
fontSize: "12px",
|
|
362
|
+
padding: "16px",
|
|
363
|
+
overflow: "auto",
|
|
364
|
+
boxSizing: "border-box"
|
|
365
|
+
},
|
|
366
|
+
children: /* @__PURE__ */ jsxDEV("pre", {
|
|
367
|
+
style: { margin: 0, whiteSpace: "pre-wrap" },
|
|
368
|
+
children: error
|
|
369
|
+
}, undefined, false, undefined, this)
|
|
370
|
+
}, undefined, false, undefined, this);
|
|
371
|
+
}
|
|
372
|
+
return /* @__PURE__ */ jsxDEV("div", {
|
|
373
|
+
style: containerStyle,
|
|
374
|
+
children: [
|
|
375
|
+
/* @__PURE__ */ jsxDEV("canvas", {
|
|
376
|
+
ref: canvasRef,
|
|
377
|
+
className,
|
|
378
|
+
style: { display: "block", width: "100%", height: "100%" }
|
|
379
|
+
}, undefined, false, undefined, this),
|
|
380
|
+
debug && /* @__PURE__ */ jsxDEV("div", {
|
|
381
|
+
style: {
|
|
382
|
+
position: "absolute",
|
|
383
|
+
top: 8,
|
|
384
|
+
left: 8,
|
|
385
|
+
backgroundColor: "rgba(0, 0, 0, 0.7)",
|
|
386
|
+
color: "#fff",
|
|
387
|
+
fontFamily: "monospace",
|
|
388
|
+
fontSize: "12px",
|
|
389
|
+
padding: "8px",
|
|
390
|
+
borderRadius: "4px",
|
|
391
|
+
pointerEvents: "none"
|
|
392
|
+
},
|
|
393
|
+
children: [
|
|
394
|
+
/* @__PURE__ */ jsxDEV("div", {
|
|
395
|
+
children: [
|
|
396
|
+
"iResolution: [",
|
|
397
|
+
debugInfo.iResolution[0],
|
|
398
|
+
", ",
|
|
399
|
+
debugInfo.iResolution[1],
|
|
400
|
+
"]"
|
|
401
|
+
]
|
|
402
|
+
}, undefined, true, undefined, this),
|
|
403
|
+
/* @__PURE__ */ jsxDEV("div", {
|
|
404
|
+
children: [
|
|
405
|
+
"iMouse: [",
|
|
406
|
+
Math.round(debugInfo.iMouse[0]),
|
|
407
|
+
", ",
|
|
408
|
+
Math.round(debugInfo.iMouse[1]),
|
|
409
|
+
"]"
|
|
410
|
+
]
|
|
411
|
+
}, undefined, true, undefined, this)
|
|
412
|
+
]
|
|
413
|
+
}, undefined, true, undefined, this)
|
|
414
|
+
]
|
|
415
|
+
}, undefined, true, undefined, this);
|
|
416
|
+
}
|
|
417
|
+
export {
|
|
418
|
+
ReactShader
|
|
419
|
+
};
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { FrameInfo } from "./hooks/useWebGL";
|
|
2
|
+
export type Vec2 = [number, number];
|
|
3
|
+
export type Vec3 = [number, number, number];
|
|
4
|
+
export type Vec4 = [number, number, number, number];
|
|
5
|
+
export type UniformValue = number | Vec2 | Vec3 | Vec4;
|
|
6
|
+
export interface ReactShaderProps {
|
|
7
|
+
className?: string;
|
|
8
|
+
fragment: string;
|
|
9
|
+
vertex?: string;
|
|
10
|
+
uniforms?: Record<string, UniformValue>;
|
|
11
|
+
debug?: boolean;
|
|
12
|
+
fullscreen?: boolean;
|
|
13
|
+
timeScale?: number;
|
|
14
|
+
onFrame?: (info: FrameInfo) => void;
|
|
15
|
+
}
|
|
16
|
+
export interface DefaultUniforms {
|
|
17
|
+
iTime: number;
|
|
18
|
+
iMouse: Vec2;
|
|
19
|
+
iMouseLeftDown: number;
|
|
20
|
+
iResolution: Vec2;
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AAEjD,MAAM,MAAM,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;AACnC,MAAM,MAAM,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;AAC3C,MAAM,MAAM,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;AAEnD,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAA;AAEtD,MAAM,WAAW,gBAAgB;IAC/B,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAA;IACvC,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,IAAI,CAAA;CACpC;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,IAAI,CAAA;IACZ,cAAc,EAAE,MAAM,CAAA;IACtB,WAAW,EAAE,IAAI,CAAA;CAClB"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
type WebGLContext = WebGLRenderingContext | WebGL2RenderingContext;
|
|
2
|
+
export declare function compileShader(gl: WebGLContext, type: number, source: string): WebGLShader;
|
|
3
|
+
export declare function createProgram(gl: WebGLContext, vertexShader: WebGLShader, fragmentShader: WebGLShader): WebGLProgram;
|
|
4
|
+
export declare function createShaderProgram(gl: WebGLContext, vertexSource: string, fragmentSource: string): WebGLProgram;
|
|
5
|
+
export {};
|
|
6
|
+
//# sourceMappingURL=shader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shader.d.ts","sourceRoot":"","sources":["../../src/utils/shader.ts"],"names":[],"mappings":"AAAA,KAAK,YAAY,GAAG,qBAAqB,GAAG,sBAAsB,CAAA;AAElE,wBAAgB,aAAa,CAAC,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,WAAW,CAiBzF;AAED,wBAAgB,aAAa,CAAC,EAAE,EAAE,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,cAAc,EAAE,WAAW,GAAG,YAAY,CAiBpH;AAED,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,YAAY,CAWhH"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { UniformValue } from "../types";
|
|
2
|
+
type WebGLContext = WebGLRenderingContext | WebGL2RenderingContext;
|
|
3
|
+
export declare function setUniform(gl: WebGLContext, location: WebGLUniformLocation | null, value: UniformValue): void;
|
|
4
|
+
export declare function getUniformLocation(gl: WebGLContext, program: WebGLProgram, name: string): WebGLUniformLocation | null;
|
|
5
|
+
export declare function setUniforms(gl: WebGLContext, program: WebGLProgram, uniforms: Record<string, UniformValue>, locationCache: Map<string, WebGLUniformLocation | null>): void;
|
|
6
|
+
export declare function createUniformLocationCache(): Map<string, WebGLUniformLocation | null>;
|
|
7
|
+
export {};
|
|
8
|
+
//# sourceMappingURL=uniforms.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"uniforms.d.ts","sourceRoot":"","sources":["../../src/utils/uniforms.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAoB,MAAM,UAAU,CAAA;AAE9D,KAAK,YAAY,GAAG,qBAAqB,GAAG,sBAAsB,CAAA;AAclE,wBAAgB,UAAU,CAAC,EAAE,EAAE,YAAY,EAAE,QAAQ,EAAE,oBAAoB,GAAG,IAAI,EAAE,KAAK,EAAE,YAAY,GAAG,IAAI,CAc7G;AAED,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,YAAY,EAAE,OAAO,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,GAAG,oBAAoB,GAAG,IAAI,CAErH;AAED,wBAAgB,WAAW,CACzB,EAAE,EAAE,YAAY,EAChB,OAAO,EAAE,YAAY,EACrB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,EACtC,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,oBAAoB,GAAG,IAAI,CAAC,GACtD,IAAI,CASN;AAED,wBAAgB,0BAA0B,IAAI,GAAG,CAAC,MAAM,EAAE,oBAAoB,GAAG,IAAI,CAAC,CAErF"}
|
package/package.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@fjandin/react-shader",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "React component for rendering WebGL shaders",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"import": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"default": "./dist/index.js"
|
|
14
|
+
},
|
|
15
|
+
"require": {
|
|
16
|
+
"types": "./dist/index.d.cts",
|
|
17
|
+
"default": "./dist/index.cjs"
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"files": [
|
|
22
|
+
"dist"
|
|
23
|
+
],
|
|
24
|
+
"scripts": {
|
|
25
|
+
"build": "bun run build:esm && bun run build:cjs && bun run build:types",
|
|
26
|
+
"build:esm": "bun build ./src/index.ts --outdir ./dist --target browser --format esm --external react --external react-dom",
|
|
27
|
+
"build:cjs": "bun build ./src/index.ts --target browser --format cjs --outfile ./dist/index.cjs --external react --external react-dom",
|
|
28
|
+
"build:types": "tsc --emitDeclarationOnly --declaration --outDir ./dist",
|
|
29
|
+
"clean": "rm -rf dist",
|
|
30
|
+
"lint": "biome check",
|
|
31
|
+
"typecheck": "tsc --noEmit",
|
|
32
|
+
"dev": "bun run src/example/index.ts"
|
|
33
|
+
},
|
|
34
|
+
"peerDependencies": {
|
|
35
|
+
"react": ">=17.0.0",
|
|
36
|
+
"react-dom": ">=17.0.0"
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"@biomejs/biome": "2.3.11",
|
|
40
|
+
"@types/react": "^18.2.0",
|
|
41
|
+
"@types/react-dom": "^18.2.0",
|
|
42
|
+
"bun-types": "latest",
|
|
43
|
+
"react": "^18.2.0",
|
|
44
|
+
"react-dom": "^18.2.0",
|
|
45
|
+
"typescript": "^5.0.0"
|
|
46
|
+
},
|
|
47
|
+
"keywords": [
|
|
48
|
+
"react",
|
|
49
|
+
"shader",
|
|
50
|
+
"webgl",
|
|
51
|
+
"glsl",
|
|
52
|
+
"graphics"
|
|
53
|
+
],
|
|
54
|
+
"author": "fjandin",
|
|
55
|
+
"license": "MIT",
|
|
56
|
+
"repository": {
|
|
57
|
+
"type": "git",
|
|
58
|
+
"url": "https://github.com/fjandin/fjandin-react-shader.git"
|
|
59
|
+
}
|
|
60
|
+
}
|