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 +245 -0
- package/dist/ShaderBg.d.ts +5 -5
- package/dist/index.d.ts +3 -1
- package/dist/shader-bg.cjs +9 -0
- package/dist/shader-bg.js +281 -0
- package/package.json +3 -3
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
|
+
|
package/dist/ShaderBg.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
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
|
|
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
package/dist/shader-bg.cjs
CHANGED
|
@@ -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.
|
|
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/
|
|
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/
|
|
12
|
+
"types": "./dist/index.d.ts"
|
|
13
13
|
}
|
|
14
14
|
},
|
|
15
15
|
"files": [
|