interactive-bg 0.1.0 → 0.2.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 (122) hide show
  1. package/README.md +55 -7
  2. package/dist/fluid/canvas2d.cjs +14 -0
  3. package/dist/fluid/canvas2d.cjs.map +1 -0
  4. package/dist/fluid/canvas2d.d.cts +5 -0
  5. package/dist/fluid/canvas2d.d.ts +5 -0
  6. package/dist/fluid/canvas2d.js +13 -0
  7. package/dist/fluid/canvas2d.js.map +1 -0
  8. package/dist/fluid/index.cjs +16 -0
  9. package/dist/fluid/index.cjs.map +1 -0
  10. package/dist/fluid/index.d.cts +5 -0
  11. package/dist/fluid/index.d.ts +5 -0
  12. package/dist/fluid/index.js +15 -0
  13. package/dist/fluid/index.js.map +1 -0
  14. package/dist/fluid/webgl.cjs +14 -0
  15. package/dist/fluid/webgl.cjs.map +1 -0
  16. package/dist/fluid/webgl.d.cts +5 -0
  17. package/dist/fluid/webgl.d.ts +5 -0
  18. package/dist/fluid/webgl.js +13 -0
  19. package/dist/fluid/webgl.js.map +1 -0
  20. package/dist/index.cjs +4 -0
  21. package/dist/index.d.cts +4 -0
  22. package/dist/index.d.ts +4 -0
  23. package/dist/index.js +3 -1
  24. package/dist/index.umd.js +255 -12
  25. package/dist/index.umd.js.map +1 -1
  26. package/dist/shimmer/canvas2d.cjs +14 -0
  27. package/dist/shimmer/canvas2d.cjs.map +1 -0
  28. package/dist/shimmer/canvas2d.d.cts +5 -0
  29. package/dist/shimmer/canvas2d.d.ts +5 -0
  30. package/dist/shimmer/canvas2d.js +13 -0
  31. package/dist/shimmer/canvas2d.js.map +1 -0
  32. package/dist/shimmer/index.cjs +16 -0
  33. package/dist/shimmer/index.cjs.map +1 -0
  34. package/dist/shimmer/index.d.cts +5 -0
  35. package/dist/shimmer/index.d.ts +5 -0
  36. package/dist/shimmer/index.js +15 -0
  37. package/dist/shimmer/index.js.map +1 -0
  38. package/dist/shimmer/webgl.cjs +14 -0
  39. package/dist/shimmer/webgl.cjs.map +1 -0
  40. package/dist/shimmer/webgl.d.cts +5 -0
  41. package/dist/shimmer/webgl.d.ts +5 -0
  42. package/dist/shimmer/webgl.js +13 -0
  43. package/dist/shimmer/webgl.js.map +1 -0
  44. package/dist/styles/fluid/canvas2d.cjs +155 -0
  45. package/dist/styles/fluid/canvas2d.cjs.map +1 -0
  46. package/dist/styles/fluid/canvas2d.d.cts +8 -0
  47. package/dist/styles/fluid/canvas2d.d.ts +8 -0
  48. package/dist/styles/fluid/canvas2d.js +155 -0
  49. package/dist/styles/fluid/canvas2d.js.map +1 -0
  50. package/dist/styles/fluid/color.cjs +40 -0
  51. package/dist/styles/fluid/color.cjs.map +1 -0
  52. package/dist/styles/fluid/color.d.cts +4 -0
  53. package/dist/styles/fluid/color.d.ts +4 -0
  54. package/dist/styles/fluid/color.js +40 -0
  55. package/dist/styles/fluid/color.js.map +1 -0
  56. package/dist/styles/fluid/definition.cjs +14 -0
  57. package/dist/styles/fluid/definition.cjs.map +1 -0
  58. package/dist/styles/fluid/definition.d.cts +8 -0
  59. package/dist/styles/fluid/definition.d.ts +8 -0
  60. package/dist/styles/fluid/definition.js +15 -0
  61. package/dist/styles/fluid/definition.js.map +1 -0
  62. package/dist/styles/fluid/options.cjs +29 -0
  63. package/dist/styles/fluid/options.cjs.map +1 -0
  64. package/dist/styles/fluid/options.d.cts +31 -0
  65. package/dist/styles/fluid/options.d.ts +31 -0
  66. package/dist/styles/fluid/options.js +28 -0
  67. package/dist/styles/fluid/options.js.map +1 -0
  68. package/dist/styles/fluid/settings.cjs +32 -0
  69. package/dist/styles/fluid/settings.cjs.map +1 -0
  70. package/dist/styles/fluid/settings.d.cts +24 -0
  71. package/dist/styles/fluid/settings.d.ts +24 -0
  72. package/dist/styles/fluid/settings.js +23 -0
  73. package/dist/styles/fluid/settings.js.map +1 -0
  74. package/dist/styles/fluid/simulation.cjs +183 -0
  75. package/dist/styles/fluid/simulation.cjs.map +1 -0
  76. package/dist/styles/fluid/simulation.d.cts +22 -0
  77. package/dist/styles/fluid/simulation.d.ts +22 -0
  78. package/dist/styles/fluid/simulation.js +183 -0
  79. package/dist/styles/fluid/simulation.js.map +1 -0
  80. package/dist/styles/fluid/size.cjs +17 -0
  81. package/dist/styles/fluid/size.cjs.map +1 -0
  82. package/dist/styles/fluid/size.d.cts +5 -0
  83. package/dist/styles/fluid/size.d.ts +5 -0
  84. package/dist/styles/fluid/size.js +17 -0
  85. package/dist/styles/fluid/size.js.map +1 -0
  86. package/dist/styles/fluid/webgl.cjs +607 -0
  87. package/dist/styles/fluid/webgl.cjs.map +1 -0
  88. package/dist/styles/fluid/webgl.d.cts +3 -0
  89. package/dist/styles/fluid/webgl.d.ts +3 -0
  90. package/dist/styles/fluid/webgl.js +607 -0
  91. package/dist/styles/fluid/webgl.js.map +1 -0
  92. package/dist/styles/shimmer/canvas2d.cjs +84 -0
  93. package/dist/styles/shimmer/canvas2d.cjs.map +1 -0
  94. package/dist/styles/shimmer/canvas2d.d.cts +3 -0
  95. package/dist/styles/shimmer/canvas2d.d.ts +3 -0
  96. package/dist/styles/shimmer/canvas2d.js +84 -0
  97. package/dist/styles/shimmer/canvas2d.js.map +1 -0
  98. package/dist/styles/shimmer/definition.cjs +15 -0
  99. package/dist/styles/shimmer/definition.cjs.map +1 -0
  100. package/dist/styles/shimmer/definition.d.cts +8 -0
  101. package/dist/styles/shimmer/definition.d.ts +8 -0
  102. package/dist/styles/shimmer/definition.js +16 -0
  103. package/dist/styles/shimmer/definition.js.map +1 -0
  104. package/dist/styles/shimmer/field.cjs +79 -0
  105. package/dist/styles/shimmer/field.cjs.map +1 -0
  106. package/dist/styles/shimmer/field.d.cts +11 -0
  107. package/dist/styles/shimmer/field.d.ts +11 -0
  108. package/dist/styles/shimmer/field.js +78 -0
  109. package/dist/styles/shimmer/field.js.map +1 -0
  110. package/dist/styles/shimmer/options.cjs +28 -0
  111. package/dist/styles/shimmer/options.cjs.map +1 -0
  112. package/dist/styles/shimmer/options.d.cts +22 -0
  113. package/dist/styles/shimmer/options.d.ts +22 -0
  114. package/dist/styles/shimmer/options.js +27 -0
  115. package/dist/styles/shimmer/options.js.map +1 -0
  116. package/dist/styles/shimmer/webgl.cjs +89 -0
  117. package/dist/styles/shimmer/webgl.cjs.map +1 -0
  118. package/dist/styles/shimmer/webgl.d.cts +4 -0
  119. package/dist/styles/shimmer/webgl.d.ts +4 -0
  120. package/dist/styles/shimmer/webgl.js +89 -0
  121. package/dist/styles/shimmer/webgl.js.map +1 -0
  122. package/package.json +76 -2
@@ -0,0 +1,84 @@
1
+ const require_color = require("../../core/color.cjs");
2
+ const require_field = require("./field.cjs");
3
+ const require_options = require("./options.cjs");
4
+ // src/styles/shimmer/canvas2d.ts
5
+ function fieldSize(width, height) {
6
+ const aspect = Math.max(1, width) / Math.max(1, height);
7
+ if (aspect >= 1) return {
8
+ width: 128,
9
+ height: Math.max(1, Math.round(128 / aspect))
10
+ };
11
+ return {
12
+ width: Math.max(1, Math.round(128 * aspect)),
13
+ height: 128
14
+ };
15
+ }
16
+ /** CPU mirror of the analytic band field, at low resolution and upscaled. */
17
+ var CPU_FPS = 12;
18
+ function createShimmerCanvas2D(ctx, initial) {
19
+ let options = require_options.resolveShimmerOptions(initial);
20
+ let clock = 0;
21
+ let accumulator = 0;
22
+ let hasFrame = false;
23
+ let target = null;
24
+ let targetCtx = null;
25
+ let image = null;
26
+ let fieldWidth = 0;
27
+ let fieldHeight = 0;
28
+ let builtWidth = 0;
29
+ let builtHeight = 0;
30
+ function allocate(width, height) {
31
+ const size = fieldSize(width, height);
32
+ fieldWidth = size.width;
33
+ fieldHeight = size.height;
34
+ target = document.createElement("canvas");
35
+ target.width = fieldWidth;
36
+ target.height = fieldHeight;
37
+ targetCtx = target.getContext("2d");
38
+ image = targetCtx?.createImageData(fieldWidth, fieldHeight) ?? null;
39
+ builtWidth = width;
40
+ builtHeight = height;
41
+ accumulator = 0;
42
+ hasFrame = false;
43
+ }
44
+ return {
45
+ update(next) {
46
+ options = require_options.resolveShimmerOptions(next);
47
+ },
48
+ render(frame) {
49
+ if (!image || frame.width !== builtWidth || frame.height !== builtHeight) allocate(frame.width, frame.height);
50
+ if (!image || !target || !targetCtx) return;
51
+ if (frame.reducedMotion) {
52
+ if (!hasFrame) {
53
+ const [baseR, baseG, baseB] = require_color.parseColor(options.baseColor);
54
+ require_field.renderShimmer(image, fieldWidth, fieldHeight, clock, .5, .5, 0, options, [
55
+ baseR,
56
+ baseG,
57
+ baseB
58
+ ]);
59
+ targetCtx.putImageData(image, 0, 0);
60
+ hasFrame = true;
61
+ }
62
+ } else {
63
+ accumulator += frame.dt;
64
+ if (!hasFrame || accumulator >= 1 / CPU_FPS) {
65
+ clock += accumulator;
66
+ accumulator = 0;
67
+ const [baseR, baseG, baseB] = require_color.parseColor(options.baseColor);
68
+ require_field.renderShimmer(image, fieldWidth, fieldHeight, clock, frame.pointer.x / Math.max(1, frame.width), frame.pointer.y / Math.max(1, frame.height), frame.pointer.influence, options, [
69
+ baseR,
70
+ baseG,
71
+ baseB
72
+ ]);
73
+ targetCtx.putImageData(image, 0, 0);
74
+ hasFrame = true;
75
+ }
76
+ }
77
+ ctx.imageSmoothingEnabled = true;
78
+ ctx.drawImage(target, 0, 0, frame.width, frame.height);
79
+ }
80
+ };
81
+ }
82
+ exports.createShimmerCanvas2D = createShimmerCanvas2D;
83
+
84
+ //# sourceMappingURL=canvas2d.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"canvas2d.cjs","names":["resolveShimmerOptions","parseColor"],"sources":["../../../src/styles/shimmer/canvas2d.ts"],"sourcesContent":["import { parseColor } from '../../core/color';\nimport type { Frame, StyleRenderer } from '../../core/style';\nimport { CPU_RESOLUTION, renderShimmer } from './field';\nimport { resolveShimmerOptions, type ShimmerOptions } from './options';\n\nfunction fieldSize(width: number, height: number): { width: number; height: number } {\n const aspect = Math.max(1, width) / Math.max(1, height);\n if (aspect >= 1) return { width: CPU_RESOLUTION, height: Math.max(1, Math.round(CPU_RESOLUTION / aspect)) };\n return { width: Math.max(1, Math.round(CPU_RESOLUTION * aspect)), height: CPU_RESOLUTION };\n}\n\n/** CPU mirror of the analytic band field, at low resolution and upscaled. */\nconst CPU_FPS = 12;\n\nexport function createShimmerCanvas2D(\n ctx: CanvasRenderingContext2D,\n initial: ShimmerOptions,\n): StyleRenderer<ShimmerOptions> {\n let options = resolveShimmerOptions(initial);\n let clock = 0;\n let accumulator = 0;\n let hasFrame = false;\n\n let target: HTMLCanvasElement | null = null;\n let targetCtx: CanvasRenderingContext2D | null = null;\n let image: ImageData | null = null;\n let fieldWidth = 0;\n let fieldHeight = 0;\n let builtWidth = 0;\n let builtHeight = 0;\n\n function allocate(width: number, height: number): void {\n const size = fieldSize(width, height);\n fieldWidth = size.width;\n fieldHeight = size.height;\n target = document.createElement('canvas');\n target.width = fieldWidth;\n target.height = fieldHeight;\n targetCtx = target.getContext('2d');\n image = targetCtx?.createImageData(fieldWidth, fieldHeight) ?? null;\n builtWidth = width;\n builtHeight = height;\n accumulator = 0;\n hasFrame = false;\n }\n\n return {\n update(next) {\n options = resolveShimmerOptions(next);\n },\n render(frame: Frame) {\n if (!image || frame.width !== builtWidth || frame.height !== builtHeight) {\n allocate(frame.width, frame.height);\n }\n if (!image || !target || !targetCtx) return;\n\n // Recompute the field at 20 fps; the engine may call us at 60 fps while\n // the pointer moves, but this field is slow and soft.\n if (frame.reducedMotion) {\n if (!hasFrame) {\n const [baseR, baseG, baseB] = parseColor(options.baseColor);\n renderShimmer(\n image,\n fieldWidth,\n fieldHeight,\n clock,\n 0.5,\n 0.5,\n 0,\n options,\n [baseR, baseG, baseB],\n );\n targetCtx.putImageData(image, 0, 0);\n hasFrame = true;\n }\n } else {\n accumulator += frame.dt;\n if (!hasFrame || accumulator >= 1 / CPU_FPS) {\n clock += accumulator;\n accumulator = 0;\n const [baseR, baseG, baseB] = parseColor(options.baseColor);\n renderShimmer(\n image,\n fieldWidth,\n fieldHeight,\n clock,\n frame.pointer.x / Math.max(1, frame.width),\n frame.pointer.y / Math.max(1, frame.height),\n frame.pointer.influence,\n options,\n [baseR, baseG, baseB],\n );\n targetCtx.putImageData(image, 0, 0);\n hasFrame = true;\n }\n }\n ctx.imageSmoothingEnabled = true;\n ctx.drawImage(target, 0, 0, frame.width, frame.height);\n },\n };\n}\n"],"mappings":";;;;AAKA,SAAS,UAAU,OAAe,QAAmD;CACnF,MAAM,SAAS,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,IAAI,GAAG,MAAM;CACtD,IAAI,UAAU,GAAG,OAAO;EAAE,OAAA;EAAuB,QAAQ,KAAK,IAAI,GAAG,KAAK,MAAA,MAAuB,MAAM,CAAC;CAAE;CAC1G,OAAO;EAAE,OAAO,KAAK,IAAI,GAAG,KAAK,MAAA,MAAuB,MAAM,CAAC;EAAG,QAAA;CAAuB;AAC3F;;AAGA,IAAM,UAAU;AAEhB,SAAgB,sBACd,KACA,SAC+B;CAC/B,IAAI,UAAUA,gBAAAA,sBAAsB,OAAO;CAC3C,IAAI,QAAQ;CACZ,IAAI,cAAc;CAClB,IAAI,WAAW;CAEf,IAAI,SAAmC;CACvC,IAAI,YAA6C;CACjD,IAAI,QAA0B;CAC9B,IAAI,aAAa;CACjB,IAAI,cAAc;CAClB,IAAI,aAAa;CACjB,IAAI,cAAc;CAElB,SAAS,SAAS,OAAe,QAAsB;EACrD,MAAM,OAAO,UAAU,OAAO,MAAM;EACpC,aAAa,KAAK;EAClB,cAAc,KAAK;EACnB,SAAS,SAAS,cAAc,QAAQ;EACxC,OAAO,QAAQ;EACf,OAAO,SAAS;EAChB,YAAY,OAAO,WAAW,IAAI;EAClC,QAAQ,WAAW,gBAAgB,YAAY,WAAW,KAAK;EAC/D,aAAa;EACb,cAAc;EACd,cAAc;EACd,WAAW;CACb;CAEA,OAAO;EACL,OAAO,MAAM;GACX,UAAUA,gBAAAA,sBAAsB,IAAI;EACtC;EACA,OAAO,OAAc;GACnB,IAAI,CAAC,SAAS,MAAM,UAAU,cAAc,MAAM,WAAW,aAC3D,SAAS,MAAM,OAAO,MAAM,MAAM;GAEpC,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,WAAW;GAIrC,IAAI,MAAM,eACJ;QAAA,CAAC,UAAU;KACb,MAAM,CAAC,OAAO,OAAO,SAASC,cAAAA,WAAW,QAAQ,SAAS;KAC1D,cAAA,cACE,OACA,YACA,aACA,OACA,IACA,IACA,GACA,SACA;MAAC;MAAO;MAAO;KAAK,CACtB;KACA,UAAU,aAAa,OAAO,GAAG,CAAC;KAClC,WAAW;IACb;UACK;IACL,eAAe,MAAM;IACrB,IAAI,CAAC,YAAY,eAAe,IAAI,SAAS;KAC3C,SAAS;KACT,cAAc;KACd,MAAM,CAAC,OAAO,OAAO,SAASA,cAAAA,WAAW,QAAQ,SAAS;KAC1D,cAAA,cACE,OACA,YACA,aACA,OACA,MAAM,QAAQ,IAAI,KAAK,IAAI,GAAG,MAAM,KAAK,GACzC,MAAM,QAAQ,IAAI,KAAK,IAAI,GAAG,MAAM,MAAM,GAC1C,MAAM,QAAQ,WACd,SACA;MAAC;MAAO;MAAO;KAAK,CACtB;KACA,UAAU,aAAa,OAAO,GAAG,CAAC;KAClC,WAAW;IACb;GACF;GACA,IAAI,wBAAwB;GAC5B,IAAI,UAAU,QAAQ,GAAG,GAAG,MAAM,OAAO,MAAM,MAAM;EACvD;CACF;AACF"}
@@ -0,0 +1,3 @@
1
+ import type { StyleRenderer } from '../../core/style.cjs';
2
+ import { type ShimmerOptions } from './options.cjs';
3
+ export declare function createShimmerCanvas2D(ctx: CanvasRenderingContext2D, initial: ShimmerOptions): StyleRenderer<ShimmerOptions>;
@@ -0,0 +1,3 @@
1
+ import type { StyleRenderer } from '../../core/style.js';
2
+ import { type ShimmerOptions } from './options.js';
3
+ export declare function createShimmerCanvas2D(ctx: CanvasRenderingContext2D, initial: ShimmerOptions): StyleRenderer<ShimmerOptions>;
@@ -0,0 +1,84 @@
1
+ import { parseColor } from "../../core/color.js";
2
+ import { renderShimmer } from "./field.js";
3
+ import { resolveShimmerOptions } from "./options.js";
4
+ // src/styles/shimmer/canvas2d.ts
5
+ function fieldSize(width, height) {
6
+ const aspect = Math.max(1, width) / Math.max(1, height);
7
+ if (aspect >= 1) return {
8
+ width: 128,
9
+ height: Math.max(1, Math.round(128 / aspect))
10
+ };
11
+ return {
12
+ width: Math.max(1, Math.round(128 * aspect)),
13
+ height: 128
14
+ };
15
+ }
16
+ /** CPU mirror of the analytic band field, at low resolution and upscaled. */
17
+ var CPU_FPS = 12;
18
+ function createShimmerCanvas2D(ctx, initial) {
19
+ let options = resolveShimmerOptions(initial);
20
+ let clock = 0;
21
+ let accumulator = 0;
22
+ let hasFrame = false;
23
+ let target = null;
24
+ let targetCtx = null;
25
+ let image = null;
26
+ let fieldWidth = 0;
27
+ let fieldHeight = 0;
28
+ let builtWidth = 0;
29
+ let builtHeight = 0;
30
+ function allocate(width, height) {
31
+ const size = fieldSize(width, height);
32
+ fieldWidth = size.width;
33
+ fieldHeight = size.height;
34
+ target = document.createElement("canvas");
35
+ target.width = fieldWidth;
36
+ target.height = fieldHeight;
37
+ targetCtx = target.getContext("2d");
38
+ image = targetCtx?.createImageData(fieldWidth, fieldHeight) ?? null;
39
+ builtWidth = width;
40
+ builtHeight = height;
41
+ accumulator = 0;
42
+ hasFrame = false;
43
+ }
44
+ return {
45
+ update(next) {
46
+ options = resolveShimmerOptions(next);
47
+ },
48
+ render(frame) {
49
+ if (!image || frame.width !== builtWidth || frame.height !== builtHeight) allocate(frame.width, frame.height);
50
+ if (!image || !target || !targetCtx) return;
51
+ if (frame.reducedMotion) {
52
+ if (!hasFrame) {
53
+ const [baseR, baseG, baseB] = parseColor(options.baseColor);
54
+ renderShimmer(image, fieldWidth, fieldHeight, clock, .5, .5, 0, options, [
55
+ baseR,
56
+ baseG,
57
+ baseB
58
+ ]);
59
+ targetCtx.putImageData(image, 0, 0);
60
+ hasFrame = true;
61
+ }
62
+ } else {
63
+ accumulator += frame.dt;
64
+ if (!hasFrame || accumulator >= 1 / CPU_FPS) {
65
+ clock += accumulator;
66
+ accumulator = 0;
67
+ const [baseR, baseG, baseB] = parseColor(options.baseColor);
68
+ renderShimmer(image, fieldWidth, fieldHeight, clock, frame.pointer.x / Math.max(1, frame.width), frame.pointer.y / Math.max(1, frame.height), frame.pointer.influence, options, [
69
+ baseR,
70
+ baseG,
71
+ baseB
72
+ ]);
73
+ targetCtx.putImageData(image, 0, 0);
74
+ hasFrame = true;
75
+ }
76
+ }
77
+ ctx.imageSmoothingEnabled = true;
78
+ ctx.drawImage(target, 0, 0, frame.width, frame.height);
79
+ }
80
+ };
81
+ }
82
+ export { createShimmerCanvas2D };
83
+
84
+ //# sourceMappingURL=canvas2d.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"canvas2d.js","names":[],"sources":["../../../src/styles/shimmer/canvas2d.ts"],"sourcesContent":["import { parseColor } from '../../core/color';\nimport type { Frame, StyleRenderer } from '../../core/style';\nimport { CPU_RESOLUTION, renderShimmer } from './field';\nimport { resolveShimmerOptions, type ShimmerOptions } from './options';\n\nfunction fieldSize(width: number, height: number): { width: number; height: number } {\n const aspect = Math.max(1, width) / Math.max(1, height);\n if (aspect >= 1) return { width: CPU_RESOLUTION, height: Math.max(1, Math.round(CPU_RESOLUTION / aspect)) };\n return { width: Math.max(1, Math.round(CPU_RESOLUTION * aspect)), height: CPU_RESOLUTION };\n}\n\n/** CPU mirror of the analytic band field, at low resolution and upscaled. */\nconst CPU_FPS = 12;\n\nexport function createShimmerCanvas2D(\n ctx: CanvasRenderingContext2D,\n initial: ShimmerOptions,\n): StyleRenderer<ShimmerOptions> {\n let options = resolveShimmerOptions(initial);\n let clock = 0;\n let accumulator = 0;\n let hasFrame = false;\n\n let target: HTMLCanvasElement | null = null;\n let targetCtx: CanvasRenderingContext2D | null = null;\n let image: ImageData | null = null;\n let fieldWidth = 0;\n let fieldHeight = 0;\n let builtWidth = 0;\n let builtHeight = 0;\n\n function allocate(width: number, height: number): void {\n const size = fieldSize(width, height);\n fieldWidth = size.width;\n fieldHeight = size.height;\n target = document.createElement('canvas');\n target.width = fieldWidth;\n target.height = fieldHeight;\n targetCtx = target.getContext('2d');\n image = targetCtx?.createImageData(fieldWidth, fieldHeight) ?? null;\n builtWidth = width;\n builtHeight = height;\n accumulator = 0;\n hasFrame = false;\n }\n\n return {\n update(next) {\n options = resolveShimmerOptions(next);\n },\n render(frame: Frame) {\n if (!image || frame.width !== builtWidth || frame.height !== builtHeight) {\n allocate(frame.width, frame.height);\n }\n if (!image || !target || !targetCtx) return;\n\n // Recompute the field at 20 fps; the engine may call us at 60 fps while\n // the pointer moves, but this field is slow and soft.\n if (frame.reducedMotion) {\n if (!hasFrame) {\n const [baseR, baseG, baseB] = parseColor(options.baseColor);\n renderShimmer(\n image,\n fieldWidth,\n fieldHeight,\n clock,\n 0.5,\n 0.5,\n 0,\n options,\n [baseR, baseG, baseB],\n );\n targetCtx.putImageData(image, 0, 0);\n hasFrame = true;\n }\n } else {\n accumulator += frame.dt;\n if (!hasFrame || accumulator >= 1 / CPU_FPS) {\n clock += accumulator;\n accumulator = 0;\n const [baseR, baseG, baseB] = parseColor(options.baseColor);\n renderShimmer(\n image,\n fieldWidth,\n fieldHeight,\n clock,\n frame.pointer.x / Math.max(1, frame.width),\n frame.pointer.y / Math.max(1, frame.height),\n frame.pointer.influence,\n options,\n [baseR, baseG, baseB],\n );\n targetCtx.putImageData(image, 0, 0);\n hasFrame = true;\n }\n }\n ctx.imageSmoothingEnabled = true;\n ctx.drawImage(target, 0, 0, frame.width, frame.height);\n },\n };\n}\n"],"mappings":";;;;AAKA,SAAS,UAAU,OAAe,QAAmD;CACnF,MAAM,SAAS,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,IAAI,GAAG,MAAM;CACtD,IAAI,UAAU,GAAG,OAAO;EAAE,OAAA;EAAuB,QAAQ,KAAK,IAAI,GAAG,KAAK,MAAA,MAAuB,MAAM,CAAC;CAAE;CAC1G,OAAO;EAAE,OAAO,KAAK,IAAI,GAAG,KAAK,MAAA,MAAuB,MAAM,CAAC;EAAG,QAAA;CAAuB;AAC3F;;AAGA,IAAM,UAAU;AAEhB,SAAgB,sBACd,KACA,SAC+B;CAC/B,IAAI,UAAU,sBAAsB,OAAO;CAC3C,IAAI,QAAQ;CACZ,IAAI,cAAc;CAClB,IAAI,WAAW;CAEf,IAAI,SAAmC;CACvC,IAAI,YAA6C;CACjD,IAAI,QAA0B;CAC9B,IAAI,aAAa;CACjB,IAAI,cAAc;CAClB,IAAI,aAAa;CACjB,IAAI,cAAc;CAElB,SAAS,SAAS,OAAe,QAAsB;EACrD,MAAM,OAAO,UAAU,OAAO,MAAM;EACpC,aAAa,KAAK;EAClB,cAAc,KAAK;EACnB,SAAS,SAAS,cAAc,QAAQ;EACxC,OAAO,QAAQ;EACf,OAAO,SAAS;EAChB,YAAY,OAAO,WAAW,IAAI;EAClC,QAAQ,WAAW,gBAAgB,YAAY,WAAW,KAAK;EAC/D,aAAa;EACb,cAAc;EACd,cAAc;EACd,WAAW;CACb;CAEA,OAAO;EACL,OAAO,MAAM;GACX,UAAU,sBAAsB,IAAI;EACtC;EACA,OAAO,OAAc;GACnB,IAAI,CAAC,SAAS,MAAM,UAAU,cAAc,MAAM,WAAW,aAC3D,SAAS,MAAM,OAAO,MAAM,MAAM;GAEpC,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,WAAW;GAIrC,IAAI,MAAM,eACJ;QAAA,CAAC,UAAU;KACb,MAAM,CAAC,OAAO,OAAO,SAAS,WAAW,QAAQ,SAAS;KAC1D,cACE,OACA,YACA,aACA,OACA,IACA,IACA,GACA,SACA;MAAC;MAAO;MAAO;KAAK,CACtB;KACA,UAAU,aAAa,OAAO,GAAG,CAAC;KAClC,WAAW;IACb;UACK;IACL,eAAe,MAAM;IACrB,IAAI,CAAC,YAAY,eAAe,IAAI,SAAS;KAC3C,SAAS;KACT,cAAc;KACd,MAAM,CAAC,OAAO,OAAO,SAAS,WAAW,QAAQ,SAAS;KAC1D,cACE,OACA,YACA,aACA,OACA,MAAM,QAAQ,IAAI,KAAK,IAAI,GAAG,MAAM,KAAK,GACzC,MAAM,QAAQ,IAAI,KAAK,IAAI,GAAG,MAAM,MAAM,GAC1C,MAAM,QAAQ,WACd,SACA;MAAC;MAAO;MAAO;KAAK,CACtB;KACA,UAAU,aAAa,OAAO,GAAG,CAAC;KAClC,WAAW;IACb;GACF;GACA,IAAI,wBAAwB;GAC5B,IAAI,UAAU,QAAQ,GAAG,GAAG,MAAM,OAAO,MAAM,MAAM;EACvD;CACF;AACF"}
@@ -0,0 +1,15 @@
1
+ // src/styles/shimmer/definition.ts
2
+ /**
3
+ * Iridescent light bands, reproduced from the reference's `sr-ground-canvas`:
4
+ * six coupled sine/cosine warps, per-channel offsets and a high-power sine
5
+ * field, mixed 80% with a base colour.
6
+ */
7
+ var shimmerStyle = {
8
+ name: "shimmer",
9
+ defaults: require("./options.cjs").shimmerDefaults,
10
+ animates: (options) => options.speed > 0,
11
+ idleFps: () => 16
12
+ };
13
+ exports.shimmerStyle = shimmerStyle;
14
+
15
+ //# sourceMappingURL=definition.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"definition.cjs","names":["shimmerDefaults"],"sources":["../../../src/styles/shimmer/definition.ts"],"sourcesContent":["import type { StyleBase } from '../../core/style';\nimport { shimmerDefaults, type ShimmerOptions } from './options';\n\n/**\n * Iridescent light bands, reproduced from the reference's `sr-ground-canvas`:\n * six coupled sine/cosine warps, per-channel offsets and a high-power sine\n * field, mixed 80% with a base colour.\n */\nexport const shimmerStyle: StyleBase<ShimmerOptions> = {\n name: 'shimmer',\n defaults: shimmerDefaults,\n animates: (options) => options.speed > 0,\n // The field drifts slowly; 24 fps is plenty and keeps the CPU fallback cheap.\n idleFps: () => 16,\n};\n"],"mappings":";;;;;;AAQA,IAAa,eAA0C;CACrD,MAAM;CACN,U,uBAAUA,CAAAA,CAAAA;CACV,WAAW,YAAY,QAAQ,QAAQ;CAEvC,eAAe;AACjB"}
@@ -0,0 +1,8 @@
1
+ import type { StyleBase } from '../../core/style.cjs';
2
+ import { type ShimmerOptions } from './options.cjs';
3
+ /**
4
+ * Iridescent light bands, reproduced from the reference's `sr-ground-canvas`:
5
+ * six coupled sine/cosine warps, per-channel offsets and a high-power sine
6
+ * field, mixed 80% with a base colour.
7
+ */
8
+ export declare const shimmerStyle: StyleBase<ShimmerOptions>;
@@ -0,0 +1,8 @@
1
+ import type { StyleBase } from '../../core/style.js';
2
+ import { type ShimmerOptions } from './options.js';
3
+ /**
4
+ * Iridescent light bands, reproduced from the reference's `sr-ground-canvas`:
5
+ * six coupled sine/cosine warps, per-channel offsets and a high-power sine
6
+ * field, mixed 80% with a base colour.
7
+ */
8
+ export declare const shimmerStyle: StyleBase<ShimmerOptions>;
@@ -0,0 +1,16 @@
1
+ import { shimmerDefaults } from "./options.js";
2
+ // src/styles/shimmer/definition.ts
3
+ /**
4
+ * Iridescent light bands, reproduced from the reference's `sr-ground-canvas`:
5
+ * six coupled sine/cosine warps, per-channel offsets and a high-power sine
6
+ * field, mixed 80% with a base colour.
7
+ */
8
+ var shimmerStyle = {
9
+ name: "shimmer",
10
+ defaults: shimmerDefaults,
11
+ animates: (options) => options.speed > 0,
12
+ idleFps: () => 16
13
+ };
14
+ export { shimmerStyle };
15
+
16
+ //# sourceMappingURL=definition.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"definition.js","names":[],"sources":["../../../src/styles/shimmer/definition.ts"],"sourcesContent":["import type { StyleBase } from '../../core/style';\nimport { shimmerDefaults, type ShimmerOptions } from './options';\n\n/**\n * Iridescent light bands, reproduced from the reference's `sr-ground-canvas`:\n * six coupled sine/cosine warps, per-channel offsets and a high-power sine\n * field, mixed 80% with a base colour.\n */\nexport const shimmerStyle: StyleBase<ShimmerOptions> = {\n name: 'shimmer',\n defaults: shimmerDefaults,\n animates: (options) => options.speed > 0,\n // The field drifts slowly; 24 fps is plenty and keeps the CPU fallback cheap.\n idleFps: () => 16,\n};\n"],"mappings":";;;;;;;AAQA,IAAa,eAA0C;CACrD,MAAM;CACN,UAAU;CACV,WAAW,YAAY,QAAQ,QAAQ;CAEvC,eAAe;AACjB"}
@@ -0,0 +1,79 @@
1
+ // src/styles/shimmer/field.ts
2
+ /** The reference's clock runs at this many time units per second. */
3
+ var TIME_RATE = .22;
4
+ /** How much of the warped colour replaces the base colour. */
5
+ var COLOR_MIX = .8;
6
+ var TAU = Math.PI * 2;
7
+ var HALF_PI = Math.PI / 2;
8
+ var LUT_SIZE = 2048;
9
+ var SIN_LUT = /* @__PURE__ */ new Float32Array(2049);
10
+ for (let i = 0; i <= LUT_SIZE; i++) SIN_LUT[i] = Math.sin(i / LUT_SIZE * TAU);
11
+ /** Interpolated sine lookup: the CPU field is trig-heavy and runs every frame. */
12
+ function fastSin(x) {
13
+ let t = x * LUT_SIZE / TAU;
14
+ t -= Math.floor(t / LUT_SIZE) * LUT_SIZE;
15
+ const i = t | 0;
16
+ const f = t - i;
17
+ return SIN_LUT[i] + (SIN_LUT[i + 1] - SIN_LUT[i]) * f;
18
+ }
19
+ function fastCos(x) {
20
+ return fastSin(x + HALF_PI);
21
+ }
22
+ /**
23
+ * The CPU mirror of the reference shader. `time` is the style clock in seconds;
24
+ * the field is evaluated at `width` × `height` and written into `image`.
25
+ * Coordinates are top-left based; the shader flips its UVs to match.
26
+ */
27
+ function renderShimmer(image, width, height, time, pointerX, pointerY, influence, options, base) {
28
+ const data = image.data;
29
+ const t = time * TIME_RATE * options.speed;
30
+ const frequency = options.frequency;
31
+ const colorScale = options.colorScale;
32
+ const warp = options.warp;
33
+ const chroma = options.chroma;
34
+ const opacity = options.opacity;
35
+ const pointer = options.pointerStrength * influence;
36
+ const aspect = Math.max(1, width) / Math.max(1, height);
37
+ const distort = (x, y, offset) => {
38
+ let px = x + offset;
39
+ let py = y + offset;
40
+ for (let i = 1; i < 7; i++) {
41
+ px += warp * .3 * fastSin(i * 5 * py + t) / i;
42
+ py += warp * .1 * fastCos(i * 3 * px + t) / i;
43
+ }
44
+ return px;
45
+ };
46
+ for (let y = 0; y < height; y++) {
47
+ const v = (y + .5) / height;
48
+ for (let x = 0; x < width; x++) {
49
+ const u = (x + .5) / width;
50
+ let su = u;
51
+ let sv = v;
52
+ if (pointer > 0) {
53
+ const dx = (u - pointerX) * aspect;
54
+ const dy = v - pointerY;
55
+ const lens = Math.exp(-(dx * dx + dy * dy) / .03) * pointer;
56
+ su += dx / aspect * lens;
57
+ sv += dy * lens;
58
+ }
59
+ const r = fastSin(distort(su, sv, 0) * frequency) * .5 + .5;
60
+ const g = fastSin(distort(su, sv, chroma) * frequency) * .5 + .5;
61
+ const b = fastSin(distort(su, sv, 2 * chroma) * frequency) * .5 + .5;
62
+ const pr = Math.pow(r, colorScale);
63
+ const pg = Math.pow(g, colorScale);
64
+ const pb = Math.pow(b, colorScale);
65
+ const red = base[0] * .19999999999999996 + pr * COLOR_MIX;
66
+ const green = base[1] * .19999999999999996 + pg * COLOR_MIX;
67
+ const blue = base[2] * .19999999999999996 + pb * COLOR_MIX;
68
+ const o = (y * width + x) * 4;
69
+ data[o] = Math.round(Math.min(1, Math.max(0, red)) * 255);
70
+ data[o + 1] = Math.round(Math.min(1, Math.max(0, green)) * 255);
71
+ data[o + 2] = Math.round(Math.min(1, Math.max(0, blue)) * 255);
72
+ data[o + 3] = Math.round(opacity * 255);
73
+ }
74
+ }
75
+ }
76
+ exports.TIME_RATE = TIME_RATE;
77
+ exports.renderShimmer = renderShimmer;
78
+
79
+ //# sourceMappingURL=field.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"field.cjs","names":[],"sources":["../../../src/styles/shimmer/field.ts"],"sourcesContent":["import type { ResolvedShimmerOptions } from './options';\n\n/** Long side of the CPU fallback's field. The reference field is very soft. */\nexport const CPU_RESOLUTION = 128;\n/** The reference's clock runs at this many time units per second. */\nexport const TIME_RATE = 0.22;\n/** How much of the warped colour replaces the base colour. */\nconst COLOR_MIX = 0.8;\nconst TAU = Math.PI * 2;\nconst HALF_PI = Math.PI / 2;\nconst LUT_SIZE = 2048;\nconst SIN_LUT = new Float32Array(LUT_SIZE + 1);\nfor (let i = 0; i <= LUT_SIZE; i++) SIN_LUT[i] = Math.sin((i / LUT_SIZE) * TAU);\n\n/** Interpolated sine lookup: the CPU field is trig-heavy and runs every frame. */\nfunction fastSin(x: number): number {\n let t = (x * LUT_SIZE) / TAU;\n t -= Math.floor(t / LUT_SIZE) * LUT_SIZE;\n const i = t | 0;\n const f = t - i;\n return SIN_LUT[i] + (SIN_LUT[i + 1] - SIN_LUT[i]) * f;\n}\n\nfunction fastCos(x: number): number {\n return fastSin(x + HALF_PI);\n}\n\n/**\n * The CPU mirror of the reference shader. `time` is the style clock in seconds;\n * the field is evaluated at `width` × `height` and written into `image`.\n * Coordinates are top-left based; the shader flips its UVs to match.\n */\nexport function renderShimmer(\n image: ImageData,\n width: number,\n height: number,\n time: number,\n pointerX: number,\n pointerY: number,\n influence: number,\n options: ResolvedShimmerOptions,\n base: readonly [number, number, number],\n): void {\n const data = image.data;\n const t = time * TIME_RATE * options.speed;\n const frequency = options.frequency;\n const colorScale = options.colorScale;\n const warp = options.warp;\n const chroma = options.chroma;\n const opacity = options.opacity;\n const pointer = options.pointerStrength * influence;\n const aspect = Math.max(1, width) / Math.max(1, height);\n\n const distort = (x: number, y: number, offset: number): number => {\n let px = x + offset;\n let py = y + offset;\n for (let i = 1; i < 7; i++) {\n px += (warp * 0.3 * fastSin(i * 5 * py + t)) / i;\n py += (warp * 0.1 * fastCos(i * 3 * px + t)) / i;\n }\n return px;\n };\n\n for (let y = 0; y < height; y++) {\n const v = (y + 0.5) / height;\n for (let x = 0; x < width; x++) {\n const u = (x + 0.5) / width;\n\n // Optional pointer lens, in screen-height units like the shader.\n let su = u;\n let sv = v;\n if (pointer > 0) {\n const dx = (u - pointerX) * aspect;\n const dy = v - pointerY;\n const lens = Math.exp(-(dx * dx + dy * dy) / 0.03) * pointer;\n su += (dx / aspect) * lens;\n sv += dy * lens;\n }\n\n const r = fastSin(distort(su, sv, 0) * frequency) * 0.5 + 0.5;\n const g = fastSin(distort(su, sv, chroma) * frequency) * 0.5 + 0.5;\n const b = fastSin(distort(su, sv, 2 * chroma) * frequency) * 0.5 + 0.5;\n const pr = Math.pow(r, colorScale);\n const pg = Math.pow(g, colorScale);\n const pb = Math.pow(b, colorScale);\n\n const red = base[0] * (1 - COLOR_MIX) + pr * COLOR_MIX;\n const green = base[1] * (1 - COLOR_MIX) + pg * COLOR_MIX;\n const blue = base[2] * (1 - COLOR_MIX) + pb * COLOR_MIX;\n\n const o = (y * width + x) * 4;\n data[o] = Math.round(Math.min(1, Math.max(0, red)) * 255);\n data[o + 1] = Math.round(Math.min(1, Math.max(0, green)) * 255);\n data[o + 2] = Math.round(Math.min(1, Math.max(0, blue)) * 255);\n data[o + 3] = Math.round(opacity * 255);\n }\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import type { ResolvedShimmerOptions } from './options.cjs';
2
+ /** Long side of the CPU fallback's field. The reference field is very soft. */
3
+ export declare const CPU_RESOLUTION = 128;
4
+ /** The reference's clock runs at this many time units per second. */
5
+ export declare const TIME_RATE = 0.22;
6
+ /**
7
+ * The CPU mirror of the reference shader. `time` is the style clock in seconds;
8
+ * the field is evaluated at `width` × `height` and written into `image`.
9
+ * Coordinates are top-left based; the shader flips its UVs to match.
10
+ */
11
+ export declare function renderShimmer(image: ImageData, width: number, height: number, time: number, pointerX: number, pointerY: number, influence: number, options: ResolvedShimmerOptions, base: readonly [number, number, number]): void;
@@ -0,0 +1,11 @@
1
+ import type { ResolvedShimmerOptions } from './options.js';
2
+ /** Long side of the CPU fallback's field. The reference field is very soft. */
3
+ export declare const CPU_RESOLUTION = 128;
4
+ /** The reference's clock runs at this many time units per second. */
5
+ export declare const TIME_RATE = 0.22;
6
+ /**
7
+ * The CPU mirror of the reference shader. `time` is the style clock in seconds;
8
+ * the field is evaluated at `width` × `height` and written into `image`.
9
+ * Coordinates are top-left based; the shader flips its UVs to match.
10
+ */
11
+ export declare function renderShimmer(image: ImageData, width: number, height: number, time: number, pointerX: number, pointerY: number, influence: number, options: ResolvedShimmerOptions, base: readonly [number, number, number]): void;
@@ -0,0 +1,78 @@
1
+ // src/styles/shimmer/field.ts
2
+ /** The reference's clock runs at this many time units per second. */
3
+ var TIME_RATE = .22;
4
+ /** How much of the warped colour replaces the base colour. */
5
+ var COLOR_MIX = .8;
6
+ var TAU = Math.PI * 2;
7
+ var HALF_PI = Math.PI / 2;
8
+ var LUT_SIZE = 2048;
9
+ var SIN_LUT = /* @__PURE__ */ new Float32Array(2049);
10
+ for (let i = 0; i <= LUT_SIZE; i++) SIN_LUT[i] = Math.sin(i / LUT_SIZE * TAU);
11
+ /** Interpolated sine lookup: the CPU field is trig-heavy and runs every frame. */
12
+ function fastSin(x) {
13
+ let t = x * LUT_SIZE / TAU;
14
+ t -= Math.floor(t / LUT_SIZE) * LUT_SIZE;
15
+ const i = t | 0;
16
+ const f = t - i;
17
+ return SIN_LUT[i] + (SIN_LUT[i + 1] - SIN_LUT[i]) * f;
18
+ }
19
+ function fastCos(x) {
20
+ return fastSin(x + HALF_PI);
21
+ }
22
+ /**
23
+ * The CPU mirror of the reference shader. `time` is the style clock in seconds;
24
+ * the field is evaluated at `width` × `height` and written into `image`.
25
+ * Coordinates are top-left based; the shader flips its UVs to match.
26
+ */
27
+ function renderShimmer(image, width, height, time, pointerX, pointerY, influence, options, base) {
28
+ const data = image.data;
29
+ const t = time * TIME_RATE * options.speed;
30
+ const frequency = options.frequency;
31
+ const colorScale = options.colorScale;
32
+ const warp = options.warp;
33
+ const chroma = options.chroma;
34
+ const opacity = options.opacity;
35
+ const pointer = options.pointerStrength * influence;
36
+ const aspect = Math.max(1, width) / Math.max(1, height);
37
+ const distort = (x, y, offset) => {
38
+ let px = x + offset;
39
+ let py = y + offset;
40
+ for (let i = 1; i < 7; i++) {
41
+ px += warp * .3 * fastSin(i * 5 * py + t) / i;
42
+ py += warp * .1 * fastCos(i * 3 * px + t) / i;
43
+ }
44
+ return px;
45
+ };
46
+ for (let y = 0; y < height; y++) {
47
+ const v = (y + .5) / height;
48
+ for (let x = 0; x < width; x++) {
49
+ const u = (x + .5) / width;
50
+ let su = u;
51
+ let sv = v;
52
+ if (pointer > 0) {
53
+ const dx = (u - pointerX) * aspect;
54
+ const dy = v - pointerY;
55
+ const lens = Math.exp(-(dx * dx + dy * dy) / .03) * pointer;
56
+ su += dx / aspect * lens;
57
+ sv += dy * lens;
58
+ }
59
+ const r = fastSin(distort(su, sv, 0) * frequency) * .5 + .5;
60
+ const g = fastSin(distort(su, sv, chroma) * frequency) * .5 + .5;
61
+ const b = fastSin(distort(su, sv, 2 * chroma) * frequency) * .5 + .5;
62
+ const pr = Math.pow(r, colorScale);
63
+ const pg = Math.pow(g, colorScale);
64
+ const pb = Math.pow(b, colorScale);
65
+ const red = base[0] * .19999999999999996 + pr * COLOR_MIX;
66
+ const green = base[1] * .19999999999999996 + pg * COLOR_MIX;
67
+ const blue = base[2] * .19999999999999996 + pb * COLOR_MIX;
68
+ const o = (y * width + x) * 4;
69
+ data[o] = Math.round(Math.min(1, Math.max(0, red)) * 255);
70
+ data[o + 1] = Math.round(Math.min(1, Math.max(0, green)) * 255);
71
+ data[o + 2] = Math.round(Math.min(1, Math.max(0, blue)) * 255);
72
+ data[o + 3] = Math.round(opacity * 255);
73
+ }
74
+ }
75
+ }
76
+ export { TIME_RATE, renderShimmer };
77
+
78
+ //# sourceMappingURL=field.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"field.js","names":[],"sources":["../../../src/styles/shimmer/field.ts"],"sourcesContent":["import type { ResolvedShimmerOptions } from './options';\n\n/** Long side of the CPU fallback's field. The reference field is very soft. */\nexport const CPU_RESOLUTION = 128;\n/** The reference's clock runs at this many time units per second. */\nexport const TIME_RATE = 0.22;\n/** How much of the warped colour replaces the base colour. */\nconst COLOR_MIX = 0.8;\nconst TAU = Math.PI * 2;\nconst HALF_PI = Math.PI / 2;\nconst LUT_SIZE = 2048;\nconst SIN_LUT = new Float32Array(LUT_SIZE + 1);\nfor (let i = 0; i <= LUT_SIZE; i++) SIN_LUT[i] = Math.sin((i / LUT_SIZE) * TAU);\n\n/** Interpolated sine lookup: the CPU field is trig-heavy and runs every frame. */\nfunction fastSin(x: number): number {\n let t = (x * LUT_SIZE) / TAU;\n t -= Math.floor(t / LUT_SIZE) * LUT_SIZE;\n const i = t | 0;\n const f = t - i;\n return SIN_LUT[i] + (SIN_LUT[i + 1] - SIN_LUT[i]) * f;\n}\n\nfunction fastCos(x: number): number {\n return fastSin(x + HALF_PI);\n}\n\n/**\n * The CPU mirror of the reference shader. `time` is the style clock in seconds;\n * the field is evaluated at `width` × `height` and written into `image`.\n * Coordinates are top-left based; the shader flips its UVs to match.\n */\nexport function renderShimmer(\n image: ImageData,\n width: number,\n height: number,\n time: number,\n pointerX: number,\n pointerY: number,\n influence: number,\n options: ResolvedShimmerOptions,\n base: readonly [number, number, number],\n): void {\n const data = image.data;\n const t = time * TIME_RATE * options.speed;\n const frequency = options.frequency;\n const colorScale = options.colorScale;\n const warp = options.warp;\n const chroma = options.chroma;\n const opacity = options.opacity;\n const pointer = options.pointerStrength * influence;\n const aspect = Math.max(1, width) / Math.max(1, height);\n\n const distort = (x: number, y: number, offset: number): number => {\n let px = x + offset;\n let py = y + offset;\n for (let i = 1; i < 7; i++) {\n px += (warp * 0.3 * fastSin(i * 5 * py + t)) / i;\n py += (warp * 0.1 * fastCos(i * 3 * px + t)) / i;\n }\n return px;\n };\n\n for (let y = 0; y < height; y++) {\n const v = (y + 0.5) / height;\n for (let x = 0; x < width; x++) {\n const u = (x + 0.5) / width;\n\n // Optional pointer lens, in screen-height units like the shader.\n let su = u;\n let sv = v;\n if (pointer > 0) {\n const dx = (u - pointerX) * aspect;\n const dy = v - pointerY;\n const lens = Math.exp(-(dx * dx + dy * dy) / 0.03) * pointer;\n su += (dx / aspect) * lens;\n sv += dy * lens;\n }\n\n const r = fastSin(distort(su, sv, 0) * frequency) * 0.5 + 0.5;\n const g = fastSin(distort(su, sv, chroma) * frequency) * 0.5 + 0.5;\n const b = fastSin(distort(su, sv, 2 * chroma) * frequency) * 0.5 + 0.5;\n const pr = Math.pow(r, colorScale);\n const pg = Math.pow(g, colorScale);\n const pb = Math.pow(b, colorScale);\n\n const red = base[0] * (1 - COLOR_MIX) + pr * COLOR_MIX;\n const green = base[1] * (1 - COLOR_MIX) + pg * COLOR_MIX;\n const blue = base[2] * (1 - COLOR_MIX) + pb * COLOR_MIX;\n\n const o = (y * width + x) * 4;\n data[o] = Math.round(Math.min(1, Math.max(0, red)) * 255);\n data[o + 1] = Math.round(Math.min(1, Math.max(0, green)) * 255);\n data[o + 2] = Math.round(Math.min(1, Math.max(0, blue)) * 255);\n data[o + 3] = Math.round(opacity * 255);\n }\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ // src/styles/shimmer/options.ts
2
+ var shimmerDefaults = Object.freeze({
3
+ opacity: 1,
4
+ speed: 1,
5
+ frequency: 7,
6
+ colorScale: 10,
7
+ baseColor: "#5735ff",
8
+ chroma: .02,
9
+ warp: 1,
10
+ pointerStrength: .35
11
+ });
12
+ var clamp = (value, min, max) => Math.min(max, Math.max(min, value));
13
+ function resolveShimmerOptions(options) {
14
+ return {
15
+ ...options,
16
+ opacity: clamp(options.opacity, 0, 1),
17
+ speed: Math.max(0, options.speed),
18
+ frequency: clamp(options.frequency, .5, 40),
19
+ colorScale: clamp(options.colorScale, .5, 40),
20
+ chroma: clamp(options.chroma, 0, .2),
21
+ warp: clamp(options.warp, 0, 4),
22
+ pointerStrength: clamp(options.pointerStrength, 0, 1.5)
23
+ };
24
+ }
25
+ exports.resolveShimmerOptions = resolveShimmerOptions;
26
+ exports.shimmerDefaults = shimmerDefaults;
27
+
28
+ //# sourceMappingURL=options.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"options.cjs","names":[],"sources":["../../../src/styles/shimmer/options.ts"],"sourcesContent":["export interface ShimmerOptions {\n /** Opacity of the finished field, from `0` to `1`. `1` is the opaque reference canvas. */\n opacity: number;\n /** How fast the field flows. `1` matches the reference's slow drift. `0` = still. */\n speed: number;\n /** Sine frequency of the bands. Higher = more, narrower bands. The reference uses `7`. */\n frequency: number;\n /** Gamma applied to the bands. Higher = darker gaps and brighter crests. The reference uses `10`. */\n colorScale: number;\n /** Colour mixed into the 20% base of the field. The reference uses white. */\n baseColor: string;\n /** Separation between the red, green and blue channel warps: chromatic dispersion. The reference uses `0.02`. */\n chroma: number;\n /** Multiplier for the sine/cosine distortion. `1` is the reference; `0` = straight bands. */\n warp: number;\n /** Optional pointer lens that bends the bands locally. `0` keeps the reference's ambient field. */\n pointerStrength: number;\n}\n\nexport const shimmerDefaults: Readonly<ShimmerOptions> = Object.freeze({\n opacity: 1,\n speed: 1,\n frequency: 7,\n colorScale: 10,\n baseColor: '#5735ff',\n chroma: 0.02,\n warp: 1,\n pointerStrength: 0.35,\n});\n\n/** Options after clamping: what the shader and the CPU field consume. */\nexport type ResolvedShimmerOptions = ShimmerOptions;\n\nconst clamp = (value: number, min: number, max: number) => Math.min(max, Math.max(min, value));\n\nexport function resolveShimmerOptions(options: ShimmerOptions): ResolvedShimmerOptions {\n return {\n ...options,\n opacity: clamp(options.opacity, 0, 1),\n speed: Math.max(0, options.speed),\n frequency: clamp(options.frequency, 0.5, 40),\n colorScale: clamp(options.colorScale, 0.5, 40),\n chroma: clamp(options.chroma, 0, 0.2),\n warp: clamp(options.warp, 0, 4),\n pointerStrength: clamp(options.pointerStrength, 0, 1.5),\n };\n}\n"],"mappings":";AAmBA,IAAa,kBAA4C,OAAO,OAAO;CACrE,SAAS;CACT,OAAO;CACP,WAAW;CACX,YAAY;CACZ,WAAW;CACX,QAAQ;CACR,MAAM;CACN,iBAAiB;AACnB,CAAC;AAKD,IAAM,SAAS,OAAe,KAAa,QAAgB,KAAK,IAAI,KAAK,KAAK,IAAI,KAAK,KAAK,CAAC;AAE7F,SAAgB,sBAAsB,SAAiD;CACrF,OAAO;EACL,GAAG;EACH,SAAS,MAAM,QAAQ,SAAS,GAAG,CAAC;EACpC,OAAO,KAAK,IAAI,GAAG,QAAQ,KAAK;EAChC,WAAW,MAAM,QAAQ,WAAW,IAAK,EAAE;EAC3C,YAAY,MAAM,QAAQ,YAAY,IAAK,EAAE;EAC7C,QAAQ,MAAM,QAAQ,QAAQ,GAAG,EAAG;EACpC,MAAM,MAAM,QAAQ,MAAM,GAAG,CAAC;EAC9B,iBAAiB,MAAM,QAAQ,iBAAiB,GAAG,GAAG;CACxD;AACF"}
@@ -0,0 +1,22 @@
1
+ export interface ShimmerOptions {
2
+ /** Opacity of the finished field, from `0` to `1`. `1` is the opaque reference canvas. */
3
+ opacity: number;
4
+ /** How fast the field flows. `1` matches the reference's slow drift. `0` = still. */
5
+ speed: number;
6
+ /** Sine frequency of the bands. Higher = more, narrower bands. The reference uses `7`. */
7
+ frequency: number;
8
+ /** Gamma applied to the bands. Higher = darker gaps and brighter crests. The reference uses `10`. */
9
+ colorScale: number;
10
+ /** Colour mixed into the 20% base of the field. The reference uses white. */
11
+ baseColor: string;
12
+ /** Separation between the red, green and blue channel warps: chromatic dispersion. The reference uses `0.02`. */
13
+ chroma: number;
14
+ /** Multiplier for the sine/cosine distortion. `1` is the reference; `0` = straight bands. */
15
+ warp: number;
16
+ /** Optional pointer lens that bends the bands locally. `0` keeps the reference's ambient field. */
17
+ pointerStrength: number;
18
+ }
19
+ export declare const shimmerDefaults: Readonly<ShimmerOptions>;
20
+ /** Options after clamping: what the shader and the CPU field consume. */
21
+ export type ResolvedShimmerOptions = ShimmerOptions;
22
+ export declare function resolveShimmerOptions(options: ShimmerOptions): ResolvedShimmerOptions;
@@ -0,0 +1,22 @@
1
+ export interface ShimmerOptions {
2
+ /** Opacity of the finished field, from `0` to `1`. `1` is the opaque reference canvas. */
3
+ opacity: number;
4
+ /** How fast the field flows. `1` matches the reference's slow drift. `0` = still. */
5
+ speed: number;
6
+ /** Sine frequency of the bands. Higher = more, narrower bands. The reference uses `7`. */
7
+ frequency: number;
8
+ /** Gamma applied to the bands. Higher = darker gaps and brighter crests. The reference uses `10`. */
9
+ colorScale: number;
10
+ /** Colour mixed into the 20% base of the field. The reference uses white. */
11
+ baseColor: string;
12
+ /** Separation between the red, green and blue channel warps: chromatic dispersion. The reference uses `0.02`. */
13
+ chroma: number;
14
+ /** Multiplier for the sine/cosine distortion. `1` is the reference; `0` = straight bands. */
15
+ warp: number;
16
+ /** Optional pointer lens that bends the bands locally. `0` keeps the reference's ambient field. */
17
+ pointerStrength: number;
18
+ }
19
+ export declare const shimmerDefaults: Readonly<ShimmerOptions>;
20
+ /** Options after clamping: what the shader and the CPU field consume. */
21
+ export type ResolvedShimmerOptions = ShimmerOptions;
22
+ export declare function resolveShimmerOptions(options: ShimmerOptions): ResolvedShimmerOptions;
@@ -0,0 +1,27 @@
1
+ // src/styles/shimmer/options.ts
2
+ var shimmerDefaults = Object.freeze({
3
+ opacity: 1,
4
+ speed: 1,
5
+ frequency: 7,
6
+ colorScale: 10,
7
+ baseColor: "#5735ff",
8
+ chroma: .02,
9
+ warp: 1,
10
+ pointerStrength: .35
11
+ });
12
+ var clamp = (value, min, max) => Math.min(max, Math.max(min, value));
13
+ function resolveShimmerOptions(options) {
14
+ return {
15
+ ...options,
16
+ opacity: clamp(options.opacity, 0, 1),
17
+ speed: Math.max(0, options.speed),
18
+ frequency: clamp(options.frequency, .5, 40),
19
+ colorScale: clamp(options.colorScale, .5, 40),
20
+ chroma: clamp(options.chroma, 0, .2),
21
+ warp: clamp(options.warp, 0, 4),
22
+ pointerStrength: clamp(options.pointerStrength, 0, 1.5)
23
+ };
24
+ }
25
+ export { resolveShimmerOptions, shimmerDefaults };
26
+
27
+ //# sourceMappingURL=options.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"options.js","names":[],"sources":["../../../src/styles/shimmer/options.ts"],"sourcesContent":["export interface ShimmerOptions {\n /** Opacity of the finished field, from `0` to `1`. `1` is the opaque reference canvas. */\n opacity: number;\n /** How fast the field flows. `1` matches the reference's slow drift. `0` = still. */\n speed: number;\n /** Sine frequency of the bands. Higher = more, narrower bands. The reference uses `7`. */\n frequency: number;\n /** Gamma applied to the bands. Higher = darker gaps and brighter crests. The reference uses `10`. */\n colorScale: number;\n /** Colour mixed into the 20% base of the field. The reference uses white. */\n baseColor: string;\n /** Separation between the red, green and blue channel warps: chromatic dispersion. The reference uses `0.02`. */\n chroma: number;\n /** Multiplier for the sine/cosine distortion. `1` is the reference; `0` = straight bands. */\n warp: number;\n /** Optional pointer lens that bends the bands locally. `0` keeps the reference's ambient field. */\n pointerStrength: number;\n}\n\nexport const shimmerDefaults: Readonly<ShimmerOptions> = Object.freeze({\n opacity: 1,\n speed: 1,\n frequency: 7,\n colorScale: 10,\n baseColor: '#5735ff',\n chroma: 0.02,\n warp: 1,\n pointerStrength: 0.35,\n});\n\n/** Options after clamping: what the shader and the CPU field consume. */\nexport type ResolvedShimmerOptions = ShimmerOptions;\n\nconst clamp = (value: number, min: number, max: number) => Math.min(max, Math.max(min, value));\n\nexport function resolveShimmerOptions(options: ShimmerOptions): ResolvedShimmerOptions {\n return {\n ...options,\n opacity: clamp(options.opacity, 0, 1),\n speed: Math.max(0, options.speed),\n frequency: clamp(options.frequency, 0.5, 40),\n colorScale: clamp(options.colorScale, 0.5, 40),\n chroma: clamp(options.chroma, 0, 0.2),\n warp: clamp(options.warp, 0, 4),\n pointerStrength: clamp(options.pointerStrength, 0, 1.5),\n };\n}\n"],"mappings":";AAmBA,IAAa,kBAA4C,OAAO,OAAO;CACrE,SAAS;CACT,OAAO;CACP,WAAW;CACX,YAAY;CACZ,WAAW;CACX,QAAQ;CACR,MAAM;CACN,iBAAiB;AACnB,CAAC;AAKD,IAAM,SAAS,OAAe,KAAa,QAAgB,KAAK,IAAI,KAAK,KAAK,IAAI,KAAK,KAAK,CAAC;AAE7F,SAAgB,sBAAsB,SAAiD;CACrF,OAAO;EACL,GAAG;EACH,SAAS,MAAM,QAAQ,SAAS,GAAG,CAAC;EACpC,OAAO,KAAK,IAAI,GAAG,QAAQ,KAAK;EAChC,WAAW,MAAM,QAAQ,WAAW,IAAK,EAAE;EAC3C,YAAY,MAAM,QAAQ,YAAY,IAAK,EAAE;EAC7C,QAAQ,MAAM,QAAQ,QAAQ,GAAG,EAAG;EACpC,MAAM,MAAM,QAAQ,MAAM,GAAG,CAAC;EAC9B,iBAAiB,MAAM,QAAQ,iBAAiB,GAAG,GAAG;CACxD;AACF"}