Skip to content

Commit 3e9ce1c

Browse files
committed
Use absolute Pages URLs in manifest url + schema $id
1 parent 80a6937 commit 3e9ce1c

104 files changed

Lines changed: 202 additions & 202 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

build/build.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ export function buildSite() {
175175
const meta = mod.meta;
176176
const { snippet, mountSnippet } = buildSnippet(mod);
177177
const source = sourceOf(mod);
178-
const url = `/effects/${id}.md`;
178+
const url = `${HOMEPAGE}/effects/${id}.md`;
179179

180180
// self-contained per-effect JSON
181181
w(`effects/${id}.json`, JSON.stringify(

schema/effect.schema.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"$schema": "http://json-schema.org/draft-07/schema#",
3-
"$id": "https://fx-lab.example/schema/effect.schema.json",
3+
"$id": "https://rosuh.github.io/fx-lab/schema/effect.schema.json",
44
"title": "FX Lab effect meta",
55
"description": "The co-located meta object on every src/effects/<kind>/<id>.js module (CONTRACT 1). Derived fields (source/snippet/mountSnippet/runtimeNotes/url/preview) are NOT part of this schema.",
66
"type": "object",

schema/manifest.schema.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"$schema": "http://json-schema.org/draft-07/schema#",
3-
"$id": "https://fx-lab.example/schema/manifest.schema.json",
3+
"$id": "https://rosuh.github.io/fx-lab/schema/manifest.schema.json",
44
"title": "FX Lab fx-index.json manifest",
55
"type": "object",
66
"required": ["name", "version", "homepage", "sections", "effects"],

site/effects/aboriginal.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,6 @@
4141
"snippet": "<!-- fx-lab effect: aboriginal — Dot Painting (canvas) · license MIT\n accuracy: Procedural approximation of Aboriginal Australian dot painting; not a faithful reproduction of any specific historical motif. -->\n<canvas id=\"fx-aboriginal\" style=\"display:block;width:100%;height:100%;min-height:240px\"></canvas>\n<script type=\"module\">\nconst draw = function aboriginal(ctx,w,h,t,mx,my,s){ if(!s.init){s.init=1;s.c=[];for(let i=0;i<6;i++)s.c.push({x:Math.random()*w,y:Math.random()*h});} ctx.fillStyle='#2a1a0f';ctx.fillRect(0,0,w,h); const cols=['#e8b04b','#d9763a','#ece0cf','#b5472a','#7a3b1f'];\n for(const c of s.c){ for(let ring=0;ring<6;ring++){ const rad=7+ring*9; const n=Math.max(6,Math.floor(rad*0.7)); for(let i=0;i<n;i++){ const a=i/n*6.2831+ring*0.3+t*0.12*(ring%2?1:-1); ctx.fillStyle=cols[(ring+i)%cols.length]; ctx.beginPath();ctx.arc(c.x+Math.cos(a)*rad,c.y+Math.sin(a)*rad,2.0,0,7);ctx.fill(); } } } };\nconst cv = document.getElementById('fx-aboriginal');\nconst ctx = cv.getContext('2d');\nconst DPR = Math.min(2, window.devicePixelRatio || 1);\nconst state = {};\nlet mx = -9999, my = -9999; // -9999 == not hovering (CONTRACT 2)\ncv.addEventListener('pointermove', (e) => {\n const r = cv.getBoundingClientRect(); mx = e.clientX - r.left; my = e.clientY - r.top;\n});\ncv.addEventListener('pointerleave', () => { mx = -9999; my = -9999; });\nconst t0 = performance.now();\n(function frame() {\n const w = Math.max(1, cv.clientWidth || 300), h = Math.max(1, cv.clientHeight || 240);\n if (cv.width !== Math.round(w * DPR) || cv.height !== Math.round(h * DPR)) {\n cv.width = Math.round(w * DPR); cv.height = Math.round(h * DPR);\n }\n ctx.setTransform(DPR, 0, 0, DPR, 0, 0); // draw in CSS px\n const t = (performance.now() - t0) / 1000;\n draw(ctx, w, h, t, mx, my, state);\n requestAnimationFrame(frame);\n})();\n</script>",
4242
"mountSnippet": "import { mount } from 'fx-lab';\nmount(document.querySelector('#app'), 'aboriginal');",
4343
"runtimeNotes": "Canvas2D: draw(ctx, w, h, t, mx, my, state) runs each frame; w/h and mx/my are CSS px (mx,my = -9999 when not hovering), ctx is pre-scaled by DPR = min(2, devicePixelRatio). Perf budget — gpu: low, cpu: low, mobileSafe: true. Persistent accumulator: Dot centre positions generated once on first frame via Math.random. Non-deterministic: uses unseeded randomness or wall-clock time, so output differs run to run. Reduced motion: freeze.",
44-
"url": "/effects/aboriginal.md",
44+
"url": "https://rosuh.github.io/fx-lab/effects/aboriginal.md",
4545
"preview": null
4646
}

site/effects/asanoha.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,6 @@
3939
"snippet": "<!-- fx-lab effect: asanoha — Asanoha (canvas) · license MIT\n accuracy: Procedural approximation of Japanese Asanoha hemp-leaf tiling; not a faithful reproduction of any specific historical motif. -->\n<canvas id=\"fx-asanoha\" style=\"display:block;width:100%;height:100%;min-height:240px\"></canvas>\n<script type=\"module\">\nconst draw = function asanoha(ctx,w,h,t){ ctx.fillStyle='#10243a';ctx.fillRect(0,0,w,h);ctx.strokeStyle='rgba(150,200,235,0.7)';ctx.lineWidth=1;const R=24,SQ=Math.sqrt(3);for(let row=0,y=0;y<h+R;row++,y+=R*1.5)for(let x=0;x<w+R;x+=R*SQ){const cx=x+(row%2)*R*SQ/2,cy=y;for(let i=0;i<6;i++){const a=i*Math.PI/3;ctx.beginPath();ctx.moveTo(cx,cy);ctx.lineTo(cx+Math.cos(a)*R,cy+Math.sin(a)*R);ctx.lineTo(cx+Math.cos(a+Math.PI/3)*R,cy+Math.sin(a+Math.PI/3)*R);ctx.stroke();}}};\nconst cv = document.getElementById('fx-asanoha');\nconst ctx = cv.getContext('2d');\nconst DPR = Math.min(2, window.devicePixelRatio || 1);\nconst state = {};\nlet mx = -9999, my = -9999; // -9999 == not hovering (CONTRACT 2)\ncv.addEventListener('pointermove', (e) => {\n const r = cv.getBoundingClientRect(); mx = e.clientX - r.left; my = e.clientY - r.top;\n});\ncv.addEventListener('pointerleave', () => { mx = -9999; my = -9999; });\nconst t0 = performance.now();\n(function frame() {\n const w = Math.max(1, cv.clientWidth || 300), h = Math.max(1, cv.clientHeight || 240);\n if (cv.width !== Math.round(w * DPR) || cv.height !== Math.round(h * DPR)) {\n cv.width = Math.round(w * DPR); cv.height = Math.round(h * DPR);\n }\n ctx.setTransform(DPR, 0, 0, DPR, 0, 0); // draw in CSS px\n const t = (performance.now() - t0) / 1000;\n draw(ctx, w, h, t, mx, my, state);\n requestAnimationFrame(frame);\n})();\n</script>",
4040
"mountSnippet": "import { mount } from 'fx-lab';\nmount(document.querySelector('#app'), 'asanoha');",
4141
"runtimeNotes": "Canvas2D: draw(ctx, w, h, t, mx, my, state) runs each frame; w/h and mx/my are CSS px (mx,my = -9999 when not hovering), ctx is pre-scaled by DPR = min(2, devicePixelRatio). Perf budget — gpu: low, cpu: low, mobileSafe: true. Reduced motion: freeze.",
42-
"url": "/effects/asanoha.md",
42+
"url": "https://rosuh.github.io/fx-lab/effects/asanoha.md",
4343
"preview": null
4444
}

site/effects/ascii.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,6 @@
3737
"snippet": "<!-- fx-lab effect: ascii — ASCII Field (canvas) · license MIT -->\n<canvas id=\"fx-ascii\" style=\"display:block;width:100%;height:100%;min-height:240px\"></canvas>\n<script type=\"module\">\nconst draw = function ascii(ctx,w,h,t){ ctx.fillStyle='#0a0b0d';ctx.fillRect(0,0,w,h); const ramp=' .:-=+*#%@';ctx.font='12px monospace';ctx.textBaseline='top'; const S=11;\n for(let y=0;y<h;y+=S)for(let x=0;x<w;x+=S){ const u=x/w,v=y/h; const n=0.5+0.5*Math.sin(u*8+t)*Math.cos(v*8-t*0.7)+0.3*Math.sin((u+v)*12+t*1.4); const b=Math.max(0,Math.min(1,n)); ctx.fillStyle='rgba(120,'+(180+b*60|0)+',255,'+(0.3+b*0.7).toFixed(2)+')'; ctx.fillText(ramp[Math.floor(b*(ramp.length-1))],x,y); } };\nconst cv = document.getElementById('fx-ascii');\nconst ctx = cv.getContext('2d');\nconst DPR = Math.min(2, window.devicePixelRatio || 1);\nconst state = {};\nlet mx = -9999, my = -9999; // -9999 == not hovering (CONTRACT 2)\ncv.addEventListener('pointermove', (e) => {\n const r = cv.getBoundingClientRect(); mx = e.clientX - r.left; my = e.clientY - r.top;\n});\ncv.addEventListener('pointerleave', () => { mx = -9999; my = -9999; });\nconst t0 = performance.now();\n(function frame() {\n const w = Math.max(1, cv.clientWidth || 300), h = Math.max(1, cv.clientHeight || 240);\n if (cv.width !== Math.round(w * DPR) || cv.height !== Math.round(h * DPR)) {\n cv.width = Math.round(w * DPR); cv.height = Math.round(h * DPR);\n }\n ctx.setTransform(DPR, 0, 0, DPR, 0, 0); // draw in CSS px\n const t = (performance.now() - t0) / 1000;\n draw(ctx, w, h, t, mx, my, state);\n requestAnimationFrame(frame);\n})();\n</script>",
3838
"mountSnippet": "import { mount } from 'fx-lab';\nmount(document.querySelector('#app'), 'ascii');",
3939
"runtimeNotes": "Canvas2D: draw(ctx, w, h, t, mx, my, state) runs each frame; w/h and mx/my are CSS px (mx,my = -9999 when not hovering), ctx is pre-scaled by DPR = min(2, devicePixelRatio). Perf budget — gpu: low, cpu: high, mobileSafe: false. Reduced motion: freeze.",
40-
"url": "/effects/ascii.md",
40+
"url": "https://rosuh.github.io/fx-lab/effects/ascii.md",
4141
"preview": null
4242
}

site/effects/audiobars.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,6 @@
3737
"snippet": "<!-- fx-lab effect: audiobars — Audio Bars (canvas) · license MIT -->\n<canvas id=\"fx-audiobars\" style=\"display:block;width:100%;height:100%;min-height:240px\"></canvas>\n<script type=\"module\">\nconst draw = function audiobars(ctx,w,h,t){ ctx.fillStyle='#0a0a0f';ctx.fillRect(0,0,w,h);const n=32,bw=w/n;for(let i=0;i<n;i++){const v=(0.25+0.75*Math.abs(Math.sin(i*0.5+t*4)*Math.sin(i*0.13+t)))*h*0.8;ctx.fillStyle='hsl('+(i/n*120+200|0)+',85%,60%)';ctx.fillRect(i*bw+1,h-v,bw-2,v);} };\nconst cv = document.getElementById('fx-audiobars');\nconst ctx = cv.getContext('2d');\nconst DPR = Math.min(2, window.devicePixelRatio || 1);\nconst state = {};\nlet mx = -9999, my = -9999; // -9999 == not hovering (CONTRACT 2)\ncv.addEventListener('pointermove', (e) => {\n const r = cv.getBoundingClientRect(); mx = e.clientX - r.left; my = e.clientY - r.top;\n});\ncv.addEventListener('pointerleave', () => { mx = -9999; my = -9999; });\nconst t0 = performance.now();\n(function frame() {\n const w = Math.max(1, cv.clientWidth || 300), h = Math.max(1, cv.clientHeight || 240);\n if (cv.width !== Math.round(w * DPR) || cv.height !== Math.round(h * DPR)) {\n cv.width = Math.round(w * DPR); cv.height = Math.round(h * DPR);\n }\n ctx.setTransform(DPR, 0, 0, DPR, 0, 0); // draw in CSS px\n const t = (performance.now() - t0) / 1000;\n draw(ctx, w, h, t, mx, my, state);\n requestAnimationFrame(frame);\n})();\n</script>",
3838
"mountSnippet": "import { mount } from 'fx-lab';\nmount(document.querySelector('#app'), 'audiobars');",
3939
"runtimeNotes": "Canvas2D: draw(ctx, w, h, t, mx, my, state) runs each frame; w/h and mx/my are CSS px (mx,my = -9999 when not hovering), ctx is pre-scaled by DPR = min(2, devicePixelRatio). Perf budget — gpu: low, cpu: low, mobileSafe: true. Reduced motion: freeze.",
40-
"url": "/effects/audiobars.md",
40+
"url": "https://rosuh.github.io/fx-lab/effects/audiobars.md",
4141
"preview": null
4242
}

site/effects/aurora.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,6 @@
4040
"snippet": "<!-- fx-lab effect: aurora — Aurora Ribbons (shader) · license MIT -->\n<canvas id=\"fx-aurora\" style=\"display:block;width:100%;height:100%;min-height:240px\"></canvas>\n<script type=\"module\">\nconst PRE = \"#version 300 es\\nprecision highp float;\\nuniform vec2 iResolution; uniform float iTime; uniform vec2 iMouse;\\nout vec4 o;\\nfloat hash(vec2 p){ p=fract(p*vec2(123.34,345.45)); p+=dot(p,p+34.345); return fract(p.x*p.y); }\\nfloat noise(vec2 p){ vec2 i=floor(p),f=fract(p); float a=hash(i),b=hash(i+vec2(1.0,0.0)),c=hash(i+vec2(0.0,1.0)),d=hash(i+vec2(1.0,1.0)); vec2 u=f*f*(3.0-2.0*f); return mix(mix(a,b,u.x),mix(c,d,u.x),u.y); }\\nfloat fbm(vec2 p){ float v=0.0,a=0.5; for(int i=0;i<5;i++){ v+=a*noise(p); p*=2.0; a*=0.5; } return v; }\\n\";\nconst GLSL = \"void main(){ vec2 uv=gl_FragCoord.xy/iResolution.xy; vec2 m=iMouse.xy/iResolution.xy; float t=iTime*0.12;\\n vec3 col=vec3(0.015,0.02,0.05)+vec3(0.0,0.02,0.05)*uv.y;\\n float sp=hash(floor(gl_FragCoord.xy*0.5)); col+=smoothstep(0.992,1.0,sp)*smoothstep(0.25,1.0,uv.y)*vec3(0.7,0.8,1.0);\\n for(int i=0;i<3;i++){ float fi=float(i);\\n float base=0.45+0.13*fi+0.10*sin(uv.x*2.0+t*1.3+fi)+(fbm(vec2(uv.x*1.5+fi*4.0,t))-0.5)*0.30+(m.y-0.5)*0.2;\\n float d=uv.y-base; float curtain=exp(-d*d*9.0);\\n float rays=fbm(vec2(uv.x*22.0+fi*13.0,uv.y*3.0-t*3.0)); curtain*=0.35+0.9*rays*rays; curtain*=smoothstep(0.0,0.3,uv.y);\\n vec3 c=mix(vec3(0.15,0.95,0.55),vec3(0.55,0.20,0.95),0.5+0.5*sin(t+fi*2.0+uv.x*2.5)); col+=curtain*c*1.3; }\\n col+=vec3(0.04,0.10,0.07)*smoothstep(0.35,0.0,uv.y);\\n col=col/(col+0.6); o=vec4(col,1.0); }\";\nconst cv = document.getElementById('fx-aurora');\nconst gl = cv.getContext('webgl2');\nif (!gl) { console.error('fx-aurora: WebGL2 not supported'); } else {\n const DPR = Math.min(2, window.devicePixelRatio || 1);\n const compile = (type, src) => {\n const s = gl.createShader(type); gl.shaderSource(s, src); gl.compileShader(s);\n if (!gl.getShaderParameter(s, gl.COMPILE_STATUS)) console.error(gl.getShaderInfoLog(s));\n return s;\n };\n const prog = gl.createProgram();\n gl.attachShader(prog, compile(gl.VERTEX_SHADER, '#version 300 es\\nin vec2 p;void main(){gl_Position=vec4(p,0,1);}'));\n gl.attachShader(prog, compile(gl.FRAGMENT_SHADER, PRE + GLSL));\n gl.linkProgram(prog);\n if (!gl.getProgramParameter(prog, gl.LINK_STATUS)) console.error(gl.getProgramInfoLog(prog));\n gl.useProgram(prog);\n const buf = gl.createBuffer();\n gl.bindBuffer(gl.ARRAY_BUFFER, buf);\n gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1, -1, 3, -1, -1, 3]), gl.STATIC_DRAW);\n const loc = gl.getAttribLocation(prog, 'p');\n gl.enableVertexAttribArray(loc);\n gl.vertexAttribPointer(loc, 2, gl.FLOAT, false, 0, 0);\n const uRes = gl.getUniformLocation(prog, 'iResolution');\n const uTime = gl.getUniformLocation(prog, 'iTime');\n const uMouse = gl.getUniformLocation(prog, 'iMouse');\n let mx = -1, my = -1;\n cv.addEventListener('pointermove', (e) => {\n const r = cv.getBoundingClientRect();\n mx = (e.clientX - r.left) * DPR;\n my = (r.height - (e.clientY - r.top)) * DPR; // Y-flipped to GL space\n });\n cv.addEventListener('pointerleave', () => { mx = -1; my = -1; });\n const t0 = performance.now();\n (function frame() {\n const w = Math.max(1, cv.clientWidth || 300), h = Math.max(1, cv.clientHeight || 240);\n if (cv.width !== Math.round(w * DPR) || cv.height !== Math.round(h * DPR)) {\n cv.width = Math.round(w * DPR); cv.height = Math.round(h * DPR);\n }\n gl.viewport(0, 0, cv.width, cv.height);\n gl.uniform2f(uRes, cv.width, cv.height);\n gl.uniform1f(uTime, (performance.now() - t0) / 1000);\n gl.uniform2f(uMouse, mx < 0 ? cv.width / 2 : mx, my < 0 ? cv.height / 2 : my);\n gl.drawArrays(gl.TRIANGLES, 0, 3);\n requestAnimationFrame(frame);\n })();\n}\n</script>",
4141
"mountSnippet": "import { mount } from 'fx-lab';\nmount(document.querySelector('#app'), 'aurora');",
4242
"runtimeNotes": "WebGL2 fragment shader compiled against the shared PRE preamble (uniforms iResolution, iTime, iMouse — iMouse is Y-flipped physical px, centered when no pointer). Browsers cap ~8–16 live WebGL2 contexts; the gallery lazy-unmounts off-screen tiles to stay under the limit. Perf budget — gpu: med, cpu: low, mobileSafe: true. Reduced motion: freeze.",
43-
"url": "/effects/aurora.md",
43+
"url": "https://rosuh.github.io/fx-lab/effects/aurora.md",
4444
"preview": null
4545
}

site/effects/aztec.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,6 @@
4040
"snippet": "<!-- fx-lab effect: aztec — Aztec Step-Fret (canvas) · license MIT\n accuracy: Procedural approximation of Mesoamerican step-fret pattern; not a faithful reproduction of any specific historical motif. -->\n<canvas id=\"fx-aztec\" style=\"display:block;width:100%;height:100%;min-height:240px\"></canvas>\n<script type=\"module\">\nconst draw = function aztec(ctx,w,h,t){ ctx.fillStyle='#1a0f0a';ctx.fillRect(0,0,w,h);const cols=['#d9763a','#e8b04b','#3a9b8a','#c0392b'],S=28;for(let y=0,r=0;y<h;y+=S,r++)for(let x=0,c=0;x<w;x+=S,c++){ctx.fillStyle=cols[(r+c)%4];ctx.fillRect(x,y,S,S);ctx.fillStyle='#1a0f0a';ctx.fillRect(x+S*0.3,y+S*0.3,S*0.4,S*0.4);ctx.fillStyle=cols[(r+c+1)%4];ctx.fillRect(x+S*0.4,y+S*0.4,S*0.2,S*0.2);}};\nconst cv = document.getElementById('fx-aztec');\nconst ctx = cv.getContext('2d');\nconst DPR = Math.min(2, window.devicePixelRatio || 1);\nconst state = {};\nlet mx = -9999, my = -9999; // -9999 == not hovering (CONTRACT 2)\ncv.addEventListener('pointermove', (e) => {\n const r = cv.getBoundingClientRect(); mx = e.clientX - r.left; my = e.clientY - r.top;\n});\ncv.addEventListener('pointerleave', () => { mx = -9999; my = -9999; });\nconst t0 = performance.now();\n(function frame() {\n const w = Math.max(1, cv.clientWidth || 300), h = Math.max(1, cv.clientHeight || 240);\n if (cv.width !== Math.round(w * DPR) || cv.height !== Math.round(h * DPR)) {\n cv.width = Math.round(w * DPR); cv.height = Math.round(h * DPR);\n }\n ctx.setTransform(DPR, 0, 0, DPR, 0, 0); // draw in CSS px\n const t = (performance.now() - t0) / 1000;\n draw(ctx, w, h, t, mx, my, state);\n requestAnimationFrame(frame);\n})();\n</script>",
4141
"mountSnippet": "import { mount } from 'fx-lab';\nmount(document.querySelector('#app'), 'aztec');",
4242
"runtimeNotes": "Canvas2D: draw(ctx, w, h, t, mx, my, state) runs each frame; w/h and mx/my are CSS px (mx,my = -9999 when not hovering), ctx is pre-scaled by DPR = min(2, devicePixelRatio). Perf budget — gpu: low, cpu: low, mobileSafe: true. Reduced motion: freeze.",
43-
"url": "/effects/aztec.md",
43+
"url": "https://rosuh.github.io/fx-lab/effects/aztec.md",
4444
"preview": null
4545
}

site/effects/bauhaus.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,6 @@
3838
"snippet": "<!-- fx-lab effect: bauhaus — Bauhaus (canvas) · license MIT -->\n<canvas id=\"fx-bauhaus\" style=\"display:block;width:100%;height:100%;min-height:240px\"></canvas>\n<script type=\"module\">\nconst draw = function bauhaus(ctx,w,h,t){ ctx.fillStyle='#e8e2d4';ctx.fillRect(0,0,w,h); const a=t*0.3,M=Math.min(w,h);\n ctx.fillStyle='#2b4a9b';ctx.beginPath();ctx.arc(w*0.32,h*0.4,M*0.26,0,7);ctx.fill();\n ctx.save();ctx.translate(w*0.7,h*0.62);ctx.rotate(a);ctx.fillStyle='#d23b2a';ctx.beginPath();ctx.arc(0,0,M*0.2,0,Math.PI);ctx.fill();ctx.restore();\n ctx.fillStyle='#e8b53a';ctx.beginPath();ctx.moveTo(w*0.55,h*0.08);ctx.lineTo(w*0.82,h*0.08);ctx.lineTo(w*0.55,h*0.42);ctx.closePath();ctx.fill();\n ctx.fillStyle='#1a1a1a';ctx.fillRect(w*0.1,h*0.76,w*0.8,6);ctx.fillRect(w*0.12,h*0.86,w*0.46,6); };\nconst cv = document.getElementById('fx-bauhaus');\nconst ctx = cv.getContext('2d');\nconst DPR = Math.min(2, window.devicePixelRatio || 1);\nconst state = {};\nlet mx = -9999, my = -9999; // -9999 == not hovering (CONTRACT 2)\ncv.addEventListener('pointermove', (e) => {\n const r = cv.getBoundingClientRect(); mx = e.clientX - r.left; my = e.clientY - r.top;\n});\ncv.addEventListener('pointerleave', () => { mx = -9999; my = -9999; });\nconst t0 = performance.now();\n(function frame() {\n const w = Math.max(1, cv.clientWidth || 300), h = Math.max(1, cv.clientHeight || 240);\n if (cv.width !== Math.round(w * DPR) || cv.height !== Math.round(h * DPR)) {\n cv.width = Math.round(w * DPR); cv.height = Math.round(h * DPR);\n }\n ctx.setTransform(DPR, 0, 0, DPR, 0, 0); // draw in CSS px\n const t = (performance.now() - t0) / 1000;\n draw(ctx, w, h, t, mx, my, state);\n requestAnimationFrame(frame);\n})();\n</script>",
3939
"mountSnippet": "import { mount } from 'fx-lab';\nmount(document.querySelector('#app'), 'bauhaus');",
4040
"runtimeNotes": "Canvas2D: draw(ctx, w, h, t, mx, my, state) runs each frame; w/h and mx/my are CSS px (mx,my = -9999 when not hovering), ctx is pre-scaled by DPR = min(2, devicePixelRatio). Perf budget — gpu: low, cpu: low, mobileSafe: true. Reduced motion: freeze.",
41-
"url": "/effects/bauhaus.md",
41+
"url": "https://rosuh.github.io/fx-lab/effects/bauhaus.md",
4242
"preview": null
4343
}

0 commit comments

Comments
 (0)