shader-bg 0.0.0 → 0.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 ADDED
@@ -0,0 +1,245 @@
1
+ # shader-bg
2
+
3
+ Lightweight WebGL shader background renderer for the browser.
4
+
5
+ `shader-bg` creates or reuses a `<canvas>`, compiles your shaders, updates common uniforms every frame, and renders a full-screen shader animation.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm install shader-bg
11
+ ```
12
+
13
+ ## Quick start
14
+
15
+ ```ts
16
+ import { ShaderBg } from 'shader-bg';
17
+
18
+ const bg = new ShaderBg(document.body, {
19
+ fragmentShader: `
20
+ precision mediump float;
21
+
22
+ uniform float u_time;
23
+ uniform vec2 u_resolution;
24
+
25
+ void main() {
26
+ vec2 uv = gl_FragCoord.xy / u_resolution.xy;
27
+ gl_FragColor = vec4(uv.x, uv.y, abs(sin(u_time)), 1.0);
28
+ }
29
+ `,
30
+ });
31
+
32
+ bg.start();
33
+ ```
34
+
35
+ To fully clean up (RAF loop, observers, WebGL resources, optional auto-created canvas):
36
+
37
+ ```ts
38
+ bg.dispose();
39
+ ```
40
+
41
+ ## API
42
+
43
+ ### `new ShaderBg(target, options)`
44
+
45
+ Creates the renderer instance.
46
+
47
+ - `target`: `string | HTMLElement | Element`
48
+ - CSS selector string (for example: `"#hero"`)
49
+ - Existing element or canvas
50
+ - If `target` is **not** a canvas, a canvas is created and appended to it
51
+ - `options`: `OptionsInput`
52
+ - Configuration for shaders, uniforms, canvas attributes/styles, and backbuffer
53
+
54
+ ### Methods
55
+
56
+ - `start()` - starts the render loop
57
+ - `stop()` - stops the render loop
58
+ - `dispose()` - releases resources and detaches observers/listeners
59
+
60
+ ## Options
61
+
62
+ ```ts
63
+ type UniformValue = number | number[] | WebGLTexture;
64
+ type UniformValueSetter = UniformValue | (() => UniformValue);
65
+
66
+ interface BackbufferOptionsInput {
67
+ enabled?: boolean;
68
+ uniformName?: string;
69
+ }
70
+
71
+ interface OptionsInput {
72
+ vertexShader?: string;
73
+ fragmentShader?: string;
74
+ canvas?: {
75
+ class?: string;
76
+ id?: string;
77
+ style?: Partial<CSSStyleDeclaration>;
78
+ };
79
+ uniforms?: Record<string, UniformValueSetter>;
80
+ backbuffer?: boolean | BackbufferOptionsInput;
81
+ }
82
+ ```
83
+
84
+ ### Option details
85
+
86
+ - `vertexShader` (`string`)
87
+ - Custom vertex shader source
88
+ - Default: built-in pass-through vertex shader (`attribute vec2 position`)
89
+
90
+ - `fragmentShader` (`string`)
91
+ - Fragment shader source
92
+ - Default: built-in solid red fragment shader
93
+
94
+ - `canvas.class` (`string`)
95
+ - Extra class name(s) added to the render canvas
96
+
97
+ - `canvas.id` (`string`)
98
+ - Optional `id` set on the canvas
99
+
100
+ - `canvas.style` (`Partial<CSSStyleDeclaration>`)
101
+ - Inline style object merged into canvas style
102
+
103
+ - `uniforms` (`Record<string, UniformValueSetter>`)
104
+ - Additional uniforms set each frame
105
+ - Value can be static or a function returning current value
106
+
107
+ - `backbuffer` (`boolean | { enabled?: boolean; uniformName?: string }`)
108
+ - Enables previous-frame texture capture
109
+ - `true` is equivalent to `{ enabled: true }`
110
+ - `uniformName` default: `"u_backbuffer"`
111
+
112
+ ## Built-in uniforms
113
+
114
+ These uniforms are updated automatically every frame:
115
+
116
+ - `u_time: float` - seconds since instance creation
117
+ - `u_resolution: vec2` - current canvas width/height in pixels
118
+ - `u_mouse: vec2` - pointer position normalized to `[0..1]`
119
+ - `u_scroll: vec2` - page scroll progress values
120
+
121
+ If backbuffer is enabled:
122
+
123
+ - `u_backbuffer` (or your custom `uniformName`) as a `sampler2D`
124
+
125
+ ## Examples
126
+
127
+ ### 1) Target by selector + custom canvas styling
128
+
129
+ ```ts
130
+ import { ShaderBg } from 'shader-bg';
131
+
132
+ const bg = new ShaderBg('#hero', {
133
+ fragmentShader: `
134
+ precision mediump float;
135
+ uniform float u_time;
136
+ uniform vec2 u_resolution;
137
+
138
+ void main() {
139
+ vec2 uv = gl_FragCoord.xy / u_resolution;
140
+ float v = 0.5 + 0.5 * sin(u_time + uv.x * 8.0);
141
+ gl_FragColor = vec4(vec3(v), 1.0);
142
+ }
143
+ `,
144
+ canvas: {
145
+ class: 'shader-bg-canvas',
146
+ id: 'hero-shader',
147
+ style: {
148
+ position: 'absolute',
149
+ inset: '0',
150
+ width: '100%',
151
+ height: '100%',
152
+ zIndex: '-1',
153
+ },
154
+ },
155
+ });
156
+
157
+ bg.start();
158
+ ```
159
+
160
+ ### 2) Dynamic custom uniforms
161
+
162
+ ```ts
163
+ import { ShaderBg } from 'shader-bg';
164
+
165
+ const bg = new ShaderBg(document.body, {
166
+ fragmentShader: `
167
+ precision mediump float;
168
+
169
+ uniform float u_time;
170
+ uniform vec2 u_resolution;
171
+ uniform vec3 u_color;
172
+ uniform float u_strength;
173
+
174
+ void main() {
175
+ vec2 uv = gl_FragCoord.xy / u_resolution;
176
+ float pulse = 0.5 + 0.5 * sin(u_time * 2.0);
177
+ gl_FragColor = vec4(u_color * pulse * u_strength, 1.0);
178
+ }
179
+ `,
180
+ uniforms: {
181
+ u_color: [0.2, 0.7, 1.0],
182
+ u_strength: () => 0.6 + 0.4 * Math.sin(performance.now() * 0.001),
183
+ },
184
+ });
185
+
186
+ bg.start();
187
+ ```
188
+
189
+ ### 3) Backbuffer feedback effect
190
+
191
+ ```ts
192
+ import { ShaderBg } from 'shader-bg';
193
+
194
+ const bg = new ShaderBg('#app', {
195
+ fragmentShader: `
196
+ precision mediump float;
197
+
198
+ uniform vec2 u_resolution;
199
+ uniform float u_time;
200
+ uniform sampler2D u_backbuffer;
201
+
202
+ void main() {
203
+ vec2 uv = gl_FragCoord.xy / u_resolution;
204
+ vec3 prev = texture2D(u_backbuffer, uv).rgb;
205
+ vec3 next = 0.98 * prev + 0.02 * vec3(
206
+ 0.5 + 0.5 * sin(u_time + uv.x * 10.0),
207
+ 0.5 + 0.5 * sin(u_time + uv.y * 10.0),
208
+ 0.5 + 0.5 * sin(u_time)
209
+ );
210
+ gl_FragColor = vec4(next, 1.0);
211
+ }
212
+ `,
213
+ backbuffer: true,
214
+ });
215
+
216
+ bg.start();
217
+ ```
218
+
219
+ Custom backbuffer uniform name:
220
+
221
+ ```ts
222
+ const bg = new ShaderBg('#app', {
223
+ fragmentShader: '...',
224
+ backbuffer: {
225
+ enabled: true,
226
+ uniformName: 'u_prevFrame',
227
+ },
228
+ });
229
+ ```
230
+
231
+ ## Notes
232
+
233
+ - Works in browsers with WebGL support (`canvas.getContext('webgl')`).
234
+ - Canvas size is synced from `canvas.clientWidth` / `canvas.clientHeight`.
235
+ - If you pass a container element, dispose removes only the auto-created canvas.
236
+ - For a minimal setup, pass `{}` as options and provide only what you need.
237
+
238
+ ## Development
239
+
240
+ ```bash
241
+ npm install
242
+ npm run dev
243
+ npm run build
244
+ ```
245
+
@@ -1,8 +1,8 @@
1
- import { Target } from './utils/Target.ts';
2
- import { OptionsInput } from './utils/Options.ts';
3
- import { GlProgram } from './GlProgram.ts';
4
- import { UniformManager } from './UniformManager.ts';
5
- import { Renderer } from './Renderer.ts';
1
+ import { Target } from './utils/Target';
2
+ import { OptionsInput } from './utils/Options';
3
+ import { GlProgram } from './GlProgram';
4
+ import { UniformManager } from './UniformManager';
5
+ import { Renderer } from './Renderer';
6
6
  export declare class ShaderBg {
7
7
  canvas: HTMLCanvasElement;
8
8
  private ownsCanvas;
package/dist/index.d.ts CHANGED
@@ -1 +1,3 @@
1
- export {};
1
+ export { ShaderBg } from './ShaderBg';
2
+ export type { OptionsInput, BackbufferOptionsInput, UniformValue, UniformValueSetter, } from './utils/Options';
3
+ export type { Target } from './utils/Target';
@@ -0,0 +1,9 @@
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});function e(e){if(typeof e==`string`){let t=document.querySelector(e);if(!t)throw Error(`Target element not found: ${e}`);return t}else if(e instanceof HTMLElement)return e;else if(e instanceof Element)return e;else throw Error(`Invalid target type`)}var t={vertexShader:`attribute vec2 position;
2
+ void main() {
3
+ gl_Position = vec4(position, 0.0, 1.0);
4
+ }`,fragmentShader:`precision mediump float;
5
+
6
+ void main() {
7
+ gl_FragColor = vec4(1.0,.0,.0, 1.0);
8
+ }`,canvas:{class:``,style:document.createElement(`canvas`).style},uniforms:{},backbuffer:{enabled:!1,uniformName:`u_backbuffer`}};function n(e={}){let n=e.backbuffer,r=typeof n==`boolean`?n:n?.enabled??t.backbuffer.enabled;return{vertexShader:e.vertexShader??t.vertexShader,fragmentShader:e.fragmentShader??t.fragmentShader,canvas:{class:e.canvas?.class??t.canvas.class,id:e.canvas?.id??t.canvas.id,style:{...t.canvas.style,...e.canvas?.style}},uniforms:{...t.uniforms,...e.uniforms},backbuffer:{enabled:r,uniformName:typeof n==`object`?n.uniformName??t.backbuffer.uniformName:t.backbuffer.uniformName}}}var r=class{gl;program;vertexShader;fragmentShader;constructor(e,t,n){this.gl=e,this.vertexShader=this.compile(e.VERTEX_SHADER,t),this.fragmentShader=this.compile(e.FRAGMENT_SHADER,n);let r=e.createProgram();if(!r)throw Error(`Failed to create WebGL program`);if(e.attachShader(r,this.vertexShader),e.attachShader(r,this.fragmentShader),e.linkProgram(r),!e.getProgramParameter(r,e.LINK_STATUS)){let t=e.getProgramInfoLog(r);throw Error(`Program linking failed:
9
+ `+t)}this.program=r}compile(e,t){let n=this.gl.createShader(e);if(!n)throw Error(`Failed to create shader`);if(this.gl.shaderSource(n,t),this.gl.compileShader(n),!this.gl.getShaderParameter(n,this.gl.COMPILE_STATUS)){let t=this.gl.getShaderInfoLog(n),r=e===this.gl.VERTEX_SHADER?`Vertex`:`Fragment`;throw Error(`${r} shader compilation failed:\n${t}`)}return n}use(){this.gl.useProgram(this.program)}dispose(){this.gl.deleteShader(this.vertexShader),this.gl.deleteShader(this.fragmentShader),this.gl.deleteProgram(this.program)}},i=class{gl;program;locations={};textureUnits={};nextTextureUnit=0;maxTextureUnits;constructor(e,t){this.gl=e,this.program=t,this.maxTextureUnits=e.getParameter(e.MAX_COMBINED_TEXTURE_IMAGE_UNITS)}getTextureUnit(e){let t=this.textureUnits[e];if(t!==void 0)return t;if(this.nextTextureUnit>=this.maxTextureUnits)throw Error(`Too many texture uniforms. Max supported is ${this.maxTextureUnits}.`);let n=this.nextTextureUnit;return this.textureUnits[e]=n,this.nextTextureUnit+=1,n}getLocation(e){let t=this.locations[e];if(!t){let n=this.gl.getUniformLocation(this.program,e);if(!n)return null;this.locations[e]=n,t=n}return t}set(e,t){let n=this.getLocation(e);if(typeof t==`number`){this.gl.uniform1f(n,t);return}if(Array.isArray(t)){switch(t.length){case 1:this.gl.uniform1f(n,t[0]);break;case 2:this.gl.uniform2fv(n,t);break;case 3:this.gl.uniform3fv(n,t);break;case 4:this.gl.uniform4fv(n,t);break;case 9:this.gl.uniformMatrix3fv(n,!1,t);break;case 16:this.gl.uniformMatrix4fv(n,!1,t);break;default:throw Error(`Unsupported uniform array length: ${t.length}`)}return}if(t instanceof WebGLTexture){let r=this.getTextureUnit(e);this.gl.activeTexture(this.gl.TEXTURE0+r),this.gl.bindTexture(this.gl.TEXTURE_2D,t),this.gl.uniform1i(n,r);return}throw Error(`Unsupported uniform value type`)}clear(){this.locations={},this.textureUnits={},this.nextTextureUnit=0}},a=class{gl;program;rafId=null;uniformsUpdate;afterRender;constructor(e,t,n,r){this.gl=e,this.program=t,this.uniformsUpdate=n,this.afterRender=r,this.setupBuffer()}setupBuffer(){let e=this.gl.createBuffer();this.gl.bindBuffer(this.gl.ARRAY_BUFFER,e),this.gl.bufferData(this.gl.ARRAY_BUFFER,new Float32Array([-1,-1,1,-1,-1,1,-1,1,1,-1,1,1]),this.gl.STATIC_DRAW);let t=this.gl.getAttribLocation(this.program,`position`);this.gl.enableVertexAttribArray(t),this.gl.vertexAttribPointer(t,2,this.gl.FLOAT,!1,0,0)}render=()=>{this.uniformsUpdate&&this.uniformsUpdate(),this.gl.clear(this.gl.COLOR_BUFFER_BIT),this.gl.drawArrays(this.gl.TRIANGLES,0,6),this.afterRender&&this.afterRender(),this.rafId=requestAnimationFrame(this.render)};start(){this.rafId||=requestAnimationFrame(this.render)}stop(){this.rafId!==null&&(cancelAnimationFrame(this.rafId),this.rafId=null)}},o=class e{canvas;static observer;static observerMap=new WeakMap;constructor(t,n){e.observer||e.init(),e.observerMap.set(t,n),e.observer.observe(t),this.canvas=t}static init(){this.observer=new ResizeObserver(e=>{for(let t of e){if(!this.observerMap.has(t.target))continue;let e=this.observerMap.get(t.target);e&&e()}})}dispose(){e.observer.unobserve(this.canvas)}},s=class{gl;canvas;texture;enabled=!1;constructor(e,t,n){this.gl=e,this.canvas=t,this.enabled=n}init(){if(!this.enabled)return;let e=this.gl.createTexture();if(!e)throw Error(`Failed to create backbuffer texture`);this.texture=e,this.gl.bindTexture(this.gl.TEXTURE_2D,e),this.gl.texParameteri(this.gl.TEXTURE_2D,this.gl.TEXTURE_MIN_FILTER,this.gl.LINEAR),this.gl.texParameteri(this.gl.TEXTURE_2D,this.gl.TEXTURE_MAG_FILTER,this.gl.LINEAR),this.gl.texParameteri(this.gl.TEXTURE_2D,this.gl.TEXTURE_WRAP_S,this.gl.CLAMP_TO_EDGE),this.gl.texParameteri(this.gl.TEXTURE_2D,this.gl.TEXTURE_WRAP_T,this.gl.CLAMP_TO_EDGE)}resize(){this.enabled&&this.texture&&(this.gl.bindTexture(this.gl.TEXTURE_2D,this.texture),this.gl.texImage2D(this.gl.TEXTURE_2D,0,this.gl.RGBA,Math.max(1,this.canvas.width),Math.max(1,this.canvas.height),0,this.gl.RGBA,this.gl.UNSIGNED_BYTE,null))}capture(){this.enabled&&(!this.texture||this.canvas.width===0||this.canvas.height===0||(this.gl.bindTexture(this.gl.TEXTURE_2D,this.texture),this.gl.copyTexSubImage2D(this.gl.TEXTURE_2D,0,0,0,0,0,this.canvas.width,this.canvas.height)))}dispose(){this.enabled&&this.texture&&this.gl.deleteTexture(this.texture)}},c=class{canvas;ownsCanvas=!1;options;gl;program;uniforms;renderer;resizeManager;backBufferManager;mousePos=[0,0];startTime=performance.now();setup_canvas(e){e instanceof HTMLCanvasElement?(this.canvas=e,this.ownsCanvas=!1):(this.canvas=document.createElement(`canvas`),e.appendChild(this.canvas),this.ownsCanvas=!0);let t=this.canvas.getAttribute(`class`)||``;this.canvas.setAttribute(`class`,`${t} ${this.options.canvas.class}`.trim()),this.options.canvas.id&&this.canvas.setAttribute(`id`,this.options.canvas.id),this.canvas.style={...this.canvas.style,...this.options.canvas.style}}constructor(t,c){this.options=n(c);let l=e(t);if(!l)throw Error(`Target element not found: ${t}`);this.setup_canvas(l);let u=this.canvas.getContext(`webgl`);if(!u)throw Error(`WebGL not supported`);this.gl=u,this.program=new r(this.gl,this.options.vertexShader,this.options.fragmentShader),this.program.use(),this.uniforms=new i(this.gl,this.program.program),this.renderer=new a(this.gl,this.program.program,()=>this.updateCommonUniforms(),()=>this.backBufferManager.capture()),this.resizeManager=new o(this.canvas,()=>this.resize()),this.backBufferManager=new s(this.gl,this.canvas,this.options.backbuffer.enabled),this.backBufferManager.init(),this.canvas.addEventListener(`pointermove`,this.mouse_move.bind(this))}mouse_move(e){let t=this.canvas.getBoundingClientRect();this.mousePos=[(e.clientX-t.left)/t.width,1-(e.clientY-t.top)/t.height]}updateCommonUniforms(){let e=(performance.now()-this.startTime)/1e3,t=this.canvas.width,n=this.canvas.height;this.uniforms.set(`u_time`,e),this.uniforms.set(`u_resolution`,[t,n]),this.uniforms.set(`u_mouse`,this.mousePos),this.uniforms.set(`u_scroll`,[window.scrollX/(document.documentElement.scrollWidth-window.innerWidth),window.scrollY/(document.documentElement.scrollTop-window.innerHeight)]),this.backBufferManager.enabled&&this.uniforms.set(this.options.backbuffer.uniformName,this.backBufferManager.texture);for(let[e,t]of Object.entries(this.options.uniforms)){let n=t;typeof t==`function`&&(n=t()),this.uniforms.set(e,n)}}start(){this.resize(),this.renderer.start()}stop(){this.renderer.stop()}resize(){this.canvas.width=this.canvas.clientWidth,this.canvas.height=this.canvas.clientHeight,this.gl.viewport(0,0,this.canvas.width,this.canvas.height),this.backBufferManager.resize()}dispose(){this.stop(),this.program.dispose(),this.uniforms.clear(),this.resizeManager.dispose(),this.backBufferManager.dispose(),this.canvas.removeEventListener(`pointermove`,this.mouse_move.bind(this)),this.ownsCanvas&&this.canvas.remove()}};exports.ShaderBg=c;
package/dist/shader-bg.js CHANGED
@@ -0,0 +1,281 @@
1
+ //#region src/utils/Target.ts
2
+ function e(e) {
3
+ if (typeof e == "string") {
4
+ let t = document.querySelector(e);
5
+ if (!t) throw Error(`Target element not found: ${e}`);
6
+ return t;
7
+ } else if (e instanceof HTMLElement) return e;
8
+ else if (e instanceof Element) return e;
9
+ else throw Error("Invalid target type");
10
+ }
11
+ //#endregion
12
+ //#region src/utils/Options.ts
13
+ var t = {
14
+ vertexShader: "attribute vec2 position;\nvoid main() {\n gl_Position = vec4(position, 0.0, 1.0);\n}",
15
+ fragmentShader: "precision mediump float;\n\nvoid main() {\n gl_FragColor = vec4(1.0,.0,.0, 1.0);\n}",
16
+ canvas: {
17
+ class: "",
18
+ style: document.createElement("canvas").style
19
+ },
20
+ uniforms: {},
21
+ backbuffer: {
22
+ enabled: !1,
23
+ uniformName: "u_backbuffer"
24
+ }
25
+ };
26
+ function n(e = {}) {
27
+ let n = e.backbuffer, r = typeof n == "boolean" ? n : n?.enabled ?? t.backbuffer.enabled;
28
+ return {
29
+ vertexShader: e.vertexShader ?? t.vertexShader,
30
+ fragmentShader: e.fragmentShader ?? t.fragmentShader,
31
+ canvas: {
32
+ class: e.canvas?.class ?? t.canvas.class,
33
+ id: e.canvas?.id ?? t.canvas.id,
34
+ style: {
35
+ ...t.canvas.style,
36
+ ...e.canvas?.style
37
+ }
38
+ },
39
+ uniforms: {
40
+ ...t.uniforms,
41
+ ...e.uniforms
42
+ },
43
+ backbuffer: {
44
+ enabled: r,
45
+ uniformName: typeof n == "object" ? n.uniformName ?? t.backbuffer.uniformName : t.backbuffer.uniformName
46
+ }
47
+ };
48
+ }
49
+ //#endregion
50
+ //#region src/GlProgram.ts
51
+ var r = class {
52
+ gl;
53
+ program;
54
+ vertexShader;
55
+ fragmentShader;
56
+ constructor(e, t, n) {
57
+ this.gl = e, this.vertexShader = this.compile(e.VERTEX_SHADER, t), this.fragmentShader = this.compile(e.FRAGMENT_SHADER, n);
58
+ let r = e.createProgram();
59
+ if (!r) throw Error("Failed to create WebGL program");
60
+ if (e.attachShader(r, this.vertexShader), e.attachShader(r, this.fragmentShader), e.linkProgram(r), !e.getProgramParameter(r, e.LINK_STATUS)) {
61
+ let t = e.getProgramInfoLog(r);
62
+ throw Error("Program linking failed:\n" + t);
63
+ }
64
+ this.program = r;
65
+ }
66
+ compile(e, t) {
67
+ let n = this.gl.createShader(e);
68
+ if (!n) throw Error("Failed to create shader");
69
+ if (this.gl.shaderSource(n, t), this.gl.compileShader(n), !this.gl.getShaderParameter(n, this.gl.COMPILE_STATUS)) {
70
+ let t = this.gl.getShaderInfoLog(n), r = e === this.gl.VERTEX_SHADER ? "Vertex" : "Fragment";
71
+ throw Error(`${r} shader compilation failed:\n${t}`);
72
+ }
73
+ return n;
74
+ }
75
+ use() {
76
+ this.gl.useProgram(this.program);
77
+ }
78
+ dispose() {
79
+ this.gl.deleteShader(this.vertexShader), this.gl.deleteShader(this.fragmentShader), this.gl.deleteProgram(this.program);
80
+ }
81
+ }, i = class {
82
+ gl;
83
+ program;
84
+ locations = {};
85
+ textureUnits = {};
86
+ nextTextureUnit = 0;
87
+ maxTextureUnits;
88
+ constructor(e, t) {
89
+ this.gl = e, this.program = t, this.maxTextureUnits = e.getParameter(e.MAX_COMBINED_TEXTURE_IMAGE_UNITS);
90
+ }
91
+ getTextureUnit(e) {
92
+ let t = this.textureUnits[e];
93
+ if (t !== void 0) return t;
94
+ if (this.nextTextureUnit >= this.maxTextureUnits) throw Error(`Too many texture uniforms. Max supported is ${this.maxTextureUnits}.`);
95
+ let n = this.nextTextureUnit;
96
+ return this.textureUnits[e] = n, this.nextTextureUnit += 1, n;
97
+ }
98
+ getLocation(e) {
99
+ let t = this.locations[e];
100
+ if (!t) {
101
+ let n = this.gl.getUniformLocation(this.program, e);
102
+ if (!n) return null;
103
+ this.locations[e] = n, t = n;
104
+ }
105
+ return t;
106
+ }
107
+ set(e, t) {
108
+ let n = this.getLocation(e);
109
+ if (typeof t == "number") {
110
+ this.gl.uniform1f(n, t);
111
+ return;
112
+ }
113
+ if (Array.isArray(t)) {
114
+ switch (t.length) {
115
+ case 1:
116
+ this.gl.uniform1f(n, t[0]);
117
+ break;
118
+ case 2:
119
+ this.gl.uniform2fv(n, t);
120
+ break;
121
+ case 3:
122
+ this.gl.uniform3fv(n, t);
123
+ break;
124
+ case 4:
125
+ this.gl.uniform4fv(n, t);
126
+ break;
127
+ case 9:
128
+ this.gl.uniformMatrix3fv(n, !1, t);
129
+ break;
130
+ case 16:
131
+ this.gl.uniformMatrix4fv(n, !1, t);
132
+ break;
133
+ default: throw Error(`Unsupported uniform array length: ${t.length}`);
134
+ }
135
+ return;
136
+ }
137
+ if (t instanceof WebGLTexture) {
138
+ let r = this.getTextureUnit(e);
139
+ this.gl.activeTexture(this.gl.TEXTURE0 + r), this.gl.bindTexture(this.gl.TEXTURE_2D, t), this.gl.uniform1i(n, r);
140
+ return;
141
+ }
142
+ throw Error("Unsupported uniform value type");
143
+ }
144
+ clear() {
145
+ this.locations = {}, this.textureUnits = {}, this.nextTextureUnit = 0;
146
+ }
147
+ }, a = class {
148
+ gl;
149
+ program;
150
+ rafId = null;
151
+ uniformsUpdate;
152
+ afterRender;
153
+ constructor(e, t, n, r) {
154
+ this.gl = e, this.program = t, this.uniformsUpdate = n, this.afterRender = r, this.setupBuffer();
155
+ }
156
+ setupBuffer() {
157
+ let e = this.gl.createBuffer();
158
+ this.gl.bindBuffer(this.gl.ARRAY_BUFFER, e), this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array([
159
+ -1,
160
+ -1,
161
+ 1,
162
+ -1,
163
+ -1,
164
+ 1,
165
+ -1,
166
+ 1,
167
+ 1,
168
+ -1,
169
+ 1,
170
+ 1
171
+ ]), this.gl.STATIC_DRAW);
172
+ let t = this.gl.getAttribLocation(this.program, "position");
173
+ this.gl.enableVertexAttribArray(t), this.gl.vertexAttribPointer(t, 2, this.gl.FLOAT, !1, 0, 0);
174
+ }
175
+ render = () => {
176
+ this.uniformsUpdate && this.uniformsUpdate(), this.gl.clear(this.gl.COLOR_BUFFER_BIT), this.gl.drawArrays(this.gl.TRIANGLES, 0, 6), this.afterRender && this.afterRender(), this.rafId = requestAnimationFrame(this.render);
177
+ };
178
+ start() {
179
+ this.rafId ||= requestAnimationFrame(this.render);
180
+ }
181
+ stop() {
182
+ this.rafId !== null && (cancelAnimationFrame(this.rafId), this.rafId = null);
183
+ }
184
+ }, o = class e {
185
+ canvas;
186
+ static observer;
187
+ static observerMap = /* @__PURE__ */ new WeakMap();
188
+ constructor(t, n) {
189
+ e.observer || e.init(), e.observerMap.set(t, n), e.observer.observe(t), this.canvas = t;
190
+ }
191
+ static init() {
192
+ this.observer = new ResizeObserver((e) => {
193
+ for (let t of e) {
194
+ if (!this.observerMap.has(t.target)) continue;
195
+ let e = this.observerMap.get(t.target);
196
+ e && e();
197
+ }
198
+ });
199
+ }
200
+ dispose() {
201
+ e.observer.unobserve(this.canvas);
202
+ }
203
+ }, s = class {
204
+ gl;
205
+ canvas;
206
+ texture;
207
+ enabled = !1;
208
+ constructor(e, t, n) {
209
+ this.gl = e, this.canvas = t, this.enabled = n;
210
+ }
211
+ init() {
212
+ if (!this.enabled) return;
213
+ let e = this.gl.createTexture();
214
+ if (!e) throw Error("Failed to create backbuffer texture");
215
+ this.texture = e, this.gl.bindTexture(this.gl.TEXTURE_2D, e), this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MIN_FILTER, this.gl.LINEAR), this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MAG_FILTER, this.gl.LINEAR), this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_S, this.gl.CLAMP_TO_EDGE), this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_T, this.gl.CLAMP_TO_EDGE);
216
+ }
217
+ resize() {
218
+ this.enabled && this.texture && (this.gl.bindTexture(this.gl.TEXTURE_2D, this.texture), this.gl.texImage2D(this.gl.TEXTURE_2D, 0, this.gl.RGBA, Math.max(1, this.canvas.width), Math.max(1, this.canvas.height), 0, this.gl.RGBA, this.gl.UNSIGNED_BYTE, null));
219
+ }
220
+ capture() {
221
+ this.enabled && (!this.texture || this.canvas.width === 0 || this.canvas.height === 0 || (this.gl.bindTexture(this.gl.TEXTURE_2D, this.texture), this.gl.copyTexSubImage2D(this.gl.TEXTURE_2D, 0, 0, 0, 0, 0, this.canvas.width, this.canvas.height)));
222
+ }
223
+ dispose() {
224
+ this.enabled && this.texture && this.gl.deleteTexture(this.texture);
225
+ }
226
+ }, c = class {
227
+ canvas;
228
+ ownsCanvas = !1;
229
+ options;
230
+ gl;
231
+ program;
232
+ uniforms;
233
+ renderer;
234
+ resizeManager;
235
+ backBufferManager;
236
+ mousePos = [0, 0];
237
+ startTime = performance.now();
238
+ setup_canvas(e) {
239
+ e instanceof HTMLCanvasElement ? (this.canvas = e, this.ownsCanvas = !1) : (this.canvas = document.createElement("canvas"), e.appendChild(this.canvas), this.ownsCanvas = !0);
240
+ let t = this.canvas.getAttribute("class") || "";
241
+ this.canvas.setAttribute("class", `${t} ${this.options.canvas.class}`.trim()), this.options.canvas.id && this.canvas.setAttribute("id", this.options.canvas.id), this.canvas.style = {
242
+ ...this.canvas.style,
243
+ ...this.options.canvas.style
244
+ };
245
+ }
246
+ constructor(t, c) {
247
+ this.options = n(c);
248
+ let l = e(t);
249
+ if (!l) throw Error(`Target element not found: ${t}`);
250
+ this.setup_canvas(l);
251
+ let u = this.canvas.getContext("webgl");
252
+ if (!u) throw Error("WebGL not supported");
253
+ this.gl = u, this.program = new r(this.gl, this.options.vertexShader, this.options.fragmentShader), this.program.use(), this.uniforms = new i(this.gl, this.program.program), this.renderer = new a(this.gl, this.program.program, () => this.updateCommonUniforms(), () => this.backBufferManager.capture()), this.resizeManager = new o(this.canvas, () => this.resize()), this.backBufferManager = new s(this.gl, this.canvas, this.options.backbuffer.enabled), this.backBufferManager.init(), this.canvas.addEventListener("pointermove", this.mouse_move.bind(this));
254
+ }
255
+ mouse_move(e) {
256
+ let t = this.canvas.getBoundingClientRect();
257
+ this.mousePos = [(e.clientX - t.left) / t.width, 1 - (e.clientY - t.top) / t.height];
258
+ }
259
+ updateCommonUniforms() {
260
+ let e = (performance.now() - this.startTime) / 1e3, t = this.canvas.width, n = this.canvas.height;
261
+ this.uniforms.set("u_time", e), this.uniforms.set("u_resolution", [t, n]), this.uniforms.set("u_mouse", this.mousePos), this.uniforms.set("u_scroll", [window.scrollX / (document.documentElement.scrollWidth - window.innerWidth), window.scrollY / (document.documentElement.scrollTop - window.innerHeight)]), this.backBufferManager.enabled && this.uniforms.set(this.options.backbuffer.uniformName, this.backBufferManager.texture);
262
+ for (let [e, t] of Object.entries(this.options.uniforms)) {
263
+ let n = t;
264
+ typeof t == "function" && (n = t()), this.uniforms.set(e, n);
265
+ }
266
+ }
267
+ start() {
268
+ this.resize(), this.renderer.start();
269
+ }
270
+ stop() {
271
+ this.renderer.stop();
272
+ }
273
+ resize() {
274
+ this.canvas.width = this.canvas.clientWidth, this.canvas.height = this.canvas.clientHeight, this.gl.viewport(0, 0, this.canvas.width, this.canvas.height), this.backBufferManager.resize();
275
+ }
276
+ dispose() {
277
+ this.stop(), this.program.dispose(), this.uniforms.clear(), this.resizeManager.dispose(), this.backBufferManager.dispose(), this.canvas.removeEventListener("pointermove", this.mouse_move.bind(this)), this.ownsCanvas && this.canvas.remove();
278
+ }
279
+ };
280
+ //#endregion
281
+ export { c as ShaderBg };
package/package.json CHANGED
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "name": "shader-bg",
3
- "version": "0.0.0",
3
+ "version": "0.0.2",
4
4
  "type": "module",
5
5
  "main": "./dist/shader-bg.cjs",
6
6
  "module": "./dist/shader-bg.js",
7
- "types": "./dist/shader-bg.d.ts",
7
+ "types": "./dist/index.d.ts",
8
8
  "exports": {
9
9
  ".": {
10
10
  "import": "./dist/shader-bg.js",
11
11
  "require": "./dist/shader-bg.cjs",
12
- "types": "./dist/shader-bg.d.ts"
12
+ "types": "./dist/index.d.ts"
13
13
  }
14
14
  },
15
15
  "files": [