aboutsummaryrefslogtreecommitdiff
path: root/packages/lib/glass-effect-manager.ts
diff options
context:
space:
mode:
Diffstat (limited to 'packages/lib/glass-effect-manager.ts')
-rw-r--r--packages/lib/glass-effect-manager.ts196
1 files changed, 98 insertions, 98 deletions
diff --git a/packages/lib/glass-effect-manager.ts b/packages/lib/glass-effect-manager.ts
index 291a30aa..03a79734 100644
--- a/packages/lib/glass-effect-manager.ts
+++ b/packages/lib/glass-effect-manager.ts
@@ -1,56 +1,56 @@
// Singleton WebGL context manager for glass effects
class GlassEffectManager {
- private static instance: GlassEffectManager | null = null
- private canvas: HTMLCanvasElement | null = null
- private gl: WebGLRenderingContext | null = null
- private program: WebGLProgram | null = null
- private uniforms: Record<string, WebGLUniformLocation | null> = {}
- private effects: Map<string, EffectInstance> = new Map()
- private animationFrame: number | null = null
- private startTime: number = performance.now()
- private mousePositions: Map<string, { x: number; y: number }> = new Map()
+ private static instance: GlassEffectManager | null = null;
+ private canvas: HTMLCanvasElement | null = null;
+ private gl: WebGLRenderingContext | null = null;
+ private program: WebGLProgram | null = null;
+ private uniforms: Record<string, WebGLUniformLocation | null> = {};
+ private effects: Map<string, EffectInstance> = new Map();
+ private animationFrame: number | null = null;
+ private startTime: number = performance.now();
+ private mousePositions: Map<string, { x: number; y: number }> = new Map();
static getInstance(): GlassEffectManager {
if (!GlassEffectManager.instance) {
- GlassEffectManager.instance = new GlassEffectManager()
+ GlassEffectManager.instance = new GlassEffectManager();
}
- return GlassEffectManager.instance
+ return GlassEffectManager.instance;
}
private constructor() {
- this.initializeContext()
+ this.initializeContext();
}
private initializeContext() {
// Create offscreen canvas
- this.canvas = document.createElement("canvas")
- this.canvas.width = 1024 // Default size, will be adjusted
- this.canvas.height = 1024
+ this.canvas = document.createElement("canvas");
+ this.canvas.width = 1024; // Default size, will be adjusted
+ this.canvas.height = 1024;
this.gl = this.canvas.getContext("webgl", {
alpha: true,
premultipliedAlpha: false,
preserveDrawingBuffer: true,
- })
+ });
if (!this.gl) {
- console.error("WebGL not supported")
- return
+ console.error("WebGL not supported");
+ return;
}
- this.setupShaders()
- this.startRenderLoop()
+ this.setupShaders();
+ this.startRenderLoop();
}
private setupShaders() {
- if (!this.gl) return
+ if (!this.gl) return;
const vsSource = `
attribute vec2 position;
void main() {
gl_Position = vec4(position, 0.0, 1.0);
}
- `
+ `;
const fsSource = `
precision mediump float;
@@ -106,48 +106,48 @@ class GlassEffectManager {
gl_FragColor = vec4(glassColor, alpha);
}
- `
+ `;
const createShader = (type: number, source: string) => {
- const shader = this.gl!.createShader(type)
- if (!shader) return null
+ const shader = this.gl!.createShader(type);
+ if (!shader) return null;
- this.gl!.shaderSource(shader, source)
- this.gl!.compileShader(shader)
+ this.gl!.shaderSource(shader, source);
+ this.gl!.compileShader(shader);
if (!this.gl!.getShaderParameter(shader, this.gl!.COMPILE_STATUS)) {
- console.error("Shader error:", this.gl!.getShaderInfoLog(shader))
- this.gl!.deleteShader(shader)
- return null
+ console.error("Shader error:", this.gl!.getShaderInfoLog(shader));
+ this.gl!.deleteShader(shader);
+ return null;
}
- return shader
- }
+ return shader;
+ };
- const vs = createShader(this.gl.VERTEX_SHADER, vsSource)
- const fs = createShader(this.gl.FRAGMENT_SHADER, fsSource)
- if (!vs || !fs) return
+ const vs = createShader(this.gl.VERTEX_SHADER, vsSource);
+ const fs = createShader(this.gl.FRAGMENT_SHADER, fsSource);
+ if (!vs || !fs) return;
- this.program = this.gl.createProgram()
- if (!this.program) return
+ this.program = this.gl.createProgram();
+ if (!this.program) return;
- this.gl.attachShader(this.program, vs)
- this.gl.attachShader(this.program, fs)
- this.gl.linkProgram(this.program)
+ this.gl.attachShader(this.program, vs);
+ this.gl.attachShader(this.program, fs);
+ this.gl.linkProgram(this.program);
// biome-ignore lint/correctness/useHookAtTopLevel: Well, not a hook
- this.gl.useProgram(this.program)
+ this.gl.useProgram(this.program);
// Buffer setup
- const buffer = this.gl.createBuffer()
- this.gl.bindBuffer(this.gl.ARRAY_BUFFER, buffer)
+ const buffer = this.gl.createBuffer();
+ this.gl.bindBuffer(this.gl.ARRAY_BUFFER, buffer);
this.gl.bufferData(
this.gl.ARRAY_BUFFER,
new Float32Array([-1, -1, 1, -1, -1, 1, 1, 1]),
this.gl.STATIC_DRAW,
- )
+ );
- const position = this.gl.getAttribLocation(this.program, "position")
- this.gl.enableVertexAttribArray(position)
- this.gl.vertexAttribPointer(position, 2, this.gl.FLOAT, false, 0, 0)
+ const position = this.gl.getAttribLocation(this.program, "position");
+ this.gl.enableVertexAttribArray(position);
+ this.gl.vertexAttribPointer(position, 2, this.gl.FLOAT, false, 0, 0);
// Store uniform locations
this.uniforms = {
@@ -155,11 +155,11 @@ class GlassEffectManager {
time: this.gl.getUniformLocation(this.program, "iTime"),
mouse: this.gl.getUniformLocation(this.program, "iMouse"),
expanded: this.gl.getUniformLocation(this.program, "iExpanded"),
- }
+ };
// Enable blending
- this.gl.enable(this.gl.BLEND)
- this.gl.blendFunc(this.gl.SRC_ALPHA, this.gl.ONE_MINUS_SRC_ALPHA)
+ this.gl.enable(this.gl.BLEND);
+ this.gl.blendFunc(this.gl.SRC_ALPHA, this.gl.ONE_MINUS_SRC_ALPHA);
}
registerEffect(
@@ -168,8 +168,8 @@ class GlassEffectManager {
isExpanded: boolean,
): () => void {
// Ensure minimum dimensions
- const width = Math.max(1, targetCanvas.width)
- const height = Math.max(1, targetCanvas.height)
+ const width = Math.max(1, targetCanvas.width);
+ const height = Math.max(1, targetCanvas.height);
const effect: EffectInstance = {
id,
@@ -177,58 +177,58 @@ class GlassEffectManager {
isExpanded,
width,
height,
- }
+ };
- this.effects.set(id, effect)
- this.mousePositions.set(id, { x: 0, y: 0 })
+ this.effects.set(id, effect);
+ this.mousePositions.set(id, { x: 0, y: 0 });
// Return cleanup function
return () => {
- this.effects.delete(id)
- this.mousePositions.delete(id)
+ this.effects.delete(id);
+ this.mousePositions.delete(id);
if (this.effects.size === 0 && this.animationFrame) {
- cancelAnimationFrame(this.animationFrame)
- this.animationFrame = null
+ cancelAnimationFrame(this.animationFrame);
+ this.animationFrame = null;
}
- }
+ };
}
updateMousePosition(id: string, x: number, y: number) {
- this.mousePositions.set(id, { x, y })
+ this.mousePositions.set(id, { x, y });
}
updateExpanded(id: string, isExpanded: boolean) {
- const effect = this.effects.get(id)
+ const effect = this.effects.get(id);
if (effect) {
- effect.isExpanded = isExpanded
+ effect.isExpanded = isExpanded;
}
}
updateSize(id: string, width: number, height: number) {
- const effect = this.effects.get(id)
+ const effect = this.effects.get(id);
if (effect) {
// Ensure minimum dimensions
- effect.width = Math.max(1, width)
- effect.height = Math.max(1, height)
+ effect.width = Math.max(1, width);
+ effect.height = Math.max(1, height);
}
}
private startRenderLoop() {
const render = () => {
if (!this.gl || !this.program || this.effects.size === 0) {
- this.animationFrame = requestAnimationFrame(render)
- return
+ this.animationFrame = requestAnimationFrame(render);
+ return;
}
- const currentTime = (performance.now() - this.startTime) / 1000
+ const currentTime = (performance.now() - this.startTime) / 1000;
// Render each effect
for (const [id, effect] of Array.from(this.effects)) {
- const mousePos = this.mousePositions.get(id) || { x: 0, y: 0 }
+ const mousePos = this.mousePositions.get(id) || { x: 0, y: 0 };
// Skip rendering if dimensions are invalid
if (effect.width <= 0 || effect.height <= 0) {
- continue
+ continue;
}
// Set canvas size if needed
@@ -236,14 +236,14 @@ class GlassEffectManager {
this.canvas!.width !== effect.width ||
this.canvas!.height !== effect.height
) {
- this.canvas!.width = effect.width
- this.canvas!.height = effect.height
- this.gl.viewport(0, 0, effect.width, effect.height)
+ this.canvas!.width = effect.width;
+ this.canvas!.height = effect.height;
+ this.gl.viewport(0, 0, effect.width, effect.height);
}
// Clear and render
- this.gl.clearColor(0, 0, 0, 0)
- this.gl.clear(this.gl.COLOR_BUFFER_BIT)
+ this.gl.clearColor(0, 0, 0, 0);
+ this.gl.clear(this.gl.COLOR_BUFFER_BIT);
// Set uniforms
if (this.uniforms.resolution) {
@@ -251,58 +251,58 @@ class GlassEffectManager {
this.uniforms.resolution,
effect.width,
effect.height,
- )
+ );
}
if (this.uniforms.time) {
- this.gl.uniform1f(this.uniforms.time, currentTime)
+ this.gl.uniform1f(this.uniforms.time, currentTime);
}
if (this.uniforms.mouse) {
- this.gl.uniform2f(this.uniforms.mouse, mousePos.x, mousePos.y)
+ this.gl.uniform2f(this.uniforms.mouse, mousePos.x, mousePos.y);
}
if (this.uniforms.expanded) {
this.gl.uniform1f(
this.uniforms.expanded,
effect.isExpanded ? 1.0 : 0.0,
- )
+ );
}
// Draw
- this.gl.drawArrays(this.gl.TRIANGLE_STRIP, 0, 4)
+ this.gl.drawArrays(this.gl.TRIANGLE_STRIP, 0, 4);
// Copy to target canvas
- const targetCtx = effect.targetCanvas.getContext("2d")
+ const targetCtx = effect.targetCanvas.getContext("2d");
if (targetCtx) {
- targetCtx.clearRect(0, 0, effect.width, effect.height)
- targetCtx.drawImage(this.canvas!, 0, 0)
+ targetCtx.clearRect(0, 0, effect.width, effect.height);
+ targetCtx.drawImage(this.canvas!, 0, 0);
}
}
- this.animationFrame = requestAnimationFrame(render)
- }
+ this.animationFrame = requestAnimationFrame(render);
+ };
- render()
+ render();
}
// Clean up method (optional, for when the app unmounts)
destroy() {
if (this.animationFrame) {
- cancelAnimationFrame(this.animationFrame)
+ cancelAnimationFrame(this.animationFrame);
}
if (this.gl && this.program) {
- this.gl.deleteProgram(this.program)
+ this.gl.deleteProgram(this.program);
}
- this.effects.clear()
- this.mousePositions.clear()
- GlassEffectManager.instance = null
+ this.effects.clear();
+ this.mousePositions.clear();
+ GlassEffectManager.instance = null;
}
}
interface EffectInstance {
- id: string
- targetCanvas: HTMLCanvasElement
- isExpanded: boolean
- width: number
- height: number
+ id: string;
+ targetCanvas: HTMLCanvasElement;
+ isExpanded: boolean;
+ width: number;
+ height: number;
}
-export default GlassEffectManager
+export default GlassEffectManager;