use-scroll-animate 4.6.0 → 4.8.0

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.
Files changed (78) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +1 -1
  3. package/README_ja.md +1 -1
  4. package/README_zh.md +1 -1
  5. package/dist/chunks/{index-tags-BQh_uGqH.cjs → index-tags-CIRY2KnU.cjs} +2 -2
  6. package/dist/chunks/{index-tags-BQh_uGqH.cjs.map → index-tags-CIRY2KnU.cjs.map} +1 -1
  7. package/dist/chunks/{index-tags-43Xtd01A.js → index-tags-Dh8nwXqw.js} +2 -2
  8. package/dist/chunks/{index-tags-43Xtd01A.js.map → index-tags-Dh8nwXqw.js.map} +1 -1
  9. package/dist/components/a11y.cjs +1 -1
  10. package/dist/components/a11y.d.cts +1 -1
  11. package/dist/components/a11y.d.ts +1 -1
  12. package/dist/components/a11y.js +1 -1
  13. package/dist/components/angular.cjs +2 -1
  14. package/dist/components/angular.cjs.map +1 -1
  15. package/dist/components/angular.d.cts +2 -1
  16. package/dist/components/angular.d.ts +2 -1
  17. package/dist/components/angular.js +2 -1
  18. package/dist/components/angular.js.map +1 -1
  19. package/dist/components/bridge.cjs +154 -0
  20. package/dist/components/bridge.cjs.map +1 -0
  21. package/dist/components/bridge.d.cts +63 -0
  22. package/dist/components/bridge.d.ts +63 -0
  23. package/dist/components/bridge.js +147 -0
  24. package/dist/components/bridge.js.map +1 -0
  25. package/dist/components/jsx.d.cts +1 -1
  26. package/dist/components/jsx.d.ts +1 -1
  27. package/dist/components/lazy.cjs +1 -1
  28. package/dist/components/lazy.d.cts +1 -1
  29. package/dist/components/lazy.d.ts +1 -1
  30. package/dist/components/lazy.js +1 -1
  31. package/dist/components/lite.cjs +333 -10
  32. package/dist/components/lite.cjs.map +1 -1
  33. package/dist/components/lite.js +320 -11
  34. package/dist/components/lite.js.map +1 -1
  35. package/dist/components/perf.cjs +1 -1
  36. package/dist/components/perf.d.cts +1 -1
  37. package/dist/components/perf.d.ts +1 -1
  38. package/dist/components/perf.js +1 -1
  39. package/dist/components/react.cjs +1 -1
  40. package/dist/components/react.js +1 -1
  41. package/dist/components/solid.cjs +2 -1
  42. package/dist/components/solid.cjs.map +1 -1
  43. package/dist/components/solid.d.cts +2 -1
  44. package/dist/components/solid.d.ts +2 -1
  45. package/dist/components/solid.js +2 -1
  46. package/dist/components/solid.js.map +1 -1
  47. package/dist/components/svelte.cjs +2 -1
  48. package/dist/components/svelte.cjs.map +1 -1
  49. package/dist/components/svelte.d.cts +2 -1
  50. package/dist/components/svelte.d.ts +2 -1
  51. package/dist/components/svelte.js +2 -1
  52. package/dist/components/svelte.js.map +1 -1
  53. package/dist/components/vue.cjs +2 -1
  54. package/dist/components/vue.cjs.map +1 -1
  55. package/dist/components/vue.d.cts +2 -1
  56. package/dist/components/vue.d.ts +2 -1
  57. package/dist/components/vue.js +2 -1
  58. package/dist/components/vue.js.map +1 -1
  59. package/dist/components/webgl.cjs +184 -10
  60. package/dist/components/webgl.cjs.map +1 -1
  61. package/dist/components/webgl.css +1 -1
  62. package/dist/components/webgl.d.cts +59 -5
  63. package/dist/components/webgl.d.ts +59 -5
  64. package/dist/components/webgl.js +178 -12
  65. package/dist/components/webgl.js.map +1 -1
  66. package/dist/components.cjs +16 -1
  67. package/dist/components.cjs.map +1 -1
  68. package/dist/components.css +1 -1
  69. package/dist/components.d.cts +119 -6
  70. package/dist/components.d.ts +119 -6
  71. package/dist/components.js +3 -2
  72. package/dist/components.js.map +1 -1
  73. package/dist/components.umd.js +3 -2
  74. package/dist/components.umd.js.map +1 -1
  75. package/docs/ROADMAP.md +2 -2
  76. package/docs/components.md +7 -0
  77. package/docs/hybrid-apps.md +15 -0
  78. package/package.json +11 -1
@@ -388,7 +388,7 @@ const COMPONENT_CATEGORIES = {
388
388
  timeline: ['usa-timeline'],
389
389
  gesture: ['usa-swipeable', 'usa-pinch-zoom'],
390
390
  svg: ['usa-draw', 'usa-morph', 'usa-mask-reveal', 'usa-anim-icon'],
391
- webgl: ['usa-shader', 'usa-distort', 'usa-liquid'],
391
+ webgl: ['usa-shader', 'usa-distort', 'usa-liquid', 'usa-post-fx'],
392
392
  depth: ['usa-cube', 'usa-depth'],
393
393
  layout: ['usa-auto-animate', 'usa-masonry'],
394
394
  packs: ['usa-pack'],
@@ -8953,6 +8953,12 @@ const SHADERS = {
8953
8953
  waves: 'void main(){vec2 u=v_uv;float t=u_time*.4;float w=0.;for(int i=0;i<4;i++){float f=float(i)+1.;w+=sin(u.x*6.*f+t*f)*.08/f;}float l=smoothstep(.0,.02,abs(u.y-.5-w));vec3 col=mix(vec3(.2,.5,1.),vec3(.04,.06,.15),l)+vec3(.1,.0,.2)*u.y;gl_FragColor=vec4(col,1.);}',
8954
8954
  aurora: 'void main(){vec2 u=v_uv;float t=u_time*.2;float b=0.;for(int i=0;i<3;i++){float f=float(i);b+=.4/abs((u.y-.6+.15*sin(u.x*3.+t+f*1.7))*(8.+f*4.));}vec3 col=vec3(.02,.03,.08)+b*mix(vec3(.1,.9,.6),vec3(.6,.3,1.),u.x)*.35;gl_FragColor=vec4(col,1.);}',
8955
8955
  distort: 'void main(){vec2 u=v_uv;vec2 d=u-u_mouse;float r=length(d);float k=u_hover*.08*exp(-r*r*18.);u-=normalize(d+1e-4)*k;float s=u_hover*.006;vec3 col=vec3(texture2D(u_tex,u+vec2(s,0.)).r,texture2D(u_tex,u).g,texture2D(u_tex,u-vec2(s,0.)).b);gl_FragColor=vec4(col,1.);}',
8956
+ // 4.8 particle presets (procedural, one quad: no buffers, no per-particle JS)
8957
+ snow: 'float h(vec2 p){return fract(sin(dot(p,vec2(127.1,311.7)))*43758.5453);}void main(){vec2 u=v_uv;u.x*=u_resolution.x/max(u_resolution.y,1.);vec3 col=mix(vec3(.05,.08,.16),vec3(.12,.16,.3),v_uv.y);for(int l=0;l<3;l++){float s=8.+float(l)*7.;vec2 q=u*s;q.y+=u_time*(.6+float(l)*.35);q.x+=sin(q.y*.7+float(l))*.3;vec2 id=floor(q);vec2 f=fract(q)-.5;float r=h(id);if(r>.6){vec2 o=vec2(h(id+3.)-.5,h(id+7.)-.5)*.6;float d=length(f-o);col+=smoothstep(.09-float(l)*.02,0.,d)*(.5+.5*r);}}gl_FragColor=vec4(col,1.);}',
8958
+ fireflies: 'float h(vec2 p){return fract(sin(dot(p,vec2(127.1,311.7)))*43758.5453);}void main(){vec2 u=v_uv;u.x*=u_resolution.x/max(u_resolution.y,1.);vec3 col=vec3(.02,.04,.03);for(int l=0;l<2;l++){vec2 q=u*(5.+float(l)*4.);vec2 id=floor(q);vec2 f=fract(q)-.5;float r=h(id+float(l)*13.);vec2 o=.35*vec2(sin(u_time*(.4+r)+r*6.28),cos(u_time*(.3+r)+r*12.));float d=length(f-o);float tw=.5+.5*sin(u_time*3.*r+r*20.);col+=vec3(1.,.85,.3)*smoothstep(.12,0.,d)*tw*step(.45,r);}gl_FragColor=vec4(col,1.);}',
8959
+ stars: 'float h(vec2 p){return fract(sin(dot(p,vec2(127.1,311.7)))*43758.5453);}void main(){vec2 c=v_uv-.5;c.x*=u_resolution.x/max(u_resolution.y,1.);vec3 col=vec3(.01,.01,.04);for(int l=0;l<4;l++){float z=fract(float(l)*.25+u_time*.05);float sc=mix(20.,.5,z);vec2 q=c*sc+float(l)*7.;vec2 id=floor(q);vec2 f=fract(q)-.5;float r=h(id);float d=length(f-(vec2(h(id+1.),h(id+2.))-.5)*.7);col+=vec3(.8,.9,1.)*smoothstep(.06,0.,d)*step(.8,r)*smoothstep(0.,.5,z)*smoothstep(1.,.8,z)*2.;}gl_FragColor=vec4(col,1.);}',
8960
+ bokeh: 'float h(vec2 p){return fract(sin(dot(p,vec2(127.1,311.7)))*43758.5453);}void main(){vec2 u=v_uv;u.x*=u_resolution.x/max(u_resolution.y,1.);vec3 col=mix(vec3(.08,.03,.12),vec3(.02,.05,.12),v_uv.y);for(int l=0;l<3;l++){vec2 q=u*(2.5+float(l)*1.5);q.y-=u_time*.05*(1.+float(l));vec2 id=floor(q);vec2 f=fract(q)-.5;float r=h(id+float(l)*5.);float d=length(f-(vec2(h(id+4.),h(id+9.))-.5)*.5);vec3 tint=.5+.5*cos(r*6.28+vec3(0.,2.,4.));col+=tint*smoothstep(.3,.26,d)*.18*step(.35,r);}gl_FragColor=vec4(col,1.);}',
8961
+ rain: 'float h(float n){return fract(sin(n)*43758.5453);}void main(){vec2 u=v_uv;vec3 col=mix(vec3(.04,.06,.1),vec3(.1,.13,.2),u.y);float n=floor(u.x*120.);float sp=.8+h(n)*1.2;float y=fract(u.y+u_time*sp+h(n+1.));float drop=smoothstep(.0,.08,y)*smoothstep(.16,.08,y)*step(.7,h(n+2.));col+=vec3(.5,.6,.8)*drop*.6;gl_FragColor=vec4(col,1.);}',
8956
8962
  liquid: 'void main(){vec2 u=v_uv;vec2 o=vec2(0.);for(int i=0;i<4;i++){vec4 r=u_ripples[i];if(r.w>0.){float d=distance(u,r.xy);float w=sin(d*60.-r.z*12.)*exp(-d*6.)*exp(-r.z*1.6)*r.w*.02;o+=normalize(u-r.xy+1e-4)*w;}}o+=vec2(sin(u.y*10.+u_time),cos(u.x*10.+u_time))*.002*u_hover;gl_FragColor=texture2D(u_tex,u+o);}',
8957
8963
  };
8958
8964
  /** Full fragment source for a preset or custom body (adds the shared header). */
@@ -9012,9 +9018,10 @@ function glQuad(canvas, frag) {
9012
9018
  const U = (n) => g.getUniformLocation(prog, n);
9013
9019
  const uT = U('u_time'), uR = U('u_resolution'), uM = U('u_mouse'), uH = U('u_hover'), uRp = U('u_ripples');
9014
9020
  let tex = null;
9021
+ const extraLoc = {};
9015
9022
  const quad = {
9016
- resize() {
9017
- const dpr = Math.min(2, (typeof devicePixelRatio === 'number' && devicePixelRatio) || 1);
9023
+ resize(scale = 1) {
9024
+ const dpr = Math.min(2, (typeof devicePixelRatio === 'number' && devicePixelRatio) || 1) * Math.min(1, Math.max(0.25, scale));
9018
9025
  const w = Math.max(1, Math.round((canvas.clientWidth || 300) * dpr));
9019
9026
  const h = Math.max(1, Math.round((canvas.clientHeight || 150) * dpr));
9020
9027
  if (canvas.width !== w || canvas.height !== h)
@@ -9037,6 +9044,9 @@ function glQuad(canvas, frag) {
9037
9044
  g.uniform1f(uH, u.hover ?? 0);
9038
9045
  if (uRp)
9039
9046
  g.uniform4fv(uRp, new Float32Array((u.ripples ?? []).concat(Array(16).fill(0)).slice(0, 16)));
9047
+ if (u.extra)
9048
+ for (const [k, v] of Object.entries(u.extra))
9049
+ g.uniform1f((extraLoc[k] ?? (extraLoc[k] = U(k))), v);
9040
9050
  g.drawArrays(g.TRIANGLES, 0, 6);
9041
9051
  },
9042
9052
  dispose() {
@@ -9051,6 +9061,131 @@ function glQuad(canvas, frag) {
9051
9061
  return quad;
9052
9062
  }
9053
9063
 
9064
+ /**
9065
+ * 4.8 — WebGL preset library on `glQuad()`: particle presets (`snow`,
9066
+ * `fireflies`, `stars`, `bokeh`, `rain` — usable as `<usa-shader preset>`),
9067
+ * chainable post-processing passes for images (`<usa-post-fx>`), one CSS
9068
+ * fallback per preset, and an adaptive quality governor (fps + battery).
9069
+ */
9070
+ const PARTICLE_PRESETS = ['snow', 'fireflies', 'stars', 'bokeh', 'rain'];
9071
+ /** Post-processing passes: `vec3 fx(vec3 c, vec2 uv)` bodies, applied in order. `u_intensity` 0–1. */
9072
+ const POST_EFFECTS = {
9073
+ vignette: 'c*=mix(1.,smoothstep(.85,.25,length(uv-.5)),u_intensity);',
9074
+ grain: 'c+=(fract(sin(dot(uv*u_resolution+u_time,vec2(12.9898,78.233)))*43758.5453)-.5)*.18*u_intensity;',
9075
+ chromatic: 'float s=.008*u_intensity;c=vec3(texture2D(u_tex,uv+vec2(s,0.)).r,c.g,texture2D(u_tex,uv-vec2(s,0.)).b);',
9076
+ scanlines: 'c*=1.-.25*u_intensity*step(.5,fract(uv.y*u_resolution.y*.5));',
9077
+ crt: 'vec2 d=uv-.5;float r=dot(d,d);c*=1.-.6*u_intensity*r;c*=.92+.08*sin(uv.y*u_resolution.y*3.14159);c.r*=1.+.05*u_intensity;',
9078
+ bloom: 'vec3 b=vec3(0.);for(int i=0;i<8;i++){float a=float(i)*.785;b+=max(texture2D(u_tex,uv+vec2(cos(a),sin(a))*.012).rgb-.6,0.);}c+=b*.35*u_intensity;',
9079
+ pixelate: 'float px=mix(1.,48.,u_intensity);vec2 g=floor(uv*u_resolution/px)*px/u_resolution;c=texture2D(u_tex,g+.5*px/u_resolution).rgb;',
9080
+ duotone: 'float l=dot(c,vec3(.299,.587,.114));c=mix(c,mix(vec3(.12,.05,.35),vec3(1.,.55,.4),l),u_intensity);',
9081
+ glitch: 'float k=step(.97,fract(sin(floor(uv.y*24.)+floor(u_time*6.))*4375.5));c=mix(c,texture2D(u_tex,uv+vec2(k*.04*u_intensity,0.)).rgb,k);',
9082
+ };
9083
+ /** One fragment shader running the passes in order over `u_tex` (pixel-sampling passes read the source). */
9084
+ function postFxShader(effects) {
9085
+ const list = effects.filter((e) => POST_EFFECTS[e]);
9086
+ const body = list.map((e) => `{${POST_EFFECTS[e]}}`).join('');
9087
+ return `uniform float u_intensity;void main(){vec2 uv=v_uv;vec3 c=texture2D(u_tex,uv).rgb;${body}gl_FragColor=vec4(clamp(c,0.,1.),1.);}`;
9088
+ }
9089
+ /** The unified CSS fallback (no WebGL / reduced data): a still background or image filter per preset. */
9090
+ const GL_FALLBACKS = {
9091
+ gradient: 'linear-gradient(120deg,#6366f1,#ec4899 50%,#22d3ee)',
9092
+ plasma: 'conic-gradient(from 90deg,#f43f5e,#a855f7,#06b6d4,#f43f5e)',
9093
+ waves: 'linear-gradient(#0a0f26,#1e3a8a)',
9094
+ aurora: 'radial-gradient(120% 60% at 30% 40%,#10b98155,transparent),radial-gradient(100% 50% at 70% 50%,#8b5cf655,transparent),#05070f',
9095
+ snow: 'radial-gradient(2px 2px at 20% 30%,#fff,transparent),radial-gradient(2px 2px at 70% 60%,#fff,transparent),radial-gradient(1.5px 1.5px at 40% 80%,#fff,transparent),linear-gradient(#0d1428,#1f2a4d)',
9096
+ fireflies: 'radial-gradient(3px 3px at 25% 40%,#fde68a,transparent),radial-gradient(3px 3px at 65% 70%,#fde68a,transparent),#05090a',
9097
+ stars: 'radial-gradient(1px 1px at 10% 20%,#fff,transparent),radial-gradient(1px 1px at 80% 30%,#fff,transparent),radial-gradient(1.5px 1.5px at 50% 70%,#cfe0ff,transparent),#02020a',
9098
+ bokeh: 'radial-gradient(40px 40px at 30% 40%,#f472b633,transparent),radial-gradient(60px 60px at 70% 60%,#60a5fa33,transparent),#140820',
9099
+ rain: 'repeating-linear-gradient(100deg,#ffffff10 0 1px,transparent 1px 14px),linear-gradient(#0a0f1a,#1a2133)',
9100
+ // post-fx fallbacks are CSS filters on the <img>
9101
+ 'post:duotone': 'grayscale(1) sepia(.6) hue-rotate(220deg) saturate(2)',
9102
+ 'post:vignette': 'brightness(.95) contrast(1.05)',
9103
+ 'post:crt': 'contrast(1.15) saturate(1.2)',
9104
+ 'post:bloom': 'brightness(1.08) saturate(1.15)',
9105
+ };
9106
+ /** CSS fallback for a shader preset / post effect list. */
9107
+ function glFallbackCss(preset, post = false) {
9108
+ if (post)
9109
+ return preset.split(/\s+/).map((e) => GL_FALLBACKS[`post:${e}`]).filter(Boolean).join(' ');
9110
+ return GL_FALLBACKS[preset] || GL_FALLBACKS.gradient;
9111
+ }
9112
+ /**
9113
+ * Adaptive quality for GL loops: measures fps, steps the resolution scale
9114
+ * down (1 → 0.5 → 0.35) after two slow seconds and back up after five good
9115
+ * ones, and caps the frame rate in battery-saver mode. Pure — feed it times.
9116
+ */
9117
+ function glGovernor(options = {}) {
9118
+ const { minFps = 40, saverFps = 30 } = options;
9119
+ const STEPS = [1, 0.5, 0.35];
9120
+ let step = 0;
9121
+ let frames = 0;
9122
+ let winStart = -1;
9123
+ let last = -Infinity;
9124
+ let slow = 0;
9125
+ let good = 0;
9126
+ let fps = 60;
9127
+ const g = {
9128
+ saver: false,
9129
+ get scale() {
9130
+ return Math.min(STEPS[step], g.saver ? 0.6 : 1);
9131
+ },
9132
+ get fps() {
9133
+ return fps;
9134
+ },
9135
+ tick(t) {
9136
+ if (winStart < 0)
9137
+ winStart = t;
9138
+ if (g.saver && t - last < 1000 / saverFps - 1)
9139
+ return false;
9140
+ last = t;
9141
+ frames++;
9142
+ if (t - winStart >= 1000) {
9143
+ fps = Math.round((frames * 1000) / (t - winStart));
9144
+ frames = 0;
9145
+ winStart = t;
9146
+ const before = g.scale;
9147
+ if (fps < minFps && !g.saver) {
9148
+ good = 0;
9149
+ if (++slow >= 2 && step < STEPS.length - 1)
9150
+ ((step++), (slow = 0));
9151
+ }
9152
+ else {
9153
+ slow = 0;
9154
+ if (++good >= 5 && step > 0)
9155
+ ((step--), (good = 0));
9156
+ }
9157
+ if (g.scale !== before)
9158
+ g.onScale?.(g.scale);
9159
+ }
9160
+ return true;
9161
+ },
9162
+ };
9163
+ return g;
9164
+ }
9165
+ /** Watch the Battery Status API (where available) and Save-Data; calls `cb(true)` in saver conditions. Returns a stop function. */
9166
+ function watchPowerSaver(cb) {
9167
+ let stopped = false;
9168
+ const nav = (typeof navigator !== 'undefined' ? navigator : {});
9169
+ const saveData = !!nav.connection?.saveData;
9170
+ if (saveData)
9171
+ cb(true);
9172
+ if (typeof nav.getBattery !== 'function')
9173
+ return () => undefined;
9174
+ let battery = null;
9175
+ const update = () => !stopped && cb(saveData || (!!battery && !battery.charging && battery.level <= 0.2));
9176
+ nav.getBattery().then((b) => {
9177
+ battery = b;
9178
+ update();
9179
+ b.addEventListener?.('levelchange', update);
9180
+ b.addEventListener?.('chargingchange', update);
9181
+ }, () => undefined);
9182
+ return () => {
9183
+ stopped = true;
9184
+ battery?.removeEventListener?.('levelchange', update);
9185
+ battery?.removeEventListener?.('chargingchange', update);
9186
+ };
9187
+ }
9188
+
9054
9189
  var css$2 = "";
9055
9190
 
9056
9191
  function make(kind) {
@@ -9064,15 +9199,23 @@ function make(kind) {
9064
9199
  this._hover = 0;
9065
9200
  this._hoverTo = 0;
9066
9201
  this._ripples = [];
9202
+ this._scale = 1;
9067
9203
  }
9068
9204
  static get observedAttributes() {
9069
- return kind === 'shader' ? ['preset', 'speed'] : ['src'];
9205
+ return kind === 'shader' ? ['preset', 'speed', 'quality'] : kind === 'post' ? ['effects', 'intensity', 'quality'] : ['src'];
9070
9206
  }
9071
9207
  get active() {
9072
9208
  return !!this._q;
9073
9209
  }
9074
9210
  fallback(reason) {
9075
9211
  this.setAttribute('data-fallback', reason);
9212
+ // 4.8 unified fallback: a still CSS rendering of the preset / effects
9213
+ if (reason !== 'off') {
9214
+ if (kind === 'shader')
9215
+ this.style.setProperty('--usa-gl-fallback', glFallbackCss(this.str('preset', 'gradient')));
9216
+ if (kind === 'post')
9217
+ this.style.setProperty('--usa-gl-filter', glFallbackCss(this.str('effects', 'vignette grain'), true) || 'none');
9218
+ }
9076
9219
  this._c?.remove();
9077
9220
  this._c = null;
9078
9221
  this._q?.dispose();
@@ -9090,12 +9233,13 @@ function make(kind) {
9090
9233
  mouse: this._mouse,
9091
9234
  hover: this._hover,
9092
9235
  ripples: this._ripples.flatMap((r) => [r.x, r.y, (t - r.t) / 1000, this.num('strength', 1)]),
9236
+ extra: kind === 'post' ? { u_intensity: clamp(this.num('intensity', 0.6), 0, 1) } : undefined,
9093
9237
  });
9094
9238
  }
9095
9239
  mount() {
9096
9240
  this.removeAttribute('data-fallback');
9097
9241
  const custom = this.querySelector('script[type="x-shader/x-fragment"]');
9098
- const frag = kind === 'shader' ? custom?.textContent || this.str('preset', 'gradient') : kind;
9242
+ const frag = kind === 'shader' ? custom?.textContent || this.str('preset', 'gradient') : kind === 'post' ? postFxShader(this.str('effects', 'vignette grain').split(/[\s,]+/)) : kind;
9099
9243
  const img = kind === 'shader' ? null : this.querySelector('img');
9100
9244
  if (kind !== 'shader' && !img)
9101
9245
  return this.fallback('no-image');
@@ -9120,7 +9264,7 @@ function make(kind) {
9120
9264
  }
9121
9265
  };
9122
9266
  const draw = () => {
9123
- q.resize();
9267
+ q.resize(this._scale);
9124
9268
  this.frame();
9125
9269
  };
9126
9270
  if (img && !(img.complete && img.naturalWidth)) {
@@ -9134,7 +9278,7 @@ function make(kind) {
9134
9278
  this.setAttribute('data-active', '');
9135
9279
  this.onCleanup(() => this.removeAttribute('data-active'));
9136
9280
  draw();
9137
- if (kind !== 'shader') {
9281
+ if (kind === 'distort' || kind === 'liquid') {
9138
9282
  const pos = (e) => {
9139
9283
  const r = this.getBoundingClientRect();
9140
9284
  this._mouse = [clamp((e.clientX - r.left) / (r.width || 1), 0, 1), clamp(1 - (e.clientY - r.top) / (r.height || 1), 0, 1)];
@@ -9151,7 +9295,7 @@ function make(kind) {
9151
9295
  // 4.0.1: also follow the element's own size (grid reflow, card expand…)
9152
9296
  if (typeof ResizeObserver !== 'undefined') {
9153
9297
  const ro = new ResizeObserver(() => {
9154
- q.resize();
9298
+ q.resize(this._scale);
9155
9299
  if (!this._id)
9156
9300
  this.frame();
9157
9301
  });
@@ -9160,9 +9304,20 @@ function make(kind) {
9160
9304
  }
9161
9305
  if (this.reduced)
9162
9306
  return; // one static frame, no loop
9307
+ // 4.8 adaptive quality: fps-driven resolution steps + battery saver frame cap
9308
+ const gov = glGovernor();
9309
+ const auto = this.str('quality', 'auto') !== 'high';
9310
+ gov.onScale = (sc) => {
9311
+ this._scale = sc;
9312
+ this.setAttribute('data-quality', String(sc));
9313
+ q.resize(sc);
9314
+ };
9315
+ if (auto)
9316
+ this.onCleanup(watchPowerSaver((saver) => ((gov.saver = saver), gov.onScale?.(gov.scale))));
9163
9317
  let visible = false;
9164
9318
  const loop = () => {
9165
- this.frame();
9319
+ if (!auto || gov.tick(now()))
9320
+ this.frame();
9166
9321
  this._id = raf(loop);
9167
9322
  };
9168
9323
  const sync = () => {
@@ -9173,7 +9328,7 @@ function make(kind) {
9173
9328
  };
9174
9329
  this.inView((v) => ((visible = v), sync()));
9175
9330
  this.listen(document, 'visibilitychange', sync);
9176
- this.listen(window, 'resize', () => q.resize(), { passive: true });
9331
+ this.listen(window, 'resize', () => q.resize(this._scale), { passive: true });
9177
9332
  this.onCleanup(() => caf(this._id));
9178
9333
  }
9179
9334
  };
@@ -9201,6 +9356,16 @@ function defineDistort(tag = 'usa-distort') {
9201
9356
  function defineLiquid(tag = 'usa-liquid') {
9202
9357
  return defineElement(tag, make('liquid'), { id: 'webgl', text: css$2 });
9203
9358
  }
9359
+ /**
9360
+ * `<usa-post-fx effects="vignette grain crt" intensity="0.6">` — GPU
9361
+ * post-processing over the `<img>` inside (4.8): `vignette` · `grain` ·
9362
+ * `chromatic` · `scanlines` · `crt` · `bloom` · `pixelate` · `duotone` ·
9363
+ * `glitch`, chained in order. `quality="high"` disables adaptive quality.
9364
+ * Fallback: the image with an approximate CSS filter.
9365
+ */
9366
+ function definePostFx(tag = 'usa-post-fx') {
9367
+ return defineElement(tag, make('post'), { id: 'webgl', text: css$2 });
9368
+ }
9204
9369
 
9205
9370
  /**
9206
9371
  * use-scroll-animate/components/webgl — lightweight canvas / WebGL (v3.4).
@@ -9213,6 +9378,7 @@ function defineWebglComponents() {
9213
9378
  defineShader();
9214
9379
  defineDistort();
9215
9380
  defineLiquid();
9381
+ definePostFx();
9216
9382
  }
9217
9383
 
9218
9384
  var css$1 = "";
@@ -10094,6 +10260,149 @@ function auditMotionA11y(root) {
10094
10260
  /** Every `<usa-*>` tag, for sweeping audits. */
10095
10261
  const ALL_TAGS = Object.values(COMPONENT_CATEGORIES).flat();
10096
10262
 
10263
+ /**
10264
+ * use-scroll-animate/components/bridge — native shell bridges (4.7).
10265
+ *
10266
+ * Keeps the web UI in sync with the host app's system settings when it runs
10267
+ * inside **WinUI 3 / WPF (WebView2)**, **.NET MAUI** (WebView / HybridWebView)
10268
+ * or **Flutter** (webview_flutter / flutter_inappwebview): the native side
10269
+ * sends "reduce motion", light / dark / high-contrast theme and accent color;
10270
+ * the page applies them to every `<usa-*>` component.
10271
+ *
10272
+ * Protocol (JSON, both directions):
10273
+ * - native → web `{ "type": "usa:settings", "reducedMotion": true, "theme": "dark", "accent": "#0078d4", "sensitivity": "gentle" }`
10274
+ * - web → native `{ "type": "usa:ready", "version": 1 }` on connect, `{ "type": "usa:request-settings" }`
10275
+ *
10276
+ * Hosts that can only run script call `window.usaNative.apply({...})`.
10277
+ * Samples: examples/native/{winui3,maui,flutter}.
10278
+ */
10279
+ const BRIDGE_PROTOCOL_VERSION = 1;
10280
+ const win = () => (typeof window === 'undefined' ? null : window);
10281
+ /** Which native shell (if any) hosts this page. */
10282
+ function detectNativeHost(channel = 'UsaBridge') {
10283
+ const w = win();
10284
+ if (!w)
10285
+ return 'browser';
10286
+ if (w.chrome?.webview?.postMessage)
10287
+ return 'webview2';
10288
+ if (w.HybridWebView?.SendRawMessage || w.HybridWebView?.SendRawMessageToDotNet || /\bMAUI\b/i.test(w.navigator?.userAgent || ''))
10289
+ return 'maui';
10290
+ if (w.flutter_inappwebview?.callHandler || w[channel]?.postMessage)
10291
+ return 'flutter';
10292
+ if (w.__TAURI__ || w.__TAURI_INTERNALS__)
10293
+ return 'tauri';
10294
+ if (w.process?.versions?.electron || /Electron\//.test(w.navigator?.userAgent || ''))
10295
+ return 'electron';
10296
+ return 'browser';
10297
+ }
10298
+ /** Send a JSON message to the native host (no-op in a plain browser). Returns whether it was sent. */
10299
+ function postToNative(message, channel = 'UsaBridge') {
10300
+ const w = win();
10301
+ if (!w)
10302
+ return false;
10303
+ const json = JSON.stringify(message);
10304
+ try {
10305
+ if (w.chrome?.webview?.postMessage)
10306
+ return w.chrome.webview.postMessage(message), true;
10307
+ if (w.HybridWebView?.SendRawMessage)
10308
+ return w.HybridWebView.SendRawMessage(json), true;
10309
+ if (w.HybridWebView?.SendRawMessageToDotNet)
10310
+ return w.HybridWebView.SendRawMessageToDotNet(json), true;
10311
+ if (w.flutter_inappwebview?.callHandler)
10312
+ return w.flutter_inappwebview.callHandler(channel, json), true;
10313
+ if (w[channel]?.postMessage)
10314
+ return w[channel].postMessage(json), true;
10315
+ }
10316
+ catch {
10317
+ /* host went away */
10318
+ }
10319
+ return false;
10320
+ }
10321
+ const THEMES = ['light', 'dark', 'high-contrast'];
10322
+ const COLOR = /^(#[0-9a-f]{3,8}|rgba?\([\d\s.,%]+\)|hsla?\([\d\s.,%deg]+\))$/i;
10323
+ /** Validate an incoming message (string or object); unknown fields are dropped. */
10324
+ function parseNativeSettings(data) {
10325
+ let d = data;
10326
+ if (typeof d === 'string') {
10327
+ try {
10328
+ d = JSON.parse(d);
10329
+ }
10330
+ catch {
10331
+ return null;
10332
+ }
10333
+ }
10334
+ if (!d || typeof d !== 'object' || d.type !== 'usa:settings')
10335
+ return null;
10336
+ const out = {};
10337
+ if (typeof d.reducedMotion === 'boolean')
10338
+ out.reducedMotion = d.reducedMotion;
10339
+ if (THEMES.includes(d.theme))
10340
+ out.theme = d.theme;
10341
+ if (typeof d.accent === 'string' && COLOR.test(d.accent.trim()))
10342
+ out.accent = d.accent.trim();
10343
+ if (MOTION_SENSITIVITY_LEVELS.includes(d.sensitivity))
10344
+ out.sensitivity = d.sensitivity;
10345
+ return out;
10346
+ }
10347
+ /**
10348
+ * Apply native settings: reduce motion → `configureComponents({ reducedMotion: 'reduce' })`
10349
+ * (`false` → follow the media query again); theme → `data-theme`, `data-usa-contrast`
10350
+ * and `color-scheme`; accent → `--usa-accent`; sensitivity → `motionSensitivity`.
10351
+ */
10352
+ function applyNativeSettings(s, root) {
10353
+ if (s.reducedMotion !== undefined)
10354
+ configureComponents({ reducedMotion: s.reducedMotion ? 'reduce' : 'user' });
10355
+ if (s.sensitivity)
10356
+ configureComponents({ motionSensitivity: s.sensitivity });
10357
+ const el = root || (typeof document !== 'undefined' ? document.documentElement : null);
10358
+ if (!el)
10359
+ return;
10360
+ if (s.theme) {
10361
+ el.setAttribute('data-theme', s.theme === 'high-contrast' ? 'dark' : s.theme);
10362
+ el.toggleAttribute('data-usa-contrast', s.theme === 'high-contrast');
10363
+ el.style.colorScheme = s.theme === 'light' ? 'light' : 'dark';
10364
+ }
10365
+ if (s.accent)
10366
+ el.style.setProperty('--usa-accent', s.accent);
10367
+ if (typeof document !== 'undefined')
10368
+ document.dispatchEvent(new CustomEvent('usa:native-settings', { detail: s }));
10369
+ }
10370
+ /**
10371
+ * Connect to the native shell: listens for `usa:settings` messages
10372
+ * (WebView2 `chrome.webview` messages, `window.postMessage`, or
10373
+ * `window.usaNative.apply()`), applies them, then announces `usa:ready` and
10374
+ * asks for the current settings. Returns `{ host, disconnect }`.
10375
+ */
10376
+ function connectNativeShell(options = {}) {
10377
+ const w = win();
10378
+ const channel = options.channel || 'UsaBridge';
10379
+ const host = detectNativeHost(channel);
10380
+ if (!w)
10381
+ return { host, disconnect: () => undefined };
10382
+ const handle = (data) => {
10383
+ const s = parseNativeSettings(data);
10384
+ if (!s)
10385
+ return;
10386
+ applyNativeSettings(s, options.root);
10387
+ options.onSettings?.(s, host);
10388
+ };
10389
+ const onMessage = (e) => handle(e.data);
10390
+ w.chrome?.webview?.addEventListener?.('message', onMessage);
10391
+ w.addEventListener?.('message', onMessage);
10392
+ const prev = w.usaNative;
10393
+ w.usaNative = { apply: (s) => handle({ ...s, type: 'usa:settings' }), version: BRIDGE_PROTOCOL_VERSION, host };
10394
+ postToNative({ type: 'usa:ready', version: BRIDGE_PROTOCOL_VERSION }, channel);
10395
+ postToNative({ type: 'usa:request-settings' }, channel);
10396
+ return {
10397
+ host,
10398
+ disconnect: () => {
10399
+ w.chrome?.webview?.removeEventListener?.('message', onMessage);
10400
+ w.removeEventListener?.('message', onMessage);
10401
+ w.usaNative = prev;
10402
+ },
10403
+ };
10404
+ }
10405
+
10097
10406
  /**
10098
10407
  * use-scroll-animate/components
10099
10408
  *
@@ -10163,11 +10472,13 @@ onDemandStyles(STYLE_BASE);
10163
10472
  exports.ALL_TAGS = ALL_TAGS;
10164
10473
  exports.AMBIENT_EFFECTS = AMBIENT_EFFECTS;
10165
10474
  exports.ANIM_ICONS = ANIM_ICONS;
10475
+ exports.BRIDGE_PROTOCOL_VERSION = BRIDGE_PROTOCOL_VERSION;
10166
10476
  exports.BUTTON_DEFORMS = BUTTON_DEFORMS;
10167
10477
  exports.CARD_EFFECTS = CARD_EFFECTS;
10168
10478
  exports.CLICK_EFFECTS = CLICK_EFFECTS;
10169
10479
  exports.COMPONENT_CATEGORIES = COMPONENT_CATEGORIES;
10170
10480
  exports.CURSOR_MODES = CURSOR_MODES;
10481
+ exports.GL_FALLBACKS = GL_FALLBACKS;
10171
10482
  exports.JOINING_SCRIPT = JOINING_SCRIPT;
10172
10483
  exports.LIVE_REGION_IDS = LIVE_REGION_IDS;
10173
10484
  exports.MASK_SHAPES = MASK_SHAPES;
@@ -10179,6 +10490,8 @@ exports.MOTION_TOKENS = MOTION_TOKENS;
10179
10490
  exports.PACKS = PACKS;
10180
10491
  exports.PACK_PRIMITIVES = PACK_PRIMITIVES;
10181
10492
  exports.PAGE_EFFECTS = PAGE_EFFECTS;
10493
+ exports.PARTICLE_PRESETS = PARTICLE_PRESETS;
10494
+ exports.POST_EFFECTS = POST_EFFECTS;
10182
10495
  exports.REVEAL_EFFECTS = REVEAL_EFFECTS;
10183
10496
  exports.SENSITIVITY_CSS = SENSITIVITY_CSS;
10184
10497
  exports.SHADERS = SHADERS;
@@ -10195,6 +10508,7 @@ exports.adoptVariants = adoptVariants;
10195
10508
  exports.animationBudget = animationBudget;
10196
10509
  exports.announce = announce;
10197
10510
  exports.applyMotionTokens = applyMotionTokens;
10511
+ exports.applyNativeSettings = applyNativeSettings;
10198
10512
  exports.applyPack = applyPack;
10199
10513
  exports.auditMotionA11y = auditMotionA11y;
10200
10514
  exports.autoAnimate = autoAnimate;
@@ -10203,6 +10517,7 @@ exports.burst = burst;
10203
10517
  exports.categoryOf = categoryOf;
10204
10518
  exports.confetti = confetti;
10205
10519
  exports.configureComponents = configureComponents;
10520
+ exports.connectNativeShell = connectNativeShell;
10206
10521
  exports.countUp = countUp;
10207
10522
  exports.createSpring = createSpring;
10208
10523
  exports.defineAccordion = defineAccordion;
@@ -10271,6 +10586,7 @@ exports.defineParticles = defineParticles;
10271
10586
  exports.definePhysicsComponents = definePhysicsComponents;
10272
10587
  exports.definePinchZoom = definePinchZoom;
10273
10588
  exports.definePopover = definePopover;
10589
+ exports.definePostFx = definePostFx;
10274
10590
  exports.definePress = definePress;
10275
10591
  exports.defineProgress = defineProgress;
10276
10592
  exports.definePullRefresh = definePullRefresh;
@@ -10311,6 +10627,7 @@ exports.defineViewSwitch = defineViewSwitch;
10311
10627
  exports.defineWaterRipple = defineWaterRipple;
10312
10628
  exports.defineWaveText = defineWaveText;
10313
10629
  exports.defineWebglComponents = defineWebglComponents;
10630
+ exports.detectNativeHost = detectNativeHost;
10314
10631
  exports.deviceTilt = deviceTilt;
10315
10632
  exports.drawLines = drawLines;
10316
10633
  exports.easeOutExpo = easeOutExpo;
@@ -10324,6 +10641,8 @@ exports.gesture = gesture;
10324
10641
  exports.getMotionIntensity = getMotionIntensity;
10325
10642
  exports.getMotionSensitivity = getMotionSensitivity;
10326
10643
  exports.getMotionTokens = getMotionTokens;
10644
+ exports.glFallbackCss = glFallbackCss;
10645
+ exports.glGovernor = glGovernor;
10327
10646
  exports.glQuad = glQuad;
10328
10647
  exports.graphemes = graphemes;
10329
10648
  exports.haptic = haptic;
@@ -10350,8 +10669,11 @@ exports.orientationToTilt = orientationToTilt;
10350
10669
  exports.pageTransition = pageTransition;
10351
10670
  exports.parseDuration = parseDuration;
10352
10671
  exports.parseEasing = parseEasing;
10672
+ exports.parseNativeSettings = parseNativeSettings;
10353
10673
  exports.pathsCompatible = pathsCompatible;
10354
10674
  exports.pinchScale = pinchScale;
10675
+ exports.postFxShader = postFxShader;
10676
+ exports.postToNative = postToNative;
10355
10677
  exports.prefersReducedMotion = prefersReducedMotion;
10356
10678
  exports.projectInertia = projectInertia;
10357
10679
  exports.readScrollProgress = readScrollProgress;
@@ -10395,4 +10717,5 @@ exports.themeTransition = themeTransition;
10395
10717
  exports.timeline = timeline;
10396
10718
  exports.toast = toast;
10397
10719
  exports.viewTransition = viewTransition;
10720
+ exports.watchPowerSaver = watchPowerSaver;
10398
10721
  //# sourceMappingURL=lite.cjs.map