@elyri/motion 0.0.0-stage → 0.0.1

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 (114) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +70 -2
  3. package/dist/chunk-A7JCTGZO.js +230 -0
  4. package/dist/chunk-A7JCTGZO.js.map +1 -0
  5. package/dist/core.cjs +238 -0
  6. package/dist/core.cjs.map +1 -0
  7. package/dist/core.d.cts +77 -0
  8. package/dist/core.d.ts +77 -0
  9. package/dist/core.js +3 -0
  10. package/dist/core.js.map +1 -0
  11. package/dist/index.cjs +4288 -0
  12. package/dist/index.cjs.map +1 -0
  13. package/dist/index.css +1118 -0
  14. package/dist/index.css.map +1 -0
  15. package/dist/index.d.cts +532 -0
  16. package/dist/index.d.ts +532 -0
  17. package/dist/index.js +4029 -0
  18. package/dist/index.js.map +1 -0
  19. package/package.json +60 -3
  20. package/src/components/animations/CountUp/CountUp.css +4 -0
  21. package/src/components/animations/CountUp/CountUp.tsx +100 -0
  22. package/src/components/animations/CountUp/index.ts +2 -0
  23. package/src/components/animations/FadeIn/FadeIn.css +43 -0
  24. package/src/components/animations/FadeIn/FadeIn.tsx +72 -0
  25. package/src/components/animations/FadeIn/index.ts +2 -0
  26. package/src/components/animations/Magnetic/Magnetic.css +11 -0
  27. package/src/components/animations/Magnetic/Magnetic.tsx +107 -0
  28. package/src/components/animations/Magnetic/index.ts +2 -0
  29. package/src/components/animations/ScaleIn/ScaleIn.css +24 -0
  30. package/src/components/animations/ScaleIn/ScaleIn.tsx +65 -0
  31. package/src/components/animations/ScaleIn/index.ts +2 -0
  32. package/src/components/animations/Spotlight/Spotlight.css +93 -0
  33. package/src/components/animations/Spotlight/Spotlight.tsx +100 -0
  34. package/src/components/animations/Spotlight/index.ts +2 -0
  35. package/src/components/animations/Tilt/Tilt.css +35 -0
  36. package/src/components/animations/Tilt/Tilt.tsx +123 -0
  37. package/src/components/animations/Tilt/index.ts +2 -0
  38. package/src/components/animations/index.ts +12 -0
  39. package/src/components/backgrounds/Aurora/Aurora.css +33 -0
  40. package/src/components/backgrounds/Aurora/Aurora.tsx +147 -0
  41. package/src/components/backgrounds/Aurora/index.ts +2 -0
  42. package/src/components/backgrounds/Caustics/Caustics.css +34 -0
  43. package/src/components/backgrounds/Caustics/Caustics.tsx +142 -0
  44. package/src/components/backgrounds/Caustics/index.ts +2 -0
  45. package/src/components/backgrounds/Contours/Contours.css +42 -0
  46. package/src/components/backgrounds/Contours/Contours.tsx +147 -0
  47. package/src/components/backgrounds/Contours/index.ts +2 -0
  48. package/src/components/backgrounds/DotField/DotField.css +44 -0
  49. package/src/components/backgrounds/DotField/DotField.tsx +135 -0
  50. package/src/components/backgrounds/DotField/index.ts +2 -0
  51. package/src/components/backgrounds/DragonScales/DragonScales.css +44 -0
  52. package/src/components/backgrounds/DragonScales/DragonScales.tsx +250 -0
  53. package/src/components/backgrounds/DragonScales/index.ts +2 -0
  54. package/src/components/backgrounds/Filaments/Filaments.css +38 -0
  55. package/src/components/backgrounds/Filaments/Filaments.tsx +138 -0
  56. package/src/components/backgrounds/Filaments/index.ts +2 -0
  57. package/src/components/backgrounds/FlipTiles/FlipTiles.css +44 -0
  58. package/src/components/backgrounds/FlipTiles/FlipTiles.tsx +368 -0
  59. package/src/components/backgrounds/FlipTiles/index.ts +2 -0
  60. package/src/components/backgrounds/Halo/Halo.css +43 -0
  61. package/src/components/backgrounds/Halo/Halo.tsx +122 -0
  62. package/src/components/backgrounds/Halo/index.ts +2 -0
  63. package/src/components/backgrounds/MeshGradient/MeshGradient.css +43 -0
  64. package/src/components/backgrounds/MeshGradient/MeshGradient.tsx +195 -0
  65. package/src/components/backgrounds/MeshGradient/index.ts +2 -0
  66. package/src/components/backgrounds/PixelVortex/PixelVortex.css +44 -0
  67. package/src/components/backgrounds/PixelVortex/PixelVortex.tsx +278 -0
  68. package/src/components/backgrounds/PixelVortex/index.ts +2 -0
  69. package/src/components/backgrounds/Planet/Planet.css +39 -0
  70. package/src/components/backgrounds/Planet/Planet.tsx +736 -0
  71. package/src/components/backgrounds/Planet/index.ts +2 -0
  72. package/src/components/backgrounds/SilkWaves/SilkWaves.css +44 -0
  73. package/src/components/backgrounds/SilkWaves/SilkWaves.tsx +144 -0
  74. package/src/components/backgrounds/SilkWaves/index.ts +2 -0
  75. package/src/components/backgrounds/Velvet/Velvet.css +35 -0
  76. package/src/components/backgrounds/Velvet/Velvet.tsx +246 -0
  77. package/src/components/backgrounds/Velvet/index.ts +2 -0
  78. package/src/components/backgrounds/index.ts +26 -0
  79. package/src/components/index.ts +4 -0
  80. package/src/components/media/AsciiImage/AsciiImage.css +16 -0
  81. package/src/components/media/AsciiImage/AsciiImage.tsx +366 -0
  82. package/src/components/media/AsciiImage/index.ts +2 -0
  83. package/src/components/media/index.ts +2 -0
  84. package/src/components/text/GradientText/GradientText.css +32 -0
  85. package/src/components/text/GradientText/GradientText.tsx +27 -0
  86. package/src/components/text/GradientText/index.ts +2 -0
  87. package/src/components/text/Marquee/Marquee.css +91 -0
  88. package/src/components/text/Marquee/Marquee.tsx +130 -0
  89. package/src/components/text/Marquee/index.ts +2 -0
  90. package/src/components/text/ParticleText/ParticleText.css +16 -0
  91. package/src/components/text/ParticleText/ParticleText.tsx +279 -0
  92. package/src/components/text/ParticleText/index.ts +2 -0
  93. package/src/components/text/RotatingText/RotatingText.css +133 -0
  94. package/src/components/text/RotatingText/RotatingText.tsx +112 -0
  95. package/src/components/text/RotatingText/index.ts +2 -0
  96. package/src/components/text/ScrambleText/ScrambleText.css +48 -0
  97. package/src/components/text/ScrambleText/ScrambleText.tsx +141 -0
  98. package/src/components/text/ScrambleText/index.ts +2 -0
  99. package/src/components/text/ScrollMarquee/ScrollMarquee.css +33 -0
  100. package/src/components/text/ScrollMarquee/ScrollMarquee.tsx +110 -0
  101. package/src/components/text/ScrollMarquee/index.ts +2 -0
  102. package/src/components/text/ScrollReveal/ScrollReveal.css +51 -0
  103. package/src/components/text/ScrollReveal/ScrollReveal.tsx +186 -0
  104. package/src/components/text/ScrollReveal/index.ts +2 -0
  105. package/src/components/text/SplitReveal/SplitReveal.css +52 -0
  106. package/src/components/text/SplitReveal/SplitReveal.tsx +127 -0
  107. package/src/components/text/SplitReveal/index.ts +2 -0
  108. package/src/components/text/Typewriter/Typewriter.css +37 -0
  109. package/src/components/text/Typewriter/Typewriter.tsx +80 -0
  110. package/src/components/text/Typewriter/index.ts +2 -0
  111. package/src/components/text/WaveText/WaveText.css +28 -0
  112. package/src/components/text/WaveText/WaveText.tsx +52 -0
  113. package/src/components/text/WaveText/index.ts +2 -0
  114. package/src/components/text/index.ts +20 -0
package/dist/index.js ADDED
@@ -0,0 +1,4029 @@
1
+ import { usePrefersReducedMotion, cn, useShaderCanvas, hexToLinearRgb, useColorScheme, splitTextUnits } from './chunk-A7JCTGZO.js';
2
+ export { ReducedMotionProvider, cn, hexToLinearRgb, splitTextUnits, useColorScheme, usePrefersReducedMotion, useShaderCanvas } from './chunk-A7JCTGZO.js';
3
+ import { useRef, useState, useEffect, useLayoutEffect, useMemo } from 'react';
4
+ import { jsxs, jsx } from 'react/jsx-runtime';
5
+
6
+ var group = (value) => value.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
7
+ var easeOutCubic = (progress) => 1 - (1 - progress) ** 3;
8
+ function CountUp({
9
+ className,
10
+ to,
11
+ from = 0,
12
+ duration = 1200,
13
+ decimals = 0,
14
+ separator = false,
15
+ prefix,
16
+ suffix,
17
+ once = true
18
+ }) {
19
+ const elementRef = useRef(null);
20
+ const prefersReducedMotion = usePrefersReducedMotion();
21
+ const [value, setValue] = useState(from);
22
+ const [isIntersecting, setIsIntersecting] = useState(false);
23
+ useEffect(() => {
24
+ const element = elementRef.current;
25
+ if (!element || prefersReducedMotion) return;
26
+ const observer = new IntersectionObserver(
27
+ ([entry]) => {
28
+ if (entry.isIntersecting) {
29
+ setIsIntersecting(true);
30
+ if (once) observer.disconnect();
31
+ return;
32
+ }
33
+ if (!once) setIsIntersecting(false);
34
+ },
35
+ { threshold: 0.1 }
36
+ );
37
+ observer.observe(element);
38
+ return () => observer.disconnect();
39
+ }, [once, prefersReducedMotion]);
40
+ useEffect(() => {
41
+ if (!isIntersecting || prefersReducedMotion) return;
42
+ let frame = 0;
43
+ const startedAt = performance.now();
44
+ const tick = (now) => {
45
+ const progress = duration > 0 ? Math.min((now - startedAt) / duration, 1) : 1;
46
+ setValue(from + (to - from) * easeOutCubic(progress));
47
+ if (progress < 1) frame = requestAnimationFrame(tick);
48
+ };
49
+ frame = requestAnimationFrame(tick);
50
+ return () => cancelAnimationFrame(frame);
51
+ }, [isIntersecting, from, to, duration, prefersReducedMotion]);
52
+ const fixed = (prefersReducedMotion ? to : value).toFixed(decimals);
53
+ const [integer, fraction] = fixed.split(".");
54
+ const grouped = separator ? group(integer) : integer;
55
+ const display = fraction ? `${grouped}.${fraction}` : grouped;
56
+ return /* @__PURE__ */ jsxs("span", { ref: elementRef, className: cn("elyri-count-up", className), children: [
57
+ prefix,
58
+ display,
59
+ suffix
60
+ ] });
61
+ }
62
+ function FadeIn({
63
+ children,
64
+ className,
65
+ direction = "up",
66
+ delay = 0,
67
+ duration = 600,
68
+ distance = 24,
69
+ once = true
70
+ }) {
71
+ const elementRef = useRef(null);
72
+ const prefersReducedMotion = usePrefersReducedMotion();
73
+ const [isIntersecting, setIsIntersecting] = useState(false);
74
+ useEffect(() => {
75
+ const element = elementRef.current;
76
+ if (!element || prefersReducedMotion) return;
77
+ const observer = new IntersectionObserver(
78
+ ([entry]) => {
79
+ if (entry.isIntersecting) {
80
+ setIsIntersecting(true);
81
+ if (once) observer.disconnect();
82
+ return;
83
+ }
84
+ if (!once) setIsIntersecting(false);
85
+ },
86
+ { threshold: 0.1 }
87
+ );
88
+ observer.observe(element);
89
+ return () => observer.disconnect();
90
+ }, [once, prefersReducedMotion]);
91
+ const style = {
92
+ "--elyri-fade-in-delay": `${delay}ms`,
93
+ "--elyri-fade-in-duration": `${duration}ms`,
94
+ "--elyri-fade-in-distance": `${distance}px`
95
+ };
96
+ const isVisible = prefersReducedMotion || isIntersecting;
97
+ return /* @__PURE__ */ jsx(
98
+ "div",
99
+ {
100
+ ref: elementRef,
101
+ className: cn("elyri-fade-in", `elyri-fade-in--${direction}`, isVisible && "elyri-fade-in--visible", className),
102
+ style,
103
+ children
104
+ }
105
+ );
106
+ }
107
+ var easeOutCubic2 = (progress) => 1 - (1 - progress) ** 3;
108
+ function Magnetic({ children, className, strength = 0.35, range = 120, resetDuration = 400 }) {
109
+ const elementRef = useRef(null);
110
+ const prefersReducedMotion = usePrefersReducedMotion();
111
+ useEffect(() => {
112
+ const element = elementRef.current;
113
+ if (!element || prefersReducedMotion) return;
114
+ let moveFrame = 0;
115
+ let resetFrame = 0;
116
+ let offset = { x: 0, y: 0 };
117
+ let pointer = null;
118
+ const setOffset = (x, y) => {
119
+ offset = { x, y };
120
+ element.style.setProperty("--elyri-magnetic-x", `${x}px`);
121
+ element.style.setProperty("--elyri-magnetic-y", `${y}px`);
122
+ };
123
+ const startReset = () => {
124
+ if (resetFrame || !offset.x && !offset.y) return;
125
+ const from = { ...offset };
126
+ const startedAt = performance.now();
127
+ const step = (now) => {
128
+ const progress = Math.min((now - startedAt) / resetDuration, 1);
129
+ const remaining = 1 - easeOutCubic2(progress);
130
+ setOffset(from.x * remaining, from.y * remaining);
131
+ resetFrame = progress < 1 ? requestAnimationFrame(step) : 0;
132
+ };
133
+ resetFrame = requestAnimationFrame(step);
134
+ };
135
+ const handlePointerMove = (event) => {
136
+ pointer = { x: event.clientX, y: event.clientY };
137
+ if (moveFrame) return;
138
+ moveFrame = requestAnimationFrame(() => {
139
+ moveFrame = 0;
140
+ if (!pointer) return;
141
+ const rect = element.getBoundingClientRect();
142
+ const dx = pointer.x - (rect.left + rect.width / 2);
143
+ const dy = pointer.y - (rect.top + rect.height / 2);
144
+ const inside = Math.abs(dx) <= rect.width / 2 + range && Math.abs(dy) <= rect.height / 2 + range;
145
+ if (inside) {
146
+ cancelAnimationFrame(resetFrame);
147
+ resetFrame = 0;
148
+ const overflowX = Math.max(0, Math.abs(dx) - rect.width / 2);
149
+ const overflowY = Math.max(0, Math.abs(dy) - rect.height / 2);
150
+ const proximity = 1 - Math.min(Math.max(overflowX, overflowY) / range, 1);
151
+ setOffset(dx * strength * proximity, dy * strength * proximity);
152
+ return;
153
+ }
154
+ startReset();
155
+ });
156
+ };
157
+ window.addEventListener("pointermove", handlePointerMove, { passive: true });
158
+ document.documentElement.addEventListener("pointerleave", startReset);
159
+ return () => {
160
+ window.removeEventListener("pointermove", handlePointerMove);
161
+ document.documentElement.removeEventListener("pointerleave", startReset);
162
+ cancelAnimationFrame(moveFrame);
163
+ cancelAnimationFrame(resetFrame);
164
+ };
165
+ }, [prefersReducedMotion, range, strength, resetDuration]);
166
+ return /* @__PURE__ */ jsx("div", { ref: elementRef, className: cn("elyri-magnetic", className), children });
167
+ }
168
+ function ScaleIn({ children, className, from = 0.9, delay = 0, duration = 600, once = true }) {
169
+ const elementRef = useRef(null);
170
+ const prefersReducedMotion = usePrefersReducedMotion();
171
+ const [isIntersecting, setIsIntersecting] = useState(false);
172
+ useEffect(() => {
173
+ const element = elementRef.current;
174
+ if (!element || prefersReducedMotion) return;
175
+ const observer = new IntersectionObserver(
176
+ ([entry]) => {
177
+ if (entry.isIntersecting) {
178
+ setIsIntersecting(true);
179
+ if (once) observer.disconnect();
180
+ return;
181
+ }
182
+ if (!once) setIsIntersecting(false);
183
+ },
184
+ { threshold: 0.1 }
185
+ );
186
+ observer.observe(element);
187
+ return () => observer.disconnect();
188
+ }, [once, prefersReducedMotion]);
189
+ const style = {
190
+ "--elyri-scale-in-from": from,
191
+ "--elyri-scale-in-delay": `${delay}ms`,
192
+ "--elyri-scale-in-duration": `${duration}ms`
193
+ };
194
+ const isVisible = prefersReducedMotion || isIntersecting;
195
+ return /* @__PURE__ */ jsx(
196
+ "div",
197
+ {
198
+ ref: elementRef,
199
+ className: cn("elyri-scale-in", isVisible && "elyri-scale-in--visible", className),
200
+ style,
201
+ children
202
+ }
203
+ );
204
+ }
205
+ function Spotlight({
206
+ children,
207
+ className,
208
+ color = "#7c8cff",
209
+ size = 240,
210
+ glow = true,
211
+ borderGlow = true,
212
+ radius = 16
213
+ }) {
214
+ const elementRef = useRef(null);
215
+ const frameRef = useRef(0);
216
+ const pointerRef = useRef(null);
217
+ const itemsRef = useRef([]);
218
+ useEffect(
219
+ () => () => {
220
+ cancelAnimationFrame(frameRef.current);
221
+ },
222
+ []
223
+ );
224
+ const handlePointerMove = (event) => {
225
+ pointerRef.current = { x: event.clientX, y: event.clientY };
226
+ if (frameRef.current) return;
227
+ frameRef.current = requestAnimationFrame(() => {
228
+ frameRef.current = 0;
229
+ const element = elementRef.current;
230
+ const pointer = pointerRef.current;
231
+ if (!element || !pointer) return;
232
+ const rect = element.getBoundingClientRect();
233
+ element.style.setProperty("--elyri-spotlight-x", `${pointer.x - rect.left}px`);
234
+ element.style.setProperty("--elyri-spotlight-y", `${pointer.y - rect.top}px`);
235
+ for (const item of itemsRef.current) {
236
+ const itemRect = item.getBoundingClientRect();
237
+ item.style.setProperty("--elyri-spotlight-x", `${pointer.x - itemRect.left}px`);
238
+ item.style.setProperty("--elyri-spotlight-y", `${pointer.y - itemRect.top}px`);
239
+ }
240
+ });
241
+ };
242
+ const handlePointerEnter = (event) => {
243
+ itemsRef.current = Array.from(
244
+ event.currentTarget.querySelectorAll(".elyri-spotlight__item")
245
+ );
246
+ handlePointerMove(event);
247
+ };
248
+ const style = {
249
+ "--elyri-spotlight-color": color,
250
+ "--elyri-spotlight-size": `${size}px`,
251
+ "--elyri-spotlight-radius": `${radius}px`
252
+ };
253
+ return /* @__PURE__ */ jsx(
254
+ "div",
255
+ {
256
+ ref: elementRef,
257
+ className: cn(
258
+ "elyri-spotlight",
259
+ glow && "elyri-spotlight--glow",
260
+ borderGlow && "elyri-spotlight--border",
261
+ className
262
+ ),
263
+ style,
264
+ onPointerEnter: handlePointerEnter,
265
+ onPointerMove: handlePointerMove,
266
+ children: /* @__PURE__ */ jsx("div", { className: "elyri-spotlight__content", children })
267
+ }
268
+ );
269
+ }
270
+ var RESET_DURATION = 400;
271
+ var easeOutCubic3 = (progress) => 1 - (1 - progress) ** 3;
272
+ function Tilt({ children, className, max = 10, perspective = 800, glare = true }) {
273
+ const elementRef = useRef(null);
274
+ const moveFrameRef = useRef(0);
275
+ const resetFrameRef = useRef(0);
276
+ const rotationRef = useRef({ x: 0, y: 0 });
277
+ const pointerRef = useRef(null);
278
+ const prefersReducedMotion = usePrefersReducedMotion();
279
+ const [isActive, setIsActive] = useState(false);
280
+ useEffect(
281
+ () => () => {
282
+ cancelAnimationFrame(moveFrameRef.current);
283
+ cancelAnimationFrame(resetFrameRef.current);
284
+ },
285
+ []
286
+ );
287
+ const setRotation = (x, y) => {
288
+ rotationRef.current = { x, y };
289
+ const element = elementRef.current;
290
+ if (!element) return;
291
+ element.style.setProperty("--elyri-tilt-rotate-x", `${x}deg`);
292
+ element.style.setProperty("--elyri-tilt-rotate-y", `${y}deg`);
293
+ };
294
+ const handlePointerMove = (event) => {
295
+ if (prefersReducedMotion) return;
296
+ cancelAnimationFrame(resetFrameRef.current);
297
+ resetFrameRef.current = 0;
298
+ pointerRef.current = { x: event.clientX, y: event.clientY };
299
+ setIsActive(true);
300
+ if (moveFrameRef.current) return;
301
+ moveFrameRef.current = requestAnimationFrame(() => {
302
+ moveFrameRef.current = 0;
303
+ const element = elementRef.current;
304
+ const pointer = pointerRef.current;
305
+ if (!element || !pointer) return;
306
+ const rect = element.getBoundingClientRect();
307
+ if (!rect.width || !rect.height) return;
308
+ const ratioX = (pointer.x - rect.left) / rect.width;
309
+ const ratioY = (pointer.y - rect.top) / rect.height;
310
+ setRotation((0.5 - ratioY) * 2 * max, (ratioX - 0.5) * 2 * max);
311
+ element.style.setProperty("--elyri-tilt-glare-x", `${ratioX * 100}%`);
312
+ element.style.setProperty("--elyri-tilt-glare-y", `${ratioY * 100}%`);
313
+ });
314
+ };
315
+ const handlePointerEnd = () => {
316
+ cancelAnimationFrame(moveFrameRef.current);
317
+ moveFrameRef.current = 0;
318
+ cancelAnimationFrame(resetFrameRef.current);
319
+ resetFrameRef.current = 0;
320
+ pointerRef.current = null;
321
+ setIsActive(false);
322
+ const from = { ...rotationRef.current };
323
+ if (prefersReducedMotion || !from.x && !from.y) return;
324
+ const startedAt = performance.now();
325
+ const step = (now) => {
326
+ const progress = Math.min((now - startedAt) / RESET_DURATION, 1);
327
+ const remaining = 1 - easeOutCubic3(progress);
328
+ setRotation(from.x * remaining, from.y * remaining);
329
+ if (progress < 1) resetFrameRef.current = requestAnimationFrame(step);
330
+ };
331
+ resetFrameRef.current = requestAnimationFrame(step);
332
+ };
333
+ return /* @__PURE__ */ jsxs(
334
+ "div",
335
+ {
336
+ ref: elementRef,
337
+ className: cn("elyri-tilt", isActive && "elyri-tilt--active", className),
338
+ style: { "--elyri-tilt-perspective": `${perspective}px` },
339
+ onPointerMove: handlePointerMove,
340
+ onPointerLeave: handlePointerEnd,
341
+ onPointerCancel: handlePointerEnd,
342
+ children: [
343
+ children,
344
+ glare && /* @__PURE__ */ jsx("span", { className: "elyri-tilt__glare", "aria-hidden": "true" })
345
+ ]
346
+ }
347
+ );
348
+ }
349
+ var DEFAULT_COLORS = ["#3cffb0", "#2a8cff", "#c04dff"];
350
+ var FRAGMENT_SHADER = `
351
+ precision highp float;
352
+
353
+ uniform vec2 uResolution;
354
+ uniform float uTime;
355
+ uniform vec2 uPointer;
356
+ uniform vec3 uColor1;
357
+ uniform vec3 uColor2;
358
+ uniform vec3 uColor3;
359
+ uniform float uIntensity;
360
+ uniform float uStars;
361
+
362
+ float hash(vec2 p) {
363
+ p = fract(p * vec2(123.34, 456.21));
364
+ p += dot(p, p + 45.32);
365
+ return fract(p.x * p.y);
366
+ }
367
+
368
+ float noise(vec2 p) {
369
+ vec2 i = floor(p);
370
+ vec2 f = fract(p);
371
+ vec2 u = f * f * (3.0 - 2.0 * f);
372
+ return mix(
373
+ mix(hash(i), hash(i + vec2(1.0, 0.0)), u.x),
374
+ mix(hash(i + vec2(0.0, 1.0)), hash(i + vec2(1.0, 1.0)), u.x),
375
+ u.y
376
+ );
377
+ }
378
+
379
+ float fbm(vec2 p) {
380
+ float v = 0.0;
381
+ float a = 0.5;
382
+ for (int i = 0; i < 4; i++) {
383
+ v += a * noise(p);
384
+ p = p * 2.03 + vec2(17.1, 9.2);
385
+ a *= 0.5;
386
+ }
387
+ return v;
388
+ }
389
+
390
+ vec3 aces(vec3 x) {
391
+ return clamp((x * (2.51 * x + 0.03)) / (x * (2.43 * x + 0.59) + 0.14), 0.0, 1.0);
392
+ }
393
+
394
+ void main() {
395
+ vec2 uv = gl_FragCoord.xy / uResolution.y;
396
+ vec2 pointer = vec2(uPointer.x, uResolution.y - uPointer.y) / uResolution.y;
397
+ float t = uTime;
398
+
399
+ // \u591C\u7A7A\u5E95\u8272\uFF1A\u5730\u5E73\u7EBF\u9644\u8FD1\u7565\u5E26\u5E18\u5E95\u8272\u7684\u5FAE\u5149
400
+ vec3 color = mix(vec3(0.002, 0.004, 0.012), vec3(0.006, 0.01, 0.03), uv.y);
401
+ color += uColor1 * 0.025 * exp(-uv.y * 5.0);
402
+
403
+ // \u661F\u7A7A
404
+ vec2 grid = uv * 55.0;
405
+ vec2 cell = floor(grid);
406
+ float rnd = hash(cell);
407
+ vec2 offset = vec2(hash(cell + 3.1), hash(cell + 7.7)) - 0.5;
408
+ float starDist = length(fract(grid) - 0.5 - offset * 0.7);
409
+ float twinkle = 0.6 + 0.4 * sin(t * (1.5 + rnd * 3.0) + rnd * 40.0);
410
+ float star = step(0.93, rnd) * exp(-starDist * starDist * 180.0) * twinkle * smoothstep(0.1, 0.6, uv.y);
411
+ color += vec3(0.8, 0.85, 1.0) * star * uStars;
412
+
413
+ vec3 aurora = vec3(0.0);
414
+ for (int i = 0; i < 4; i++) {
415
+ float fi = float(i);
416
+ float x = uv.x * (0.6 + fi * 0.15) + fi * 3.7;
417
+ float curve = 0.42 + fi * 0.09 + (fbm(vec2(x * 1.2 - t * 0.05 * (1.0 + fi * 0.3), fi * 5.3 + t * 0.03)) - 0.5) * 0.45;
418
+ float d = uv.y - curve;
419
+ float body = d > 0.0 ? exp(-d * (3.5 + fi)) : exp(d * 40.0);
420
+ float rays = fbm(vec2(x * 28.0 + curve * 6.0, t * 0.35 + fi));
421
+ rays = 0.25 + 1.5 * rays * rays;
422
+ float flow = 0.6 + 0.4 * sin(x * 3.0 - t * 0.4 + fi * 1.7);
423
+ vec3 c = mix(uColor1, uColor2, smoothstep(0.0, 0.3, d));
424
+ c = mix(c, uColor3, smoothstep(0.12, 0.55, d) * (0.55 + 0.45 * sin(fi * 2.1 + x * 0.8 + t * 0.1)));
425
+ aurora += c * body * rays * flow * (0.5 - fi * 0.08);
426
+ }
427
+
428
+ vec2 dp = uv - pointer;
429
+ aurora *= 1.0 + 0.7 * exp(-dot(dp, dp) * 6.0);
430
+ color += aurora * uIntensity;
431
+
432
+ vec2 vc = gl_FragCoord.xy / uResolution - 0.5;
433
+ color *= 1.0 - dot(vc, vc) * 0.9;
434
+ color = pow(aces(color * 1.2), vec3(1.0 / 2.2));
435
+ color += (hash(gl_FragCoord.xy + fract(t)) - 0.5) / 255.0;
436
+
437
+ gl_FragColor = vec4(color, 1.0);
438
+ }
439
+ `;
440
+ function Aurora({
441
+ children,
442
+ className,
443
+ colors = DEFAULT_COLORS,
444
+ speed = 1,
445
+ intensity = 1,
446
+ stars = true,
447
+ interactive = true
448
+ }) {
449
+ const [bottom = DEFAULT_COLORS[0], middle = DEFAULT_COLORS[1], top = DEFAULT_COLORS[2]] = colors;
450
+ const canvasRef = useShaderCanvas({
451
+ fragmentShader: FRAGMENT_SHADER,
452
+ speed,
453
+ interactive,
454
+ onDraw: ({ gl, uniform }) => {
455
+ gl.uniform3fv(uniform("uColor1"), hexToLinearRgb(bottom));
456
+ gl.uniform3fv(uniform("uColor2"), hexToLinearRgb(middle));
457
+ gl.uniform3fv(uniform("uColor3"), hexToLinearRgb(top));
458
+ gl.uniform1f(uniform("uIntensity"), Math.max(0, intensity));
459
+ gl.uniform1f(uniform("uStars"), stars ? 1 : 0);
460
+ }
461
+ });
462
+ return /* @__PURE__ */ jsxs("div", { className: cn("elyri-aurora", className), style: { "--elyri-aurora-color": bottom }, children: [
463
+ /* @__PURE__ */ jsx("canvas", { ref: canvasRef, className: "elyri-aurora__canvas", "aria-hidden": "true" }),
464
+ children != null && /* @__PURE__ */ jsx("div", { className: "elyri-aurora__content", children })
465
+ ] });
466
+ }
467
+ var FRAGMENT_SHADER2 = `
468
+ precision highp float;
469
+
470
+ uniform vec2 uResolution;
471
+ uniform float uTime;
472
+ uniform vec2 uPointer;
473
+ uniform vec3 uBase;
474
+ uniform vec3 uHighlight;
475
+ uniform float uDensity;
476
+ uniform float uRipple;
477
+
478
+ float hash(vec2 p) {
479
+ p = fract(p * vec2(123.34, 456.21));
480
+ p += dot(p, p + 45.32);
481
+ return fract(p.x * p.y);
482
+ }
483
+
484
+ vec2 hash2(vec2 p) {
485
+ p = vec2(dot(p, vec2(127.1, 311.7)), dot(p, vec2(269.5, 183.3)));
486
+ return fract(sin(p) * 43758.5453);
487
+ }
488
+
489
+ float cellEdge(vec2 p, float t) {
490
+ vec2 i = floor(p);
491
+ vec2 f = fract(p);
492
+ float f1 = 8.0;
493
+ float f2 = 8.0;
494
+ for (int y = -1; y <= 1; y++) {
495
+ for (int x = -1; x <= 1; x++) {
496
+ vec2 g = vec2(float(x), float(y));
497
+ vec2 o = 0.5 + 0.4 * sin(t * 0.8 + 6.2831 * hash2(i + g));
498
+ float d = length(g + o - f);
499
+ if (d < f1) {
500
+ f2 = f1;
501
+ f1 = d;
502
+ } else if (d < f2) {
503
+ f2 = d;
504
+ }
505
+ }
506
+ }
507
+ return f2 - f1;
508
+ }
509
+
510
+ float caustic(vec2 p, float t) {
511
+ p += 0.25 * vec2(sin(p.y * 1.3 + t * 0.7), cos(p.x * 1.1 - t * 0.6));
512
+ float a = exp(-cellEdge(p, t) * 9.0);
513
+ float b = exp(-cellEdge(p * 1.7 + vec2(3.1, 7.4), t * 1.3) * 9.0);
514
+ return a * 0.7 + b * 0.45 + a * b * 1.5;
515
+ }
516
+
517
+ vec3 aces(vec3 x) {
518
+ return clamp((x * (2.51 * x + 0.03)) / (x * (2.43 * x + 0.59) + 0.14), 0.0, 1.0);
519
+ }
520
+
521
+ void main() {
522
+ vec2 frag = vec2(gl_FragCoord.x, uResolution.y - gl_FragCoord.y);
523
+ float minRes = min(uResolution.x, uResolution.y);
524
+ float t = uTime;
525
+ vec2 p = frag / minRes * 4.0 * uDensity;
526
+
527
+ // \u6307\u9488\u5904\u8361\u5F00\u7684\u6C34\u6CE2
528
+ vec2 dp = (frag - uPointer) / minRes;
529
+ float r = length(dp);
530
+ p += dp / (r + 1e-4) * sin(r * 38.0 - t * 4.0) * 0.06 * exp(-r * 5.0) * uRipple;
531
+
532
+ vec3 caus = vec3(
533
+ caustic(p + vec2(0.014, 0.0), t),
534
+ caustic(p, t),
535
+ caustic(p - vec2(0.014, 0.0), t)
536
+ );
537
+
538
+ float depth = frag.y / uResolution.y;
539
+ vec3 color = mix(uBase * 0.35, uBase * 0.025, depth);
540
+
541
+ // \u659C\u5C04\u5149\u675F
542
+ float x = frag.x / minRes + depth * 0.35;
543
+ float rays = (0.5 + 0.5 * sin(x * 9.0 + sin(x * 3.1 + t * 0.3) * 2.0 + t * 0.2)) * (0.5 + 0.5 * sin(x * 17.0 - t * 0.35));
544
+ color += uHighlight * 0.12 * rays * rays * (1.0 - depth) * (1.0 - depth);
545
+
546
+ float focus = 0.6 + 0.8 * exp(-dot(dp, dp) * 3.0) * uRipple;
547
+ color += uHighlight * caus * 0.5 * mix(1.0, 0.3, depth) * focus;
548
+
549
+ vec2 vc = frag / uResolution - 0.5;
550
+ color *= 1.0 - dot(vc, vc) * 1.1;
551
+ color = pow(aces(color * 1.2), vec3(1.0 / 2.2));
552
+ color += (hash(frag + fract(t)) - 0.5) / 255.0;
553
+
554
+ gl_FragColor = vec4(color, 1.0);
555
+ }
556
+ `;
557
+ function Caustics({
558
+ children,
559
+ className,
560
+ color = "#0a6fa8",
561
+ highlightColor = "#c8fbff",
562
+ density = 1,
563
+ speed = 1,
564
+ interactive = true
565
+ }) {
566
+ const canvasRef = useShaderCanvas({
567
+ fragmentShader: FRAGMENT_SHADER2,
568
+ speed,
569
+ interactive,
570
+ onDraw: ({ gl, uniform }) => {
571
+ gl.uniform3fv(uniform("uBase"), hexToLinearRgb(color));
572
+ gl.uniform3fv(uniform("uHighlight"), hexToLinearRgb(highlightColor));
573
+ gl.uniform1f(uniform("uDensity"), Math.max(0.2, density));
574
+ gl.uniform1f(uniform("uRipple"), interactive ? 1 : 0);
575
+ }
576
+ });
577
+ return /* @__PURE__ */ jsxs("div", { className: cn("elyri-caustics", className), style: { "--elyri-caustics-color": color }, children: [
578
+ /* @__PURE__ */ jsx("canvas", { ref: canvasRef, className: "elyri-caustics__canvas", "aria-hidden": "true" }),
579
+ children != null && /* @__PURE__ */ jsx("div", { className: "elyri-caustics__content", children })
580
+ ] });
581
+ }
582
+ var FRAGMENT_SHADER3 = `
583
+ precision highp float;
584
+
585
+ uniform vec2 uResolution;
586
+ uniform float uTime;
587
+ uniform vec2 uPointer;
588
+ uniform vec3 uLine;
589
+ uniform vec3 uHighlight;
590
+ uniform vec3 uBg;
591
+ uniform float uLevels;
592
+ uniform float uDpr;
593
+
594
+ const float SCALE = 2.4;
595
+
596
+ float hash(vec2 p) {
597
+ vec3 p3 = fract(vec3(p.xyx) * 0.1031);
598
+ p3 += dot(p3, p3.yzx + 33.33);
599
+ return fract((p3.x + p3.y) * p3.z);
600
+ }
601
+
602
+ float noise(vec2 p) {
603
+ vec2 i = floor(p);
604
+ vec2 f = fract(p);
605
+ vec2 u = f * f * f * (f * (f * 6.0 - 15.0) + 10.0);
606
+ return mix(mix(hash(i), hash(i + vec2(1.0, 0.0)), u.x), mix(hash(i + vec2(0.0, 1.0)), hash(i + 1.0), u.x), u.y);
607
+ }
608
+
609
+ float fbm(vec2 p) {
610
+ float v = 0.0;
611
+ float a = 0.5;
612
+ mat2 rot = mat2(0.8, -0.6, 0.6, 0.8);
613
+ for (int i = 0; i < 3; i++) {
614
+ v += a * noise(p);
615
+ p = rot * p * 2.03 + 17.1;
616
+ a *= 0.5;
617
+ }
618
+ return v;
619
+ }
620
+
621
+ float terrain(vec2 p, vec2 hill, float t) {
622
+ vec2 warp = vec2(noise(p * 0.55 + vec2(t * 0.08, 3.1)), noise(p * 0.55 + vec2(7.7, -t * 0.07)));
623
+ float h = fbm(p + warp * 1.3 + vec2(t * 0.035, -t * 0.025));
624
+ vec2 d = p - hill;
625
+ return h + 0.2 * exp(-dot(d, d) * 1.6);
626
+ }
627
+
628
+ void main() {
629
+ vec2 frag = vec2(gl_FragCoord.x, uResolution.y - gl_FragCoord.y);
630
+ float minRes = min(uResolution.x, uResolution.y);
631
+ float px = SCALE / minRes;
632
+ vec2 p = frag * px;
633
+ vec2 hill = uPointer * px;
634
+ float t = uTime;
635
+
636
+ float h = terrain(p, hill, t);
637
+ float hx = terrain(p + vec2(px, 0.0), hill, t);
638
+ float hy = terrain(p + vec2(0.0, px), hill, t);
639
+
640
+ float f = h * uLevels;
641
+ float slope = max(length(vec2(hx - h, hy - h)) * uLevels, 1e-5);
642
+ float fr = fract(f);
643
+ float dist = min(fr, 1.0 - fr) / slope;
644
+
645
+ float index = floor(f + 0.5);
646
+ float major = 1.0 - step(0.5, abs(mod(index, 5.0)));
647
+ float halfWidth = mix(0.5, 0.95, major) * uDpr;
648
+ float line = 1.0 - smoothstep(halfWidth - 0.6, halfWidth + 0.6, dist);
649
+
650
+ // \u7EBF\u95F4\u8DDD\u5C0F\u4E8E\u6570\u50CF\u7D20\u65F6\u6DE1\u51FA\uFF0C\u907F\u514D\u9661\u5761\u5904\u7CCA\u6210\u4E00\u7247
651
+ line *= smoothstep(2.5 * uDpr, 6.0 * uDpr, 1.0 / slope);
652
+
653
+ float radius = 240.0 * uDpr;
654
+ vec2 dp = frag - uPointer;
655
+ float near = exp(-dot(dp, dp) / (radius * radius));
656
+
657
+ vec2 q = (frag - 0.5 * uResolution) / (0.5 * uResolution);
658
+ float mask = mix(0.4, 1.0, 1.0 - smoothstep(0.45, 1.4, length(q)));
659
+
660
+ float alpha = line * mix(0.32, 0.7, major) * (1.0 + 0.9 * near) * mask;
661
+ vec3 lineColor = mix(uLine, uHighlight, clamp(near * 1.2, 0.0, 1.0));
662
+ vec3 color = mix(uBg, lineColor, clamp(alpha, 0.0, 1.0));
663
+
664
+ color = pow(color, vec3(1.0 / 2.2));
665
+ color += (hash(frag + fract(t)) - 0.5) / 255.0;
666
+
667
+ gl_FragColor = vec4(color, 1.0);
668
+ }
669
+ `;
670
+ function Contours({
671
+ children,
672
+ className,
673
+ color = "#8a90a6",
674
+ highlightColor = "#f08a4b",
675
+ density = 1,
676
+ speed = 1,
677
+ interactive = true
678
+ }) {
679
+ const scheme = useColorScheme();
680
+ const canvasRef = useShaderCanvas({
681
+ fragmentShader: FRAGMENT_SHADER3,
682
+ speed,
683
+ interactive,
684
+ onDraw: ({ gl, uniform, dpr }) => {
685
+ gl.uniform3fv(uniform("uLine"), hexToLinearRgb(color));
686
+ gl.uniform3fv(uniform("uHighlight"), hexToLinearRgb(highlightColor));
687
+ gl.uniform3fv(uniform("uBg"), hexToLinearRgb(scheme === "light" ? "#f9f9f7" : "#0a0b0f"));
688
+ gl.uniform1f(uniform("uLevels"), 18 * Math.max(0.2, density));
689
+ gl.uniform1f(uniform("uDpr"), dpr);
690
+ }
691
+ });
692
+ return /* @__PURE__ */ jsxs(
693
+ "div",
694
+ {
695
+ className: cn("elyri-contours", className),
696
+ "data-scheme": scheme,
697
+ style: { "--elyri-contours-color": color },
698
+ children: [
699
+ /* @__PURE__ */ jsx("canvas", { ref: canvasRef, className: "elyri-contours__canvas", "aria-hidden": "true" }),
700
+ children != null && /* @__PURE__ */ jsx("div", { className: "elyri-contours__content", children })
701
+ ]
702
+ }
703
+ );
704
+ }
705
+ var FRAGMENT_SHADER4 = `
706
+ precision highp float;
707
+
708
+ uniform vec2 uResolution;
709
+ uniform float uTime;
710
+ uniform vec2 uPointer;
711
+ uniform vec3 uDot;
712
+ uniform vec3 uHighlight;
713
+ uniform vec3 uBg;
714
+ uniform float uGap;
715
+ uniform float uDpr;
716
+
717
+ float hash(vec2 p) {
718
+ vec3 p3 = fract(vec3(p.xyx) * 0.1031);
719
+ p3 += dot(p3, p3.yzx + 33.33);
720
+ return fract((p3.x + p3.y) * p3.z);
721
+ }
722
+
723
+ float noise(vec2 p) {
724
+ vec2 i = floor(p);
725
+ vec2 f = fract(p);
726
+ vec2 u = f * f * f * (f * (f * 6.0 - 15.0) + 10.0);
727
+ return mix(mix(hash(i), hash(i + vec2(1.0, 0.0)), u.x), mix(hash(i + vec2(0.0, 1.0)), hash(i + 1.0), u.x), u.y);
728
+ }
729
+
730
+ void main() {
731
+ vec2 frag = vec2(gl_FragCoord.x, uResolution.y - gl_FragCoord.y);
732
+ float gap = uGap * uDpr;
733
+ float radius = 170.0 * uDpr;
734
+ float t = uTime;
735
+ vec2 cell = floor(frag / gap);
736
+
737
+ float best = 0.0;
738
+ float bestTint = 0.0;
739
+ for (int j = -1; j <= 1; j++) {
740
+ for (int i = -1; i <= 1; i++) {
741
+ vec2 id = cell + vec2(float(i), float(j));
742
+ vec2 center = (id + 0.5) * gap;
743
+
744
+ vec2 away = center - uPointer;
745
+ float d = length(away);
746
+ float lens = exp(-d * d / (radius * radius));
747
+ center += away / max(d, 1.0) * lens * gap * 0.32;
748
+
749
+ // \u4E24\u5C42\u566A\u58F0\uFF1A\u5927\u5C3A\u5EA6\u7684\u300C\u98CE\u300D+ \u5C0F\u5C3A\u5EA6\u7684\u7EC6\u788E\u8D77\u4F0F
750
+ float wave = noise(id * 0.075 + vec2(t * 0.11, -t * 0.07)) * 0.65
751
+ + noise(id * 0.21 - vec2(t * 0.05, t * 0.09)) * 0.35;
752
+ wave = smoothstep(0.3, 0.85, wave);
753
+
754
+ float r = max(gap * (0.055 + 0.075 * wave + 0.1 * lens), 0.7 * uDpr);
755
+ float cover = 1.0 - smoothstep(r - 0.8, r + 0.8, length(frag - center));
756
+ float alpha = cover * clamp(0.22 + 0.5 * wave + 0.55 * lens, 0.0, 1.0);
757
+ if (alpha > best) {
758
+ best = alpha;
759
+ bestTint = clamp(wave * wave * 0.55 + lens * 0.95, 0.0, 1.0);
760
+ }
761
+ }
762
+ }
763
+
764
+ // \u56DB\u5468\u6E10\u9690\uFF0C\u89C6\u7EBF\u805A\u7126\u5728\u4E2D\u592E
765
+ vec2 q = (frag - 0.5 * uResolution) / (0.5 * uResolution);
766
+ float mask = mix(0.18, 1.0, 1.0 - smoothstep(0.3, 1.3, length(q)));
767
+
768
+ vec3 dotColor = mix(uDot, uHighlight, bestTint);
769
+ vec3 color = mix(uBg, dotColor, best * mask);
770
+
771
+ // \u6307\u9488\u4E0B\u65B9\u6781\u6DE1\u7684\u5149\u6655
772
+ float glow = exp(-dot(frag - uPointer, frag - uPointer) / (radius * radius * 2.5));
773
+ color += uHighlight * glow * 0.035;
774
+
775
+ color = pow(color, vec3(1.0 / 2.2));
776
+ color += (hash(frag + fract(t)) - 0.5) / 255.0;
777
+
778
+ gl_FragColor = vec4(color, 1.0);
779
+ }
780
+ `;
781
+ function DotField({
782
+ children,
783
+ className,
784
+ color = "#8a90a6",
785
+ highlightColor = "#7c6cff",
786
+ gap = 22,
787
+ speed = 1,
788
+ interactive = true
789
+ }) {
790
+ const scheme = useColorScheme();
791
+ const canvasRef = useShaderCanvas({
792
+ fragmentShader: FRAGMENT_SHADER4,
793
+ speed,
794
+ interactive,
795
+ onDraw: ({ gl, uniform, dpr }) => {
796
+ gl.uniform3fv(uniform("uDot"), hexToLinearRgb(color));
797
+ gl.uniform3fv(uniform("uHighlight"), hexToLinearRgb(highlightColor));
798
+ gl.uniform3fv(uniform("uBg"), hexToLinearRgb(scheme === "light" ? "#fafafb" : "#0a0a0e"));
799
+ gl.uniform1f(uniform("uGap"), Math.max(8, gap));
800
+ gl.uniform1f(uniform("uDpr"), dpr);
801
+ }
802
+ });
803
+ return /* @__PURE__ */ jsxs(
804
+ "div",
805
+ {
806
+ className: cn("elyri-dot-field", className),
807
+ "data-scheme": scheme,
808
+ style: { "--elyri-dot-field-color": color, "--elyri-dot-field-gap": `${Math.max(8, gap)}px` },
809
+ children: [
810
+ /* @__PURE__ */ jsx("canvas", { ref: canvasRef, className: "elyri-dot-field__canvas", "aria-hidden": "true" }),
811
+ children != null && /* @__PURE__ */ jsx("div", { className: "elyri-dot-field__content", children })
812
+ ]
813
+ }
814
+ );
815
+ }
816
+ var FRAGMENT_SHADER5 = `
817
+ precision highp float;
818
+
819
+ uniform vec2 uResolution;
820
+ uniform float uTime;
821
+ uniform vec2 uPointer;
822
+ uniform float uScale;
823
+ uniform vec3 uBase;
824
+ uniform vec3 uHighlight;
825
+ uniform float uFlare;
826
+ uniform float uScheme;
827
+
828
+ const float R = 0.92;
829
+ const float D = 0.30;
830
+ const float B = 0.8697;
831
+ const float ROW = 0.5;
832
+ const float BEVEL = 0.075;
833
+ const float TAU = 6.28318;
834
+
835
+ float hash(vec2 p) {
836
+ p = fract(p * vec2(123.34, 456.21));
837
+ p += dot(p, p + 45.32);
838
+ return fract(p.x * p.y);
839
+ }
840
+
841
+ float sdScale(vec2 p) {
842
+ p = abs(p);
843
+ return ((p.y - B) * D > p.x * B) ? length(p - vec2(0.0, B)) : length(p - vec2(-D, 0.0)) - R;
844
+ }
845
+
846
+ float scaleHeight(vec2 q) {
847
+ float inner = clamp(-sdScale(q) / BEVEL, 0.0, 1.0);
848
+ float bevel = sqrt(1.0 - (1.0 - inner) * (1.0 - inner));
849
+ vec2 d = vec2(q.x / 0.62, (q.y - 0.35) / 0.75);
850
+ float dome = 0.14 * (1.0 - dot(d, d));
851
+ float keel = 0.035 * exp(-q.x * q.x / 0.03) * smoothstep(-0.1, B, q.y);
852
+ return bevel * (0.07 + dome + keel);
853
+ }
854
+
855
+ // \u5F00\u5408\uFF1A\u4E00\u9053\u6CE2\u6CBF\u8EAB\u4F53\u65B9\u5411\u63A8\u8FDB\uFF0C\u7ECF\u8FC7\u7684\u9CDE\u7247\u4F9D\u6B21\u5F20\u5F00\u518D\u5408\u62E2
856
+ float openAmount(vec2 c) {
857
+ return uFlare * smoothstep(0.15, 1.0, sin(c.y * 0.45 - uTime * 1.3 + c.x * 0.3));
858
+ }
859
+
860
+ // \u5F20\u5F00\u65F6\u9CDE\u7247\u7ED5\u6839\u90E8\u7FD8\u8D77\uFF0C\u900F\u89C6\u4E0A\u8868\u73B0\u4E3A\u9CDE\u5C16\u4F38\u957F\u3001\u7565\u5FAE\u53D8\u5BBD
861
+ vec2 warp(vec2 q, float o) {
862
+ return vec2(q.x / (1.0 + 0.05 * o), (q.y + 0.2) / (1.0 + 0.2 * o) - 0.2);
863
+ }
864
+
865
+ vec3 aces(vec3 x) {
866
+ return clamp((x * (2.51 * x + 0.03)) / (x * (2.43 * x + 0.59) + 0.14), 0.0, 1.0);
867
+ }
868
+
869
+ void main() {
870
+ vec2 frag = vec2(gl_FragCoord.x, uResolution.y - gl_FragCoord.y);
871
+ // \u5F62\u72B6\u7A7A\u95F4\u7684 y\uFF08\u9CDE\u5C16\u65B9\u5411\uFF09\u5BF9\u5E94\u5C4F\u5E55 x\uFF0C\u5B9E\u73B0\u4FA7\u5411\u6392\u5217
872
+ vec2 p = frag.yx / uScale;
873
+ float px = 1.0 / uScale;
874
+ float t = uTime;
875
+
876
+ // \u4ECE\u5DE6\u5230\u53F3\u67E5\u627E\u7B2C\u4E00\u7247\u8986\u76D6\u5F53\u524D\u50CF\u7D20\u7684\u9CDE\uFF0C\u5E76\u8BB0\u5F55\u5230\u538B\u5728\u4E0A\u9762\u7684\u9CDE\u7247\u8FB9\u7F18\u7684\u8DDD\u79BB\u3002
877
+ // \u9634\u5F71\u4E0E\u7F1D\u9699\u6563\u5149\u6309\u6BCF\u7247\u4E0A\u65B9\u9CDE\u5206\u522B\u8BA1\u7B97\u540E\u53D6\u6700\u5927\u503C\uFF0C\u907F\u514D\u5728\u4E24\u7247\u5F20\u5F00\u7A0B\u5EA6\u4E0D\u540C\u7684\u9CDE\u4E4B\u95F4\u51FA\u73B0\u63A5\u7F1D
878
+ float j0 = floor(p.y / ROW);
879
+ vec2 center = vec2(0.0);
880
+ vec2 cell = vec2(0.0);
881
+ float sd = -1.0;
882
+ float cover = 1.0;
883
+ float shade = 0.0;
884
+ float gap = 0.0;
885
+ float open = 0.0;
886
+ float above = 1.0;
887
+ float aboveShade = 0.0;
888
+ float aboveGap = 0.0;
889
+ float found = 0.0;
890
+ for (int dj = -2; dj <= 2; dj++) {
891
+ float j = j0 + float(dj);
892
+ float offset = mod(j, 2.0) * 0.5;
893
+ float i0 = floor(p.x - offset + 0.5);
894
+ float rowMin = 1.0;
895
+ float rowShade = 0.0;
896
+ float rowGap = 0.0;
897
+ for (int di = -1; di <= 1; di++) {
898
+ vec2 id = vec2(i0 + float(di), j);
899
+ vec2 c = vec2(id.x + offset, j * ROW);
900
+ float o = openAmount(c);
901
+ float d = sdScale(warp(p - c, o));
902
+ if (found < 0.5 && d < 0.0) {
903
+ found = 1.0;
904
+ center = c;
905
+ cell = id;
906
+ sd = d;
907
+ open = o;
908
+ cover = above;
909
+ shade = aboveShade;
910
+ gap = aboveGap;
911
+ }
912
+ rowMin = min(rowMin, d);
913
+ // \u4E0A\u65B9\u9CDE\u7247\u5F20\u5F00\u65F6\u6295\u4E0B\u66F4\u5BBD\u7684\u9634\u5F71\uFF0C\u4E0B\u65B9\u900F\u51FA\u6563\u5C04\u5149
914
+ rowShade = max(rowShade, 1.0 - smoothstep(0.0, 0.16 + 0.3 * o, d));
915
+ rowGap = max(rowGap, (1.0 - smoothstep(0.0, 0.1 + 0.25 * o, d)) * o);
916
+ }
917
+ if (found < 0.5) {
918
+ above = min(above, rowMin);
919
+ aboveShade = max(aboveShade, rowShade);
920
+ aboveGap = max(aboveGap, rowGap);
921
+ }
922
+ }
923
+
924
+ vec2 q = warp(p - center, open);
925
+ float seed = hash(cell);
926
+ float seed2 = hash(cell + 17.31);
927
+
928
+ float e = 0.006;
929
+ vec2 grad = vec2(
930
+ scaleHeight(q + vec2(e, 0.0)) - scaleHeight(q - vec2(e, 0.0)),
931
+ scaleHeight(q + vec2(0.0, e)) - scaleHeight(q - vec2(0.0, e))
932
+ ) / (2.0 * e);
933
+ vec3 ns = normalize(vec3(-grad, 1.0));
934
+
935
+ // \u6BCF\u7247\u9CDE\u5404\u81EA\u7565\u5FAE\u503E\u659C\uFF0C\u518D\u53E0\u4E00\u9053\u6CBF\u8EAB\u4F53\u65B9\u5411\u6EDA\u52A8\u7684\u8D77\u4F0F\uFF0C\u8BA9\u53CD\u5149\u6761\u5728\u9CDE\u95F4\u6D41\u52A8
936
+ float wave = sin(center.y * 0.35 - t * 0.9 + sin(center.x * 0.2 + t * 0.25) * 1.2);
937
+ ns = normalize(ns + vec3((seed - 0.5) * 0.22, (seed2 - 0.5) * 0.22 + wave * 0.1 - open * 0.45, 0.0));
938
+ vec3 n = vec3(ns.y, ns.x, ns.z);
939
+
940
+ float minRes = min(uResolution.x, uResolution.y);
941
+ vec3 V = vec3(0.0, 0.0, 1.0);
942
+ vec3 toLight = vec3(uPointer - frag, minRes * 0.6);
943
+ float dist = length(toLight);
944
+ vec3 L = toLight / dist;
945
+ float atten = 1.0 / (1.0 + 1.5 * pow(dist / minRes, 2.0));
946
+ float NdotH = max(dot(n, normalize(L + V)), 0.0);
947
+ float NdotV = max(n.z, 0.0);
948
+ float soft = pow(NdotH, 24.0) * atten;
949
+ float ao = mix(0.04, 1.0, 1.0 - shade);
950
+ float fres = 0.35 + 0.65 * pow(1.0 - NdotV, 2.0);
951
+
952
+ // \u7EBF\u5F62\u706F\u5E26\uFF1A\u4E3B\u706F\u5E26\u7A7F\u8FC7\u5149\u6E90\u4F4D\u7F6E\u5E76\u7F13\u6162\u8F6C\u52A8\uFF0C\u526F\u706F\u5E26\u53EA\u843D\u5728\u671D\u5411\u4E00\u4FA7\u7684\u5012\u89D2\u4E0A
953
+ vec3 Rf = reflect(-V, n);
954
+ vec2 toL = (uPointer - frag) / minRes;
955
+ float a = 0.7 + 0.45 * sin(t * 0.17);
956
+ vec2 dir = vec2(cos(a), sin(a));
957
+ float s1 = dot(Rf.xy, dir) - dot(toL, dir) * 1.3 + 0.12 * sin(t * 0.5 + seed * TAU);
958
+ float line1 = exp(-s1 * s1 / 0.0022);
959
+ float band1 = exp(-s1 * s1 / 0.09);
960
+ float s2 = dot(Rf.xy, vec2(-dir.y, dir.x)) - 0.85 - 0.15 * sin(t * 0.33);
961
+ float line2 = exp(-s2 * s2 / 0.01);
962
+
963
+ // \u672C\u4F53\u8FD1\u9ED1\uFF0C\u53EA\u5728\u671D\u5411\u5149\u6E90\u7684\u533A\u57DF\u6CDB\u51FA\u4E00\u5C42\u67D4\u548C\u7684\u5E95\u8272\uFF0C\u4EAE\u5EA6\u4E3B\u8981\u6765\u81EA\u53CD\u5149\u6761
964
+ vec3 bloom = mix(uBase, uHighlight, 0.35);
965
+ vec3 color = uBase * (0.006 + 0.5 * soft * mix(0.6, 1.1, seed)) * ao;
966
+ color += bloom * band1 * 0.3 * atten * ao;
967
+ color += uHighlight * line1 * 1.4 * fres * (0.3 + atten) * ao;
968
+ color += uHighlight * line2 * 0.18 * fres * ao;
969
+
970
+ // \u53CC\u7EBF\u9CDE\u7F18\uFF1A\u4E0A\u5C42\u9CDE\u7684\u8FB9\u4E0E\u88AB\u538B\u4F4F\u7684\u4E0B\u5C42\u9CDE\u7684\u8FB9\u5404\u4E00\u9053\u7EC6\u4EAE\u7EBF\uFF0C\u4E2D\u95F4\u7559\u4E00\u6761\u6697\u7F1D
971
+ float rimLight = (0.07 + 0.6 * soft + band1 * 0.9) * (1.0 + 1.2 * open);
972
+ float r1 = (sd + 1.6 * px) / (1.4 * px);
973
+ float r2 = (cover - 1.8 * px) / (1.4 * px);
974
+ color += uHighlight * exp(-r1 * r1) * rimLight * 0.9;
975
+ color += uHighlight * exp(-r2 * r2) * rimLight * 0.35;
976
+
977
+ color += mix(uBase, uHighlight, 0.2) * gap * 0.35 * (0.3 + atten + band1);
978
+
979
+ color += uHighlight * 0.03 * exp(-pow(length(frag - uPointer) / (minRes * 0.4), 2.0));
980
+
981
+ vec2 vc = frag / uResolution - 0.5;
982
+ float vig = dot(vc, vc);
983
+
984
+ if (uScheme > 0.5) {
985
+ // \u6D45\u8272\uFF1A\u5148\u8272\u8C03\u6620\u5C04\uFF0C\u518D\u505A\u4EAE\u6697\u955C\u50CF\uFF0C\u4FDD\u7559\u8272\u76F8 \u2014\u2014 \u9ED1\u66DC\u77F3\u9CDE\u9762\u7FFB\u8F6C\u6210\u6D45\u8272\u9CDE\u9762\uFF0C\u53CD\u5149\u6761\u8F6C\u4E3A\u6DF1\u8272\u9CDE\u7F18
986
+ vec3 c = pow(aces(color * 1.3), vec3(1.0 / 2.2));
987
+ float lum = dot(c, vec3(0.2126, 0.7152, 0.0722));
988
+ color = clamp(c + (1.0 - 2.0 * lum), 0.0, 1.0) * (1.0 - vig * 0.35);
989
+ } else {
990
+ color *= 1.0 - vig * 1.5;
991
+ color = pow(aces(color * 1.3), vec3(1.0 / 2.2));
992
+ }
993
+
994
+ color += (hash(frag + fract(t)) - 0.5) / 255.0;
995
+
996
+ gl_FragColor = vec4(color, 1.0);
997
+ }
998
+ `;
999
+ function DragonScales({
1000
+ children,
1001
+ className,
1002
+ color = "#7b3fe4",
1003
+ highlightColor = "#e7a6ff",
1004
+ scaleSize = 110,
1005
+ speed = 1,
1006
+ flare = 0,
1007
+ interactive = true,
1008
+ scheme: forcedScheme
1009
+ }) {
1010
+ const detectedScheme = useColorScheme();
1011
+ const scheme = forcedScheme ?? detectedScheme;
1012
+ const canvasRef = useShaderCanvas({
1013
+ fragmentShader: FRAGMENT_SHADER5,
1014
+ speed,
1015
+ interactive,
1016
+ onDraw: ({ gl, uniform, dpr }) => {
1017
+ gl.uniform1f(uniform("uScale"), Math.max(8, scaleSize) * dpr);
1018
+ gl.uniform3fv(uniform("uBase"), hexToLinearRgb(color));
1019
+ gl.uniform3fv(uniform("uHighlight"), hexToLinearRgb(highlightColor));
1020
+ gl.uniform1f(uniform("uFlare"), Math.min(Math.max(flare, 0), 1));
1021
+ gl.uniform1f(uniform("uScheme"), scheme === "light" ? 1 : 0);
1022
+ }
1023
+ });
1024
+ return /* @__PURE__ */ jsxs(
1025
+ "div",
1026
+ {
1027
+ className: cn("elyri-dragon-scales", className),
1028
+ "data-scheme": scheme,
1029
+ style: { "--elyri-dragon-scales-color": color },
1030
+ children: [
1031
+ /* @__PURE__ */ jsx("canvas", { ref: canvasRef, className: "elyri-dragon-scales__canvas", "aria-hidden": "true" }),
1032
+ children != null && /* @__PURE__ */ jsx("div", { className: "elyri-dragon-scales__content", children })
1033
+ ]
1034
+ }
1035
+ );
1036
+ }
1037
+ var FRAGMENT_SHADER6 = `
1038
+ precision highp float;
1039
+
1040
+ uniform vec2 uResolution;
1041
+ uniform float uTime;
1042
+ uniform vec2 uPointer;
1043
+ uniform vec3 uColor;
1044
+ uniform vec3 uHighlight;
1045
+ uniform vec3 uBg;
1046
+ uniform float uAmplitude;
1047
+ uniform float uDpr;
1048
+ uniform float uScheme;
1049
+
1050
+ const int COUNT = 7;
1051
+
1052
+ float hash(vec2 p) {
1053
+ vec3 p3 = fract(vec3(p.xyx) * 0.1031);
1054
+ p3 += dot(p3, p3.yzx + 33.33);
1055
+ return fract((p3.x + p3.y) * p3.z);
1056
+ }
1057
+
1058
+ vec3 shade(vec3 bg, vec3 light) {
1059
+ if (uScheme > 0.5) {
1060
+ float amount = max(max(light.r, light.g), light.b);
1061
+ vec3 tint = light / max(amount, 1e-4);
1062
+ return bg * exp(-amount * 1.4 * (1.0 - tint * 0.82));
1063
+ }
1064
+ return bg + 1.0 - exp(-light);
1065
+ }
1066
+
1067
+ float glow(float d, float w) {
1068
+ return exp(-d * d / (w * w)) + 0.22 * exp(-abs(d) / (w * 5.0)) + 0.035 * exp(-abs(d) / 0.05);
1069
+ }
1070
+
1071
+ float threadY(float x, float fi, float t, vec2 m) {
1072
+ float ph = fi * 1.7;
1073
+ float env = exp(-x * x * 1.1);
1074
+ float pluck = exp(-pow((x - m.x) / 0.35, 2.0));
1075
+ float amp = (0.05 + 0.09 * pow(sin(fi * 1.3 + 0.5), 2.0)) * uAmplitude + pluck * 0.05;
1076
+ return env * (amp * sin(x * (2.1 + fi * 0.09) + t * (1.0 + 0.12 * fi) + ph) + 0.028 * uAmplitude * sin(x * 4.3 - t * 0.7 + ph * 2.0))
1077
+ + m.y * 0.22 * pluck;
1078
+ }
1079
+
1080
+ void main() {
1081
+ vec2 frag = vec2(gl_FragCoord.x, uResolution.y - gl_FragCoord.y);
1082
+ float minRes = min(uResolution.x, uResolution.y);
1083
+ vec2 uv = (frag - 0.5 * uResolution) / minRes;
1084
+ vec2 m = (uPointer - 0.5 * uResolution) / minRes;
1085
+ float t = uTime * 0.35;
1086
+ float hx = 0.5 * uResolution.x / minRes;
1087
+
1088
+ float w = 0.9 * uDpr / minRes;
1089
+ float shift = 1.6 * uDpr / minRes;
1090
+ float e = 0.002;
1091
+ float fade = smoothstep(hx * 1.02, hx * 0.3, abs(uv.x));
1092
+
1093
+ vec3 light = vec3(0.0);
1094
+ for (int i = 0; i < COUNT; i++) {
1095
+ float fi = float(i);
1096
+ float y = threadY(uv.x, fi, t, m);
1097
+ float dy = (threadY(uv.x + e, fi, t, m) - y) / e;
1098
+ float s = (uv.y - y) / sqrt(1.0 + dy * dy);
1099
+
1100
+ float ph = fi * 1.7;
1101
+ float pulse = pow(0.5 + 0.5 * sin(uv.x * 1.6 - t * 1.4 + ph * 2.3), 6.0);
1102
+ float energy = (0.22 + 1.1 * pulse) * fade * (0.6 + 0.4 * sin(fi * 2.1 + 1.0) * sin(fi * 2.1 + 1.0));
1103
+ vec3 tint = mix(uColor, uHighlight, 0.5 + 0.5 * sin(uv.x * 1.2 + ph + t * 0.3));
1104
+
1105
+ vec3 g = vec3(glow(s - shift, w), glow(s, w), glow(s + shift, w));
1106
+ light += tint * g * energy;
1107
+ }
1108
+
1109
+ vec3 color = pow(clamp(shade(uBg, light), 0.0, 1.0), vec3(1.0 / 2.2));
1110
+ color += (hash(frag + fract(t)) - 0.5) / 255.0;
1111
+
1112
+ gl_FragColor = vec4(color, 1.0);
1113
+ }
1114
+ `;
1115
+ function Filaments({
1116
+ children,
1117
+ className,
1118
+ color = "#5b8cff",
1119
+ highlightColor = "#d46bff",
1120
+ amplitude = 1,
1121
+ speed = 1,
1122
+ interactive = true
1123
+ }) {
1124
+ const scheme = useColorScheme();
1125
+ const canvasRef = useShaderCanvas({
1126
+ fragmentShader: FRAGMENT_SHADER6,
1127
+ speed,
1128
+ interactive,
1129
+ onDraw: ({ gl, uniform, dpr }) => {
1130
+ gl.uniform3fv(uniform("uColor"), hexToLinearRgb(color));
1131
+ gl.uniform3fv(uniform("uHighlight"), hexToLinearRgb(highlightColor));
1132
+ gl.uniform3fv(uniform("uBg"), hexToLinearRgb(scheme === "light" ? "#f7f7fa" : "#06060a"));
1133
+ gl.uniform1f(uniform("uAmplitude"), Math.max(0, amplitude));
1134
+ gl.uniform1f(uniform("uDpr"), dpr);
1135
+ gl.uniform1f(uniform("uScheme"), scheme === "light" ? 1 : 0);
1136
+ }
1137
+ });
1138
+ return /* @__PURE__ */ jsxs(
1139
+ "div",
1140
+ {
1141
+ className: cn("elyri-filaments", className),
1142
+ "data-scheme": scheme,
1143
+ style: { "--elyri-filaments-color": color },
1144
+ children: [
1145
+ /* @__PURE__ */ jsx("canvas", { ref: canvasRef, className: "elyri-filaments__canvas", "aria-hidden": "true" }),
1146
+ children != null && /* @__PURE__ */ jsx("div", { className: "elyri-filaments__content", children })
1147
+ ]
1148
+ }
1149
+ );
1150
+ }
1151
+ var PATTERNS = ["wave", "ripple", "drift"];
1152
+ var FRAGMENT_SHADER7 = `
1153
+ precision highp float;
1154
+
1155
+ uniform vec2 uResolution;
1156
+ uniform float uTime;
1157
+ uniform vec2 uPointer;
1158
+ uniform float uScale;
1159
+ uniform vec3 uColor;
1160
+ uniform vec3 uHighlight;
1161
+ uniform vec3 uFront;
1162
+ uniform vec3 uFloor;
1163
+ uniform float uPattern;
1164
+ uniform float uScheme;
1165
+ uniform float uSamples;
1166
+
1167
+ const float PI = 3.14159265;
1168
+ const float HALF = 0.45;
1169
+ const float BEVEL = 0.055;
1170
+ const float FLOOR_Z = -0.45;
1171
+ const float TILT = 0.42;
1172
+ const vec3 KEY = vec3(-0.5, -0.42, 0.757);
1173
+
1174
+ float hash(vec2 p) {
1175
+ p = fract(p * vec2(123.34, 456.21));
1176
+ p += dot(p, p + 45.32);
1177
+ return fract(p.x * p.y);
1178
+ }
1179
+
1180
+ float noise(vec2 p) {
1181
+ vec2 i = floor(p);
1182
+ vec2 f = fract(p);
1183
+ f = f * f * (3.0 - 2.0 * f);
1184
+ return mix(
1185
+ mix(hash(i), hash(i + vec2(1.0, 0.0)), f.x),
1186
+ mix(hash(i + vec2(0.0, 1.0)), hash(i + vec2(1.0, 1.0)), f.x),
1187
+ f.y
1188
+ );
1189
+ }
1190
+
1191
+ // \u7ED5\u5355\u4F4D\u8F74 k \u65CB\u8F6C a \u5F27\u5EA6\uFF08Rodrigues\uFF09
1192
+ mat3 rotAxis(vec3 k, float a) {
1193
+ float s = sin(a);
1194
+ float c = cos(a);
1195
+ float oc = 1.0 - c;
1196
+ return mat3(
1197
+ oc * k.x * k.x + c, oc * k.x * k.y + k.z * s, oc * k.z * k.x - k.y * s,
1198
+ oc * k.x * k.y - k.z * s, oc * k.y * k.y + c, oc * k.y * k.z + k.x * s,
1199
+ oc * k.z * k.x + k.y * s, oc * k.y * k.z - k.x * s, oc * k.z * k.z + c
1200
+ );
1201
+ }
1202
+
1203
+ mat3 transposeM(mat3 m) {
1204
+ return mat3(
1205
+ m[0][0], m[1][0], m[2][0],
1206
+ m[0][1], m[1][1], m[2][1],
1207
+ m[0][2], m[1][2], m[2][2]
1208
+ );
1209
+ }
1210
+
1211
+ // 0\u21921 \u7684\u6B20\u963B\u5C3C\u5F39\u7C27\uFF1A\u8D77\u6B65\u96F6\u901F\u5EA6\uFF0C\u7EA6 7% \u7684\u56DE\u5F39\u540E\u7A33\u5B9A
1212
+ float spring(float x) {
1213
+ x = clamp(x, 0.0, 1.0);
1214
+ float k = exp(-6.0 * x) * (cos(7.0 * x) + 6.0 / 7.0 * sin(7.0 * x));
1215
+ return 1.0 - k * (1.0 - smoothstep(0.8, 1.0, x));
1216
+ }
1217
+
1218
+ // \u5355\u4E2A\u7ACB\u65B9\u4F53\u7684\u59FF\u6001\uFF1A\u6EDA\u52A8\u8F74\u4E0E\u89D2\u5EA6\u3001\u6EDA\u52A8\u65F6\u7684\u6536\u7F29\u4E0E\u8DC3\u8D77\u3001\u6307\u9488\u5904\u7684\u5347\u8D77
1219
+ void cubeAt(vec2 c, vec2 pw, out mat3 inv, out vec4 geo, out float open) {
1220
+ float h = hash(c);
1221
+ float h2 = hash(c + 17.31);
1222
+ vec3 axis;
1223
+ float f;
1224
+ if (uPattern < 0.5) {
1225
+ float a = 0.35 + 0.3 * sin(uTime * 0.045);
1226
+ axis = vec3(0.0, 1.0, 0.0);
1227
+ f = uTime * 0.15 - dot(c, vec2(cos(a), sin(a))) * 0.1 + h * 0.035;
1228
+ } else if (uPattern < 1.5) {
1229
+ axis = abs(c.x) >= abs(c.y) ? vec3(0.0, c.x < 0.0 ? -1.0 : 1.0, 0.0) : vec3(c.y < 0.0 ? 1.0 : -1.0, 0.0, 0.0);
1230
+ f = uTime * 0.14 - length(c) * 0.12 + h * 0.03;
1231
+ } else {
1232
+ axis = h2 < 0.5 ? vec3(0.0, 1.0, 0.0) : vec3(1.0, 0.0, 0.0);
1233
+ f = uTime * 0.07 + noise(c * 0.16 + vec2(uTime * 0.015, -uTime * 0.01)) * 2.2 + h * 0.05;
1234
+ }
1235
+ // \u6BCF\u4E2A\u5468\u671F\u6EDA\u4E24\u6B21\uFF1A\u5148\u628A\u5F3A\u8C03\u8272\u4FA7\u9762\u7FFB\u4E0A\u6765\uFF0C\u505C\u7559\u7247\u523B\uFF0C\u518D\u6EDA\u56DE\u9ED1\u66DC\u77F3\u9762
1236
+ float fl = floor(f);
1237
+ float fr = f - fl;
1238
+ float roll = spring(fr / 0.2) + spring((fr - 0.26) / 0.2);
1239
+ float ang = PI * 0.5 * (2.0 * mod(fl, 2.0) + roll);
1240
+ open = abs(sin(2.0 * ang));
1241
+
1242
+ // \u8F6C\u5230 45\xB0 \u65F6\u5BF9\u89D2\u7EBF\u6700\u957F\uFF0C\u6309\u6295\u5F71\u5BBD\u5EA6\u6536\u7F29\uFF0C\u4FDD\u8BC1\u4E0E\u90BB\u5C45\u59CB\u7EC8\u4E0D\u76F8\u4EA4
1243
+ float reach = HALF * (abs(cos(ang)) + abs(sin(ang)));
1244
+ float s = min(1.0, 0.49 / reach);
1245
+ vec2 toP = c - pw;
1246
+ float lift = 0.3 * exp(-dot(toP, toP) / 5.0);
1247
+
1248
+ inv = rotAxis(axis, -ang);
1249
+ geo = vec4(c, lift + (1.0 - s) * 0.7, HALF * s);
1250
+ }
1251
+
1252
+ // \u5C04\u7EBF\u4E0E\u4EE5\u539F\u70B9\u4E3A\u4E2D\u5FC3\u7684\u7ACB\u65B9\u4F53\u6C42\u4EA4\uFF0C\u8FD4\u56DE\u6700\u8FD1\u4EA4\u70B9\u8DDD\u79BB\uFF08\u672A\u547D\u4E2D\u4E3A -1\uFF09\u4E0E\u5C40\u90E8\u6CD5\u7EBF
1253
+ float boxHit(vec3 ro, vec3 rd, float rad, out vec3 n) {
1254
+ rd = mix(rd, vec3(1e-6), step(abs(rd), vec3(1e-6)));
1255
+ vec3 m = 1.0 / rd;
1256
+ vec3 k = abs(m) * rad;
1257
+ vec3 t1 = -m * ro - k;
1258
+ vec3 t2 = -m * ro + k;
1259
+ float tN = max(max(t1.x, t1.y), t1.z);
1260
+ float tF = min(min(t2.x, t2.y), t2.z);
1261
+ n = vec3(0.0);
1262
+ if (tN > tF || tF < 0.0) return -1.0;
1263
+ n = -sign(rd) * step(t1.yzx, t1.xyz) * step(t1.zxy, t1.xyz);
1264
+ return tN;
1265
+ }
1266
+
1267
+ // \u73AF\u5883\u5149\uFF1A\u53EA\u4FDD\u7559\u4E00\u5C42\u6781\u5F31\u7684\u5747\u5300\u8865\u5149\uFF0C\u4E0D\u518D\u6709\u626B\u8FC7\u753B\u9762\u7684\u706F\u5E26\u53CD\u5149
1268
+ vec3 envLight(vec3 R) {
1269
+ return vec3(0.008 + 0.02 * smoothstep(-0.1, 0.4, R.z));
1270
+ }
1271
+
1272
+ vec3 aces(vec3 x) {
1273
+ return clamp((x * (2.51 * x + 0.03)) / (x * (2.43 * x + 0.59) + 0.14), 0.0, 1.0);
1274
+ }
1275
+
1276
+ vec2 sampleOffset(int s) {
1277
+ if (uSamples < 1.5) return vec2(0.0);
1278
+ if (uSamples < 2.5) return s == 0 ? vec2(-0.25) : vec2(0.25);
1279
+ if (s == 0) return vec2(-0.125, -0.375);
1280
+ if (s == 1) return vec2(0.375, -0.125);
1281
+ if (s == 2) return vec2(0.125, 0.375);
1282
+ return vec2(-0.375, 0.125);
1283
+ }
1284
+
1285
+ void main() {
1286
+ vec2 frag = vec2(gl_FragCoord.x, uResolution.y - gl_FragCoord.y);
1287
+ vec2 center = 0.5 * uResolution;
1288
+ float halfW = 0.5 * max(uResolution.x, uResolution.y) / uScale;
1289
+
1290
+ // \u7565\u5E26\u4FEF\u89D2\u7684\u900F\u89C6\u76F8\u673A\uFF1A\u5C4F\u5E55\u5411\u4E0B\u5BF9\u5E94\u4E16\u754C +y\uFF0C\u76F8\u673A\u4ECE +y \u4E00\u4FA7\u659C\u770B\uFF0C\u80FD\u770B\u5230\u7ACB\u65B9\u4F53\u671D\u5411\u89C2\u8005\u7684\u4FA7\u9762
1291
+ float D = halfW * 2.0;
1292
+ vec3 ro = vec3(0.0, D * sin(TILT), D * cos(TILT));
1293
+ vec3 fw = -normalize(ro);
1294
+ vec3 dn = vec3(0.0, cos(TILT), -sin(TILT));
1295
+
1296
+ vec2 puv = (uPointer - center) / uScale;
1297
+ vec3 prd = normalize(fw * D + vec3(puv.x, 0.0, 0.0) + dn * puv.y);
1298
+ vec2 pw = (ro + prd * ((HALF - ro.z) / prd.z)).xy;
1299
+
1300
+ vec2 cuv = (frag - center) / uScale;
1301
+ vec3 crd = normalize(fw * D + vec3(cuv.x, 0.0, 0.0) + dn * cuv.y);
1302
+ vec2 base = floor((ro + crd * (-ro.z / crd.z)).xy + 0.5);
1303
+
1304
+ vec3 L = normalize(KEY);
1305
+ // \u4E3B\u5149\u6E90\u7684\u955C\u9762\u9AD8\u5149\u989C\u8272
1306
+ vec3 specCol = mix(vec3(1.0), uHighlight, 0.35);
1307
+ float amb = mix(0.03, 0.45, uScheme);
1308
+
1309
+ // \u5468\u56F4 3\xD73 \u4E2A\u7ACB\u65B9\u4F53\u7684\u59FF\u6001\u53EA\u7B97\u4E00\u6B21\uFF0C\u4F9B\u6240\u6709\u91C7\u6837\u4E0E\u9634\u5F71\u590D\u7528
1310
+ mat3 invs[9];
1311
+ vec4 geos[9];
1312
+ float opens[9];
1313
+ float seeds[9];
1314
+ vec3 localL[9];
1315
+ for (int k = 0; k < 9; k++) {
1316
+ int row = k / 3;
1317
+ vec2 c = base + vec2(float(k - row * 3) - 1.0, float(row) - 1.0);
1318
+ mat3 inv;
1319
+ vec4 geo;
1320
+ float open;
1321
+ cubeAt(c, pw, inv, geo, open);
1322
+ invs[k] = inv;
1323
+ geos[k] = geo;
1324
+ opens[k] = open;
1325
+ seeds[k] = hash(c + 5.17);
1326
+ // \u5149\u6E90\u65B9\u5411\u662F\u5E38\u91CF\uFF0C\u63D0\u524D\u8F6C\u5230\u5404\u7ACB\u65B9\u4F53\u5C40\u90E8\u7A7A\u95F4\uFF0C\u7701\u53BB\u6BCF\u6B21\u91C7\u6837\u91CD\u590D\u7684\u77E9\u9635\u4E58\u6CD5
1327
+ localL[k] = inv * L;
1328
+ }
1329
+
1330
+ vec3 acc = vec3(0.0);
1331
+ for (int s = 0; s < 4; s++) {
1332
+ if (float(s) >= uSamples) break;
1333
+ vec2 uv = (frag + sampleOffset(s) - center) / uScale;
1334
+ vec3 rd = normalize(fw * D + vec3(uv.x, 0.0, 0.0) + dn * uv.y);
1335
+
1336
+ float tMin = 1e9;
1337
+ int hitK = -1;
1338
+ vec3 hitN = vec3(0.0);
1339
+ vec3 hitP = vec3(0.0);
1340
+ mat3 hitInv = mat3(1.0);
1341
+ float hitSize = HALF;
1342
+ float hitSeed = 0.0;
1343
+ for (int k = 0; k < 9; k++) {
1344
+ vec3 lro = invs[k] * (ro - geos[k].xyz);
1345
+ vec3 lrd = invs[k] * rd;
1346
+ vec3 n;
1347
+ float t = boxHit(lro, lrd, geos[k].w, n);
1348
+ if (t > 0.0 && t < tMin) {
1349
+ tMin = t;
1350
+ hitK = k;
1351
+ hitN = n;
1352
+ hitP = lro + lrd * t;
1353
+ hitInv = invs[k];
1354
+ hitSize = geos[k].w;
1355
+ hitSeed = seeds[k];
1356
+ }
1357
+ }
1358
+
1359
+ bool hit = hitK >= 0;
1360
+ float tFloor = (FLOOR_Z - ro.z) / rd.z;
1361
+ float tHit = hit ? tMin : tFloor;
1362
+ vec3 wp = ro + rd * tHit;
1363
+
1364
+ // \u7ACB\u65B9\u4F53\u6CD5\u7EBF\uFF1A\u68F1\u8FB9\u4E00\u5708\u5411\u5916\u503E\u659C\uFF0C\u5F62\u6210\u5706\u6DA6\u7684\u5012\u89D2\uFF1B\u6BCF\u5757\u9876\u9762\u6781\u8F7B\u5FAE\u9519\u5F00\uFF0C\u50CF\u624B\u5DE5\u94FA\u8D34
1365
+ vec3 n = vec3(0.0, 0.0, 1.0);
1366
+ float bevel = 0.0;
1367
+ if (hit) {
1368
+ vec3 b = (1.0 - smoothstep(0.0, BEVEL, hitSize - abs(hitP))) * (1.0 - abs(hitN));
1369
+ bevel = max(max(b.x, b.y), b.z);
1370
+ vec3 jitter = (vec3(hitSeed, fract(hitSeed * 7.13), fract(hitSeed * 3.71)) - 0.5) * 0.04 * (1.0 - abs(hitN));
1371
+ n = normalize(transposeM(hitInv) * normalize(hitN + sign(hitP) * b * 1.2 + jitter));
1372
+ }
1373
+
1374
+ // \u4E3B\u5149\u6E90\u7684\u6295\u5F71\uFF1A\u5411\u5149\u6E90\u65B9\u5411\u68C0\u6D4B\u5176\u4F59\u7ACB\u65B9\u4F53\uFF0C\u547D\u4E2D\u5373\u63D0\u524D\u9000\u51FA
1375
+ float shadow = 1.0;
1376
+ vec3 sp = wp + n * 0.003;
1377
+ for (int k = 0; k < 9; k++) {
1378
+ if (k == hitK) continue;
1379
+ vec3 tmp;
1380
+ float ts = boxHit(invs[k] * (sp - geos[k].xyz), localL[k], geos[k].w, tmp);
1381
+ if (ts > 0.0) {
1382
+ shadow = 0.0;
1383
+ break;
1384
+ }
1385
+ }
1386
+
1387
+ vec3 V = -rd;
1388
+ float diffK = max(dot(n, L), 0.0) * shadow;
1389
+ float NdHk = max(dot(n, normalize(L + V)), 0.0);
1390
+
1391
+ vec3 color;
1392
+ if (hit) {
1393
+ float fres = pow(1.0 - max(dot(n, V), 0.0), 5.0);
1394
+ vec3 env = envLight(reflect(rd, n));
1395
+
1396
+ vec3 edge = uHighlight * (0.03 + 0.4 * diffK)
1397
+ + mix(uHighlight, vec3(1.0), 0.3 + 0.7 * fres) * env * 1.4
1398
+ + specCol * pow(NdHk, 48.0) * shadow * 0.8;
1399
+
1400
+ vec3 face;
1401
+ if (abs(hitN.z) > 0.5) {
1402
+ face = uFront * (amb + 0.55 * diffK)
1403
+ + env * (0.12 + 0.88 * fres) * mix(1.0, 0.45, uScheme)
1404
+ + specCol * pow(NdHk, 900.0) * shadow * 0.4;
1405
+ } else {
1406
+ face = uColor * (amb * 0.6 + 0.02 + 0.7 * diffK)
1407
+ + mix(uColor, vec3(1.0), fres) * env * 0.8
1408
+ + mix(uColor, specCol, 0.5) * (pow(NdHk, 40.0) * shadow * 0.5);
1409
+ }
1410
+ color = mix(face, edge, bevel * 0.5);
1411
+ } else {
1412
+ // \u69FD\u5E95\uFF1A\u7D27\u8D34\u7ACB\u65B9\u4F53\u5904\u538B\u6697\uFF0C\u6EDA\u52A8\u4E2D\u7684\u7ACB\u65B9\u4F53\u4E0B\u65B9\u900F\u51FA\u5F3A\u8C03\u8272\u8F89\u5149
1413
+ float ao = 1.0;
1414
+ float glow = 0.0;
1415
+ for (int k = 0; k < 9; k++) {
1416
+ vec2 dv = wp.xy - geos[k].xy;
1417
+ float d2 = dot(dv, dv);
1418
+ // \u8D85\u8FC7\u6B64\u8DDD\u79BB ao \u5DF2\u9971\u548C\u3001\u8F89\u5149\u5DF2\u8870\u51CF\u5230\u770B\u4E0D\u89C1\uFF0C\u8DF3\u8FC7\u4EE5\u7701\u7B97\u529B
1419
+ if (d2 > 1.5) continue;
1420
+ vec2 q = abs(dv) - geos[k].w;
1421
+ ao *= mix(0.2, 1.0, smoothstep(0.0, 0.3, length(max(q, 0.0))));
1422
+ glow += opens[k] * exp(-d2 / 0.18);
1423
+ }
1424
+ color = uFloor * (amb + 0.45 * diffK) * ao
1425
+ + uColor * (glow * 0.5 * (0.4 + 0.6 * ao));
1426
+ }
1427
+
1428
+ // \u8FDC\u5904\u6E10\u9690\u8FDB\u96FE\u91CC\uFF0C\u62C9\u5F00\u7EB5\u6DF1
1429
+ float fog = smoothstep(D * 0.95, D * 1.5, tHit);
1430
+ color = mix(color, uFloor * mix(0.4, 0.9, uScheme), fog * 0.8);
1431
+
1432
+ acc += aces(color * mix(1.3, 1.05, uScheme));
1433
+ }
1434
+ vec3 color = acc / uSamples;
1435
+
1436
+ vec2 vc = frag / uResolution - 0.5;
1437
+ color *= 1.0 - dot(vc, vc) * mix(1.1, 0.3, uScheme);
1438
+ color = pow(color, vec3(1.0 / 2.2));
1439
+ color += (hash(frag + fract(uTime)) - 0.5) / 255.0;
1440
+
1441
+ gl_FragColor = vec4(color, 1.0);
1442
+ }
1443
+ `;
1444
+ function FlipTiles({
1445
+ children,
1446
+ className,
1447
+ color = "#4f6bff",
1448
+ highlightColor = "#b8c4ff",
1449
+ tileSize = 72,
1450
+ speed = 1,
1451
+ pattern = "wave",
1452
+ interactive = true
1453
+ }) {
1454
+ const scheme = useColorScheme();
1455
+ const canvasRef = useShaderCanvas({
1456
+ fragmentShader: FRAGMENT_SHADER7,
1457
+ speed,
1458
+ interactive,
1459
+ // 每像素要跑 9 次立方体求交与阴影射线,属于重填充率着色器:内部按 1.5× 渲染,
1460
+ // 视觉上几乎无损,但相比 2× 能省下约 44% 的像素量
1461
+ maxDpr: 1.5,
1462
+ onDraw: ({ gl, uniform, dpr }) => {
1463
+ const light = scheme === "light";
1464
+ gl.uniform1f(uniform("uScale"), Math.max(24, tileSize) * dpr);
1465
+ gl.uniform3fv(uniform("uColor"), hexToLinearRgb(color));
1466
+ gl.uniform3fv(uniform("uHighlight"), hexToLinearRgb(highlightColor));
1467
+ gl.uniform3fv(uniform("uFront"), hexToLinearRgb(light ? "#ececf1" : "#0c0c12"));
1468
+ gl.uniform3fv(uniform("uFloor"), hexToLinearRgb(light ? "#c6c6d0" : "#040406"));
1469
+ gl.uniform1f(uniform("uPattern"), Math.max(0, PATTERNS.indexOf(pattern)));
1470
+ gl.uniform1f(uniform("uScheme"), light ? 1 : 0);
1471
+ gl.uniform1f(uniform("uSamples"), 2);
1472
+ }
1473
+ });
1474
+ return /* @__PURE__ */ jsxs(
1475
+ "div",
1476
+ {
1477
+ className: cn("elyri-flip-tiles", className),
1478
+ "data-scheme": scheme,
1479
+ style: { "--elyri-flip-tiles-color": color },
1480
+ children: [
1481
+ /* @__PURE__ */ jsx("canvas", { ref: canvasRef, className: "elyri-flip-tiles__canvas", "aria-hidden": "true" }),
1482
+ children != null && /* @__PURE__ */ jsx("div", { className: "elyri-flip-tiles__content", children })
1483
+ ]
1484
+ }
1485
+ );
1486
+ }
1487
+ var DEFAULT_COLORS2 = ["#8fb4ff", "#e7c6ff"];
1488
+ var FRAGMENT_SHADER8 = `
1489
+ precision highp float;
1490
+
1491
+ uniform vec2 uResolution;
1492
+ uniform float uTime;
1493
+ uniform vec2 uPointer;
1494
+ uniform vec3 uColor1;
1495
+ uniform vec3 uColor2;
1496
+ uniform float uSize;
1497
+ uniform float uInteractive;
1498
+ uniform vec3 uBg;
1499
+ uniform float uScheme;
1500
+
1501
+ float hash(vec2 p) {
1502
+ p = fract(p * vec2(123.34, 456.21));
1503
+ p += dot(p, p + 45.32);
1504
+ return fract(p.x * p.y);
1505
+ }
1506
+
1507
+ // \u6DF1\u8272\u4E0B\u5149\u7EBF\u53E0\u52A0\u53D1\u4EAE\uFF1B\u6D45\u8272\u4E0B\u6539\u4E3A\u6309\u8272\u76F8\u5438\u6536\u5E95\u8272\uFF0C\u50CF\u58A8\u8272\u7EC6\u7EBF\u843D\u5728\u7EB8\u9762\u4E0A
1508
+ vec3 shade(vec3 bg, vec3 light) {
1509
+ if (uScheme > 0.5) {
1510
+ float amount = max(max(light.r, light.g), light.b);
1511
+ vec3 tint = light / max(amount, 1e-4);
1512
+ return bg * exp(-amount * 1.6 * (1.0 - tint * 0.8));
1513
+ }
1514
+ return bg + 1.0 - exp(-light * 1.4);
1515
+ }
1516
+
1517
+ void main() {
1518
+ vec2 res = uResolution;
1519
+ vec2 p = (gl_FragCoord.xy - 0.5 * res) / res.y;
1520
+ vec2 pointer = (vec2(uPointer.x, res.y - uPointer.y) - 0.5 * res) / res.y;
1521
+ p -= pointer * 0.06 * uInteractive;
1522
+ float t = uTime;
1523
+
1524
+ float r = length(p);
1525
+ float a = atan(p.y, p.x);
1526
+
1527
+ float radius = 0.3 * uSize;
1528
+ radius += 0.010 * sin(a * 3.0 + t * 0.5) + 0.006 * sin(a * 5.0 - t * 0.8);
1529
+ float d = abs(r - radius);
1530
+
1531
+ float line = exp(-d * d * 60000.0);
1532
+ float glow = exp(-d * 22.0) * 0.28 + exp(-d * 6.0) * 0.05;
1533
+
1534
+ // \u4E24\u675F\u9AD8\u5149\u6CBF\u5706\u5468\u4EE5\u4E0D\u540C\u901F\u5EA6\u6D41\u52A8
1535
+ float f1 = pow(0.5 + 0.5 * cos(a - t * 0.45), 6.0);
1536
+ float f2 = pow(0.5 + 0.5 * cos(a + 2.4 + t * 0.28), 10.0);
1537
+ float flow = 0.12 + f1 + 0.6 * f2;
1538
+
1539
+ vec3 tint = mix(uColor1, uColor2, 0.5 + 0.5 * sin(a + t * 0.2));
1540
+ vec3 light = tint * (line * 1.1 + glow) * flow;
1541
+ // \u5708\u5185\u6781\u6DE1\u7684\u96FE\u5149
1542
+ light += uColor1 * 0.015 * exp(-r * r / (radius * radius) * 1.5);
1543
+
1544
+ vec3 color = pow(clamp(shade(uBg, light), 0.0, 1.0), vec3(1.0 / 2.2));
1545
+ color += (hash(gl_FragCoord.xy + fract(t)) - 0.5) / 255.0;
1546
+
1547
+ gl_FragColor = vec4(color, 1.0);
1548
+ }
1549
+ `;
1550
+ function Halo({
1551
+ children,
1552
+ className,
1553
+ colors = DEFAULT_COLORS2,
1554
+ size = 1,
1555
+ speed = 1,
1556
+ interactive = true
1557
+ }) {
1558
+ const [first = DEFAULT_COLORS2[0], second = DEFAULT_COLORS2[1]] = colors;
1559
+ const scheme = useColorScheme();
1560
+ const canvasRef = useShaderCanvas({
1561
+ fragmentShader: FRAGMENT_SHADER8,
1562
+ speed,
1563
+ interactive,
1564
+ onDraw: ({ gl, uniform }) => {
1565
+ gl.uniform3fv(uniform("uColor1"), hexToLinearRgb(first));
1566
+ gl.uniform3fv(uniform("uColor2"), hexToLinearRgb(second));
1567
+ gl.uniform1f(uniform("uSize"), Math.max(0.1, size));
1568
+ gl.uniform1f(uniform("uInteractive"), interactive ? 1 : 0);
1569
+ gl.uniform3fv(uniform("uBg"), hexToLinearRgb(scheme === "light" ? "#f8f8fa" : "#010102"));
1570
+ gl.uniform1f(uniform("uScheme"), scheme === "light" ? 1 : 0);
1571
+ }
1572
+ });
1573
+ return /* @__PURE__ */ jsxs(
1574
+ "div",
1575
+ {
1576
+ className: cn("elyri-halo", className),
1577
+ "data-scheme": scheme,
1578
+ style: { "--elyri-halo-color": first },
1579
+ children: [
1580
+ /* @__PURE__ */ jsx("canvas", { ref: canvasRef, className: "elyri-halo__canvas", "aria-hidden": "true" }),
1581
+ children != null && /* @__PURE__ */ jsx("div", { className: "elyri-halo__content", children })
1582
+ ]
1583
+ }
1584
+ );
1585
+ }
1586
+ var DEFAULT_COLORS3 = ["#6e6af0", "#f472b6", "#fbbf77", "#38bdf8"];
1587
+ var FRAGMENT_SHADER9 = `
1588
+ precision highp float;
1589
+
1590
+ uniform vec2 uResolution;
1591
+ uniform float uTime;
1592
+ uniform vec2 uPointer;
1593
+ uniform vec3 uColor0;
1594
+ uniform vec3 uColor1;
1595
+ uniform vec3 uColor2;
1596
+ uniform vec3 uColor3;
1597
+ uniform vec3 uBase;
1598
+ uniform float uGrain;
1599
+
1600
+ float hash(vec2 p) {
1601
+ vec3 p3 = fract(vec3(p.xyx) * 0.1031);
1602
+ p3 += dot(p3, p3.yzx + 33.33);
1603
+ return fract((p3.x + p3.y) * p3.z);
1604
+ }
1605
+
1606
+ vec3 toOklab(vec3 c) {
1607
+ float l = 0.4122214708 * c.r + 0.5363325363 * c.g + 0.0514459929 * c.b;
1608
+ float m = 0.2119034982 * c.r + 0.6806995451 * c.g + 0.1073969566 * c.b;
1609
+ float s = 0.0883024619 * c.r + 0.2817188376 * c.g + 0.6299787005 * c.b;
1610
+ l = pow(max(l, 0.0), 1.0 / 3.0);
1611
+ m = pow(max(m, 0.0), 1.0 / 3.0);
1612
+ s = pow(max(s, 0.0), 1.0 / 3.0);
1613
+ return vec3(
1614
+ 0.2104542553 * l + 0.7936177850 * m - 0.0040720468 * s,
1615
+ 1.9779984951 * l - 2.4285922050 * m + 0.4505937099 * s,
1616
+ 0.0259040371 * l + 0.7827717662 * m - 0.8086757660 * s
1617
+ );
1618
+ }
1619
+
1620
+ vec3 fromOklab(vec3 c) {
1621
+ float l = c.x + 0.3963377774 * c.y + 0.2158037573 * c.z;
1622
+ float m = c.x - 0.1055613458 * c.y - 0.0638541728 * c.z;
1623
+ float s = c.x - 0.0894841775 * c.y - 1.2914855480 * c.z;
1624
+ l = l * l * l;
1625
+ m = m * m * m;
1626
+ s = s * s * s;
1627
+ return vec3(
1628
+ 4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s,
1629
+ -1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s,
1630
+ -0.0041960863 * l - 0.7034186147 * m + 1.7076147010 * s
1631
+ );
1632
+ }
1633
+
1634
+ float noise(vec2 p) {
1635
+ vec2 i = floor(p);
1636
+ vec2 f = fract(p);
1637
+ vec2 u = f * f * f * (f * (f * 6.0 - 15.0) + 10.0);
1638
+ return mix(mix(hash(i), hash(i + vec2(1.0, 0.0)), u.x), mix(hash(i + vec2(0.0, 1.0)), hash(i + 1.0), u.x), u.y);
1639
+ }
1640
+
1641
+ float fbm(vec2 p) {
1642
+ float v = 0.0;
1643
+ float a = 0.5;
1644
+ mat2 rot = mat2(1.6, 1.2, -1.2, 1.6);
1645
+ for (int i = 0; i < 4; i++) {
1646
+ v += a * noise(p);
1647
+ p = rot * p + vec2(11.3, 7.9);
1648
+ a *= 0.5;
1649
+ }
1650
+ return v;
1651
+ }
1652
+
1653
+ float blob(vec2 uv, vec2 center) {
1654
+ vec2 d = uv - center;
1655
+ return exp(-dot(d, d) * 3.2);
1656
+ }
1657
+
1658
+ void main() {
1659
+ vec2 frag = vec2(gl_FragCoord.x, uResolution.y - gl_FragCoord.y);
1660
+ float minRes = min(uResolution.x, uResolution.y);
1661
+ vec2 half_ = 0.5 * uResolution / minRes;
1662
+ vec2 uv = (frag - 0.5 * uResolution) / minRes;
1663
+ vec2 pointer = (uPointer - 0.5 * uResolution) / minRes;
1664
+ float t = uTime * 0.1;
1665
+
1666
+ // \u6307\u9488\u6F29\u6DA1\uFF1A\u7ED5\u6307\u9488\u65CB\u8F6C\u91C7\u6837\u5750\u6807\uFF0C\u8D8A\u9760\u8FD1\u8F6C\u5F97\u8D8A\u591A\uFF0C\u8272\u5F69\u88AB\u5377\u8FDB\u53BB
1667
+ vec2 dm = uv - pointer;
1668
+ float swirl = exp(-dot(dm, dm) / 0.07);
1669
+ float angle = swirl * 1.1;
1670
+ uv = pointer + mat2(cos(angle), sin(angle), -sin(angle), cos(angle)) * dm * (1.0 - swirl * 0.12);
1671
+
1672
+ // \u4E24\u7EA7\u57DF\u626D\u66F2\u7684\u6D41\u573A\uFF1Aq \u7F13\u6162\u6F02\u79FB\uFF0Cr \u5728 q \u7684\u57FA\u7840\u4E0A\u518D\u626D\u4E00\u6B21\uFF0C\u5F97\u5230\u6DB2\u4F53\u822C\u7684\u5377\u6D41
1673
+ vec2 p = uv * 1.5;
1674
+ vec2 q = vec2(fbm(p + vec2(0.0, t)), fbm(p + vec2(5.2, 1.3) - t * 0.8));
1675
+ vec2 r = vec2(fbm(p + 2.6 * q + vec2(1.7, 9.2) + t * 1.2), fbm(p + 2.6 * q + vec2(8.3, 2.8) - t));
1676
+ vec2 w = uv + (r - 0.5) * 0.9 + (q - 0.5) * 0.3;
1677
+
1678
+ float tb = uTime * 0.12;
1679
+ vec2 p0 = half_ * vec2(-0.55 + 0.35 * sin(tb * 0.63), -0.45 + 0.35 * cos(tb * 0.51));
1680
+ vec2 p1 = half_ * vec2(0.55 + 0.30 * cos(tb * 0.57 + 1.0), -0.35 + 0.40 * sin(tb * 0.71 + 2.0));
1681
+ vec2 p2 = half_ * vec2(0.45 + 0.35 * sin(tb * 0.49 + 3.0), 0.50 + 0.30 * cos(tb * 0.61 + 1.5));
1682
+ vec2 p3 = half_ * vec2(-0.50 + 0.35 * cos(tb * 0.67 + 4.0), 0.45 + 0.35 * sin(tb * 0.53 + 0.5));
1683
+
1684
+ float w0 = blob(w, p0);
1685
+ float w1 = blob(w, p1);
1686
+ float w2 = blob(w, p2);
1687
+ float w3 = blob(w, p3);
1688
+ float wb = 0.05;
1689
+
1690
+ vec3 lab = (toOklab(uColor0) * w0 + toOklab(uColor1) * w1 + toOklab(uColor2) * w2 + toOklab(uColor3) * w3 + toOklab(uBase) * wb)
1691
+ / (w0 + w1 + w2 + w3 + wb);
1692
+
1693
+ // \u7F0E\u9762\u6D41\u5149\uFF1A\u6CBF\u6D41\u573A\u7B49\u503C\u7EBF\u7684\u67D4\u548C\u4EAE\u5E26\u968F\u65F6\u95F4\u6ED1\u8FC7\uFF0C\u8C37\u5E95\u5FAE\u6697\uFF0C\u5E26\u51FA\u4E1D\u7EF8\u822C\u7684\u5C42\u6B21
1694
+ float band = sin((r.x * 1.4 - r.y + q.x * 0.6) * 11.0 + t * 3.0);
1695
+ float gloss = smoothstep(0.15, 1.0, band);
1696
+ lab.x += gloss * gloss * 0.06 - (1.0 - smoothstep(-1.0, 0.1, band)) * 0.025;
1697
+ lab.x += swirl * 0.025;
1698
+ // \u9AD8\u5149\u5904\u7565\u964D\u9971\u548C\uFF0C\u50CF\u5149\u6253\u5728\u7EC7\u7269\u8868\u9762
1699
+ lab.yz *= 1.0 - gloss * gloss * 0.12;
1700
+
1701
+ vec3 color = pow(clamp(fromOklab(lab), 0.0, 1.0), vec3(1.0 / 2.2));
1702
+
1703
+ // \u80F6\u7247\u9897\u7C92 + \u6296\u52A8\uFF0C\u6D88\u9664\u5927\u9762\u79EF\u6E10\u53D8\u7684\u8272\u5E26
1704
+ float n = hash(frag + fract(uTime * 7.31) * 113.0) - 0.5;
1705
+ color += n * (uGrain * 0.07 + 1.0 / 255.0);
1706
+
1707
+ gl_FragColor = vec4(color, 1.0);
1708
+ }
1709
+ `;
1710
+ function MeshGradient({
1711
+ children,
1712
+ className,
1713
+ style,
1714
+ colors = DEFAULT_COLORS3,
1715
+ grain = 0.35,
1716
+ speed = 1,
1717
+ interactive = true
1718
+ }) {
1719
+ const scheme = useColorScheme();
1720
+ const [c0 = DEFAULT_COLORS3[0], c1 = DEFAULT_COLORS3[1], c2 = DEFAULT_COLORS3[2], c3 = DEFAULT_COLORS3[3]] = colors;
1721
+ const canvasRef = useShaderCanvas({
1722
+ fragmentShader: FRAGMENT_SHADER9,
1723
+ speed,
1724
+ interactive,
1725
+ onDraw: ({ gl, uniform }) => {
1726
+ gl.uniform3fv(uniform("uColor0"), hexToLinearRgb(c0));
1727
+ gl.uniform3fv(uniform("uColor1"), hexToLinearRgb(c1));
1728
+ gl.uniform3fv(uniform("uColor2"), hexToLinearRgb(c2));
1729
+ gl.uniform3fv(uniform("uColor3"), hexToLinearRgb(c3));
1730
+ gl.uniform3fv(uniform("uBase"), hexToLinearRgb(scheme === "light" ? "#f7f7fa" : "#0b0b12"));
1731
+ gl.uniform1f(uniform("uGrain"), Math.max(0, grain));
1732
+ }
1733
+ });
1734
+ return /* @__PURE__ */ jsxs(
1735
+ "div",
1736
+ {
1737
+ className: cn("elyri-mesh-gradient", className),
1738
+ "data-scheme": scheme,
1739
+ style: {
1740
+ "--elyri-mesh-gradient-c0": c0,
1741
+ "--elyri-mesh-gradient-c1": c1,
1742
+ "--elyri-mesh-gradient-c2": c2,
1743
+ "--elyri-mesh-gradient-c3": c3,
1744
+ ...style
1745
+ },
1746
+ children: [
1747
+ /* @__PURE__ */ jsx("canvas", { ref: canvasRef, className: "elyri-mesh-gradient__canvas", "aria-hidden": "true" }),
1748
+ children != null && /* @__PURE__ */ jsx("div", { className: "elyri-mesh-gradient__content", children })
1749
+ ]
1750
+ }
1751
+ );
1752
+ }
1753
+ var FRAGMENT_SHADER10 = `
1754
+ precision highp float;
1755
+
1756
+ uniform vec2 uResolution;
1757
+ uniform float uTime;
1758
+ uniform vec2 uPointer;
1759
+ uniform vec3 uColor;
1760
+ uniform vec3 uHighlight;
1761
+ uniform float uCell;
1762
+ uniform float uFollow;
1763
+ uniform sampler2D uVideo;
1764
+ uniform float uHasVideo;
1765
+ uniform vec2 uVideoSize;
1766
+ uniform float uScheme;
1767
+ uniform float uSeam;
1768
+
1769
+ float hash(vec2 p) {
1770
+ p = fract(p * vec2(123.34, 456.21));
1771
+ p += dot(p, p + 45.32);
1772
+ return fract(p.x * p.y);
1773
+ }
1774
+
1775
+ float noise(vec2 p) {
1776
+ vec2 i = floor(p);
1777
+ vec2 f = fract(p);
1778
+ vec2 u = f * f * (3.0 - 2.0 * f);
1779
+ return mix(
1780
+ mix(hash(i), hash(i + vec2(1.0, 0.0)), u.x),
1781
+ mix(hash(i + vec2(0.0, 1.0)), hash(i + vec2(1.0, 1.0)), u.x),
1782
+ u.y
1783
+ );
1784
+ }
1785
+
1786
+ float fbm(vec2 p) {
1787
+ float v = 0.0;
1788
+ float a = 0.5;
1789
+ mat2 m = mat2(1.6, 1.2, -1.2, 1.6);
1790
+ for (int i = 0; i < 5; i++) {
1791
+ v += a * noise(p);
1792
+ p = m * p;
1793
+ a *= 0.5;
1794
+ }
1795
+ return v;
1796
+ }
1797
+
1798
+ // \u706B\u7130\u70ED\u5EA6\u573A\uFF1A\u57DF\u626D\u66F2\u7684\u566A\u58F0\u7F13\u6162\u4E0A\u5347\uFF0C\u9760\u8FD1\u7126\u70B9\u5904\u66F4\u4EAE
1799
+ float heat(vec2 p, vec2 focus, float t) {
1800
+ vec2 q = vec2(
1801
+ fbm(p * 1.4 + vec2(0.0, t * 0.18)),
1802
+ fbm(p * 1.4 + vec2(5.2, 1.3) - t * 0.12)
1803
+ );
1804
+ float n = fbm(p * 2.0 + q * 1.7 + vec2(0.0, t * 0.3));
1805
+ vec2 d = p - focus;
1806
+ float glow = exp(-dot(d, d) * 1.6);
1807
+ return n * n * 1.8 * (0.3 + 0.9 * glow);
1808
+ }
1809
+
1810
+ vec3 aces(vec3 x) {
1811
+ return clamp((x * (2.51 * x + 0.03)) / (x * (2.43 * x + 0.59) + 0.14), 0.0, 1.0);
1812
+ }
1813
+
1814
+ void main() {
1815
+ vec2 frag = vec2(gl_FragCoord.x, uResolution.y - gl_FragCoord.y);
1816
+ float cell = max(uCell, 2.0);
1817
+ float minRes = min(uResolution.x, uResolution.y);
1818
+ float t = uTime;
1819
+
1820
+ // \u4EE5\u706F\u73E0\u4E2D\u5FC3\u91C7\u6837\uFF0C\u6574\u9897\u706F\u73E0\u540C\u8272
1821
+ vec2 id = floor(frag / cell);
1822
+ vec2 center = (id + 0.5) * cell;
1823
+ vec2 vc = frag / uResolution - 0.5;
1824
+ float grain = (0.86 + 0.2 * hash(id)) * (0.95 + 0.1 * hash(id + floor(t * 10.0) * vec2(7.13, 3.71)));
1825
+ vec3 color;
1826
+
1827
+ if (uHasVideo > 0.5) {
1828
+ // cover \u94FA\u6EE1\uFF1A\u6309\u5BBD\u9AD8\u6BD4\u88C1\u5207\u89C6\u9891
1829
+ vec2 uv = center / uResolution - 0.5;
1830
+ float screenAspect = uResolution.x / uResolution.y;
1831
+ float videoAspect = uVideoSize.x / max(uVideoSize.y, 1.0);
1832
+ if (screenAspect > videoAspect) uv.y *= videoAspect / screenAspect;
1833
+ else uv.x *= screenAspect / videoAspect;
1834
+ color = texture2D(uVideo, uv + 0.5).rgb * grain * 1.1;
1835
+ // \u6DF1\u8272\u538B\u6697\u56DB\u89D2\uFF0C\u6D45\u8272\u63D0\u4EAE\u56DB\u89D2
1836
+ color = mix(color * (1.0 - dot(vc, vc) * 0.6), mix(color, vec3(1.0), dot(vc, vc) * 0.8), uScheme);
1837
+ } else {
1838
+ vec2 p = (center - 0.5 * uResolution) / minRes;
1839
+ vec2 focus = mix(vec2(0.0, 0.1), (uPointer - 0.5 * uResolution) / minRes, 0.5 * uFollow);
1840
+ float h = heat(p, focus, t) * grain;
1841
+ color = uColor * h * 1.3 + uHighlight * pow(max(h - 0.55, 0.0), 2.0) * 2.2;
1842
+ color += uColor * 0.012;
1843
+ color *= 1.0 - dot(vc, vc) * 0.9;
1844
+ color = pow(aces(color), vec3(1.0 / 2.2));
1845
+ if (uScheme > 0.5) {
1846
+ // \u6D45\u8272\uFF1A\u4EAE\u6697\u955C\u50CF\u5E76\u4FDD\u7559\u8272\u76F8 \u2014\u2014 \u6DF1\u5E95\u4EAE\u7130\u7FFB\u8F6C\u4E3A\u6D45\u5E95\u6DF1\u7130
1847
+ float lum = dot(color, vec3(0.2126, 0.7152, 0.0722));
1848
+ color = clamp(color + (1.0 - 2.0 * lum), 0.0, 1.0);
1849
+ }
1850
+ }
1851
+
1852
+ // \u8272\u9636\u91CF\u5316\uFF0C\u6A21\u62DF LED \u9A71\u52A8\u7684\u6709\u9650\u7070\u5EA6
1853
+ color = floor(color * 14.0 + hash(id + 3.1)) / 14.0;
1854
+
1855
+ // \u706F\u73E0\u5F62\u72B6\uFF1A\u65B9\u5F62\u53D1\u5149\u70B9\uFF1B\u95F4\u9699\u6DF1\u8272\u65F6\u4FDD\u7559\u5C11\u8BB8\u6F0F\u5149\uFF0C\u6D45\u8272\u65F6\u4E3A\u6D45\u7070\u5E95\u677F
1856
+ vec2 f = fract(frag / cell) - 0.5;
1857
+ float d = max(abs(f.x), abs(f.y));
1858
+ vec3 gap = mix(color * 0.2, mix(color, vec3(0.96), 0.7), uScheme);
1859
+ color = mix(gap, color, smoothstep(0.5, 0.28, d));
1860
+
1861
+ // \u62FC\u63A5\u5C4F\u63A5\u7F1D\uFF1A\u6BCF 16\xD710 \u9897\u706F\u73E0\u4E00\u5757\uFF0C\u63A5\u7F1D + \u7F1D\u4E0B\u65B9\u7684\u9634\u5F71
1862
+ if (uSeam > 0.0) {
1863
+ vec2 pp = mod(frag, vec2(16.0, 10.0) * cell);
1864
+ float seam = max(1.0 - smoothstep(uSeam - 1.0, uSeam + 1.0, pp.x), 1.0 - smoothstep(uSeam - 1.0, uSeam + 1.0, pp.y));
1865
+ float shade = max(1.0 - smoothstep(uSeam, uSeam + cell * 1.5, pp.x), 1.0 - smoothstep(uSeam, uSeam + cell * 1.5, pp.y));
1866
+ color *= 1.0 - mix(0.45, 0.15, uScheme) * shade;
1867
+ vec3 seamColor = mix(vec3(0.018, 0.016, 0.015), vec3(0.86, 0.855, 0.85), uScheme) + color * 0.06;
1868
+ color = mix(color, seamColor, seam);
1869
+ }
1870
+
1871
+ gl_FragColor = vec4(color, 1.0);
1872
+ }
1873
+ `;
1874
+ function PixelVortex({
1875
+ children,
1876
+ className,
1877
+ videoSrc,
1878
+ color = "#ff4d12",
1879
+ highlightColor = "#ffd9b0",
1880
+ cellSize = 4,
1881
+ seams = false,
1882
+ seamWidth = 5,
1883
+ speed = 1,
1884
+ interactive = true
1885
+ }) {
1886
+ const scheme = useColorScheme();
1887
+ const [video, setVideo] = useState(null);
1888
+ const [videoReady, setVideoReady] = useState(false);
1889
+ const textureRef = useRef(null);
1890
+ useEffect(() => {
1891
+ if (!videoSrc) return;
1892
+ const el = document.createElement("video");
1893
+ el.crossOrigin = "anonymous";
1894
+ el.muted = true;
1895
+ el.loop = true;
1896
+ el.playsInline = true;
1897
+ el.preload = "auto";
1898
+ let disposed = false;
1899
+ const tryPlay = () => {
1900
+ if (disposed || document.hidden) return;
1901
+ el.play().catch(() => {
1902
+ });
1903
+ };
1904
+ const handleReady = () => {
1905
+ if (disposed) return;
1906
+ setVideo(el);
1907
+ setVideoReady(true);
1908
+ };
1909
+ const handleError = () => {
1910
+ if (disposed) return;
1911
+ setVideoReady(false);
1912
+ setVideo(null);
1913
+ };
1914
+ const handlePause = () => tryPlay();
1915
+ const handleVisibility = () => {
1916
+ if (document.hidden) el.pause();
1917
+ else tryPlay();
1918
+ };
1919
+ el.addEventListener("loadeddata", handleReady);
1920
+ el.addEventListener("canplay", handleReady);
1921
+ el.addEventListener("error", handleError);
1922
+ el.addEventListener("pause", handlePause);
1923
+ document.addEventListener("visibilitychange", handleVisibility);
1924
+ el.src = videoSrc;
1925
+ tryPlay();
1926
+ return () => {
1927
+ disposed = true;
1928
+ el.removeEventListener("loadeddata", handleReady);
1929
+ el.removeEventListener("canplay", handleReady);
1930
+ el.removeEventListener("error", handleError);
1931
+ el.removeEventListener("pause", handlePause);
1932
+ document.removeEventListener("visibilitychange", handleVisibility);
1933
+ el.pause();
1934
+ el.removeAttribute("src");
1935
+ el.load();
1936
+ setVideoReady(false);
1937
+ setVideo(null);
1938
+ };
1939
+ }, [videoSrc]);
1940
+ const canvasRef = useShaderCanvas({
1941
+ fragmentShader: FRAGMENT_SHADER10,
1942
+ speed,
1943
+ interactive,
1944
+ onDraw: ({ gl, uniform, dpr }) => {
1945
+ gl.uniform3fv(uniform("uColor"), hexToLinearRgb(color));
1946
+ gl.uniform3fv(uniform("uHighlight"), hexToLinearRgb(highlightColor));
1947
+ gl.uniform1f(uniform("uCell"), Math.max(2, cellSize) * dpr);
1948
+ gl.uniform1f(uniform("uFollow"), interactive ? 1 : 0);
1949
+ gl.uniform1f(uniform("uScheme"), scheme === "light" ? 1 : 0);
1950
+ gl.uniform1f(uniform("uSeam"), seams ? Math.max(0.5, seamWidth) * dpr : 0);
1951
+ const hasVideo = video != null && videoReady;
1952
+ gl.uniform1f(uniform("uHasVideo"), hasVideo ? 1 : 0);
1953
+ if (!hasVideo || !video) return;
1954
+ if (textureRef.current?.gl !== gl) {
1955
+ const texture = gl.createTexture();
1956
+ gl.bindTexture(gl.TEXTURE_2D, texture);
1957
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
1958
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
1959
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
1960
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
1961
+ textureRef.current = { gl, texture };
1962
+ }
1963
+ gl.activeTexture(gl.TEXTURE0);
1964
+ gl.bindTexture(gl.TEXTURE_2D, textureRef.current.texture);
1965
+ if (video.readyState >= video.HAVE_CURRENT_DATA && video.videoWidth > 0) {
1966
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, video);
1967
+ }
1968
+ gl.uniform1i(uniform("uVideo"), 0);
1969
+ gl.uniform2f(uniform("uVideoSize"), video.videoWidth, video.videoHeight);
1970
+ }
1971
+ });
1972
+ return /* @__PURE__ */ jsxs(
1973
+ "div",
1974
+ {
1975
+ className: cn("elyri-pixel-vortex", className),
1976
+ "data-scheme": scheme,
1977
+ style: { "--elyri-pixel-vortex-color": color },
1978
+ children: [
1979
+ /* @__PURE__ */ jsx("canvas", { ref: canvasRef, className: "elyri-pixel-vortex__canvas", "aria-hidden": "true" }),
1980
+ children != null && /* @__PURE__ */ jsx("div", { className: "elyri-pixel-vortex__content", children })
1981
+ ]
1982
+ }
1983
+ );
1984
+ }
1985
+ var PLANET_PRESETS = {
1986
+ mars: { color: "#c1532b", terrainColor: "#4e1e12", atmosphereColor: "#f2a57c", atmosphere: 0.6 },
1987
+ jupiter: { color: "#ecdcc0", terrainColor: "#a86a3a", atmosphereColor: "#d9cdb8", atmosphere: 0.3 },
1988
+ earth: { color: "#3f5a2a", terrainColor: "#0a2744", atmosphereColor: "#6fa8ff", atmosphere: 0.55 }
1989
+ };
1990
+ var COMMON = `
1991
+ precision highp float;
1992
+
1993
+ #define PI 3.14159265
1994
+ #define HALF 8
1995
+
1996
+ uniform vec2 uResolution;
1997
+ uniform float uTime;
1998
+ uniform vec2 uPointer;
1999
+ uniform vec3 uSurface;
2000
+ uniform vec3 uTerrain;
2001
+ uniform vec3 uAtmosphere;
2002
+ uniform float uDensity;
2003
+ uniform float uStars;
2004
+ uniform float uHorizon;
2005
+ uniform float uSun;
2006
+ uniform vec2 uSunPos;
2007
+ uniform float uSunFixed;
2008
+
2009
+ const float ALT = 0.15;
2010
+ const float FL = 1.25;
2011
+ const float H = 0.0045;
2012
+ const float RT = 1.0 + 10.0 * H;
2013
+ const float SUN_I = 5.0;
2014
+ const float SUN_R = 0.012;
2015
+
2016
+ float hash12(vec2 p) {
2017
+ vec3 p3 = fract(vec3(p.xyx) * 0.1031);
2018
+ p3 += dot(p3, p3.yzx + 33.33);
2019
+ return fract((p3.x + p3.y) * p3.z);
2020
+ }
2021
+
2022
+ float hash13(vec3 p) {
2023
+ p = fract(p * 0.1031);
2024
+ p += dot(p, p.zyx + 31.32);
2025
+ return fract((p.x + p.y) * p.z);
2026
+ }
2027
+
2028
+ vec3 hash33(vec3 p) {
2029
+ p = fract(p * vec3(0.1031, 0.1030, 0.0973));
2030
+ p += dot(p, p.yxz + 33.33);
2031
+ return fract((p.xxy + p.yxx) * p.zyx);
2032
+ }
2033
+
2034
+ float noise2(vec2 p) {
2035
+ vec2 i = floor(p);
2036
+ vec2 f = fract(p);
2037
+ vec2 u = f * f * (3.0 - 2.0 * f);
2038
+ return mix(
2039
+ mix(hash12(i), hash12(i + vec2(1.0, 0.0)), u.x),
2040
+ mix(hash12(i + vec2(0.0, 1.0)), hash12(i + vec2(1.0, 1.0)), u.x),
2041
+ u.y
2042
+ );
2043
+ }
2044
+
2045
+ float noise3(vec3 x) {
2046
+ vec3 i = floor(x);
2047
+ vec3 f = fract(x);
2048
+ vec3 u = f * f * (3.0 - 2.0 * f);
2049
+ return mix(
2050
+ mix(mix(hash13(i), hash13(i + vec3(1, 0, 0)), u.x), mix(hash13(i + vec3(0, 1, 0)), hash13(i + vec3(1, 1, 0)), u.x), u.y),
2051
+ mix(mix(hash13(i + vec3(0, 0, 1)), hash13(i + vec3(1, 0, 1)), u.x), mix(hash13(i + vec3(0, 1, 1)), hash13(i + vec3(1, 1, 1)), u.x), u.y),
2052
+ u.z
2053
+ );
2054
+ }
2055
+
2056
+ float lod(float freqTimesFootprint) {
2057
+ return 1.0 - smoothstep(0.25, 0.6, freqTimesFootprint);
2058
+ }
2059
+
2060
+ float fbm3(vec3 p, float fp, int octaves) {
2061
+ float s = 0.0;
2062
+ float a = 0.5;
2063
+ float f = 1.0;
2064
+ for (int i = 0; i < 8; i++) {
2065
+ float w = lod(f * fp);
2066
+ if (i >= octaves || w <= 0.0) break;
2067
+ s += a * w * (noise3(p * f + float(i) * 7.13) - 0.5);
2068
+ a *= 0.55;
2069
+ f *= 2.02;
2070
+ }
2071
+ return s;
2072
+ }
2073
+
2074
+ float chapman(float X, float h, float coschi) {
2075
+ float c = sqrt(X + h);
2076
+ if (coschi >= 0.0) return c / (c * coschi + 1.0) * exp(-h);
2077
+ float x0 = sqrt(1.0 - coschi * coschi) * (X + h);
2078
+ return 2.0 * sqrt(x0) * exp(min(X - x0, 40.0)) - c / (1.0 - c * coschi) * exp(-h);
2079
+ }
2080
+
2081
+ vec3 stars(vec3 rd, float pixAngle) {
2082
+ vec2 a = vec2(atan(rd.x, -rd.z), asin(clamp(rd.y, -1.0, 1.0)));
2083
+ vec3 col = vec3(0.0);
2084
+ for (int layer = 0; layer < 2; layer++) {
2085
+ float scale = layer == 0 ? 70.0 : 190.0;
2086
+ float seed = float(layer) * 17.0;
2087
+ vec2 g = a * scale;
2088
+ vec2 cell = floor(g);
2089
+ if (hash12(cell + seed) > uStars * (layer == 0 ? 0.22 : 0.2)) continue;
2090
+ vec2 pos = 0.2 + 0.6 * vec2(hash12(cell + seed + 3.1), hash12(cell + seed + 7.7));
2091
+ float px = length(fract(g) - pos) / scale / pixAngle;
2092
+ float h = hash12(cell + seed + 5.3);
2093
+ float mag = layer == 0 ? 0.05 + 2.0 * pow(h, 8.0) : 0.015 + 0.05 * h;
2094
+ float tw = 0.7 + 0.3 * sin(uTime * (0.8 + 2.5 * h) + h * 40.0);
2095
+ vec3 tint = mix(vec3(0.62, 0.74, 1.0), vec3(1.0, 0.82, 0.64), hash12(cell + seed + 9.9));
2096
+ col += tint * mag * tw * exp(-px * px * 1.4);
2097
+ }
2098
+ vec2 q = a * vec2(2.2, 3.4);
2099
+ float neb = noise2(q) * 0.55 + noise2(q * 2.1 + 4.0) * 0.3 + noise2(q * 4.3 + 9.0) * 0.15;
2100
+ float band = exp(-pow((a.y + 0.35 * a.x + 0.05) * 2.6, 2.0));
2101
+ col += mix(vec3(0.05, 0.04, 0.12), vec3(0.1, 0.05, 0.07), neb) * smoothstep(0.35, 0.85, neb) * band * 0.12 * uStars;
2102
+ return col;
2103
+ }
2104
+
2105
+ vec3 aces(vec3 x) {
2106
+ return clamp((x * (2.51 * x + 0.03)) / (x * (2.43 * x + 0.59) + 0.14), 0.0, 1.0);
2107
+ }
2108
+ `;
2109
+ var RELIEF = `
2110
+ vec4 noised(vec3 x) {
2111
+ vec3 i = floor(x);
2112
+ vec3 w = fract(x);
2113
+ vec3 u = w * w * w * (w * (w * 6.0 - 15.0) + 10.0);
2114
+ vec3 du = 30.0 * w * w * (w * (w - 2.0) + 1.0);
2115
+ float a = hash13(i);
2116
+ float b = hash13(i + vec3(1, 0, 0));
2117
+ float c = hash13(i + vec3(0, 1, 0));
2118
+ float d = hash13(i + vec3(1, 1, 0));
2119
+ float e = hash13(i + vec3(0, 0, 1));
2120
+ float f = hash13(i + vec3(1, 0, 1));
2121
+ float g = hash13(i + vec3(0, 1, 1));
2122
+ float h = hash13(i + vec3(1, 1, 1));
2123
+ float k1 = b - a;
2124
+ float k2 = c - a;
2125
+ float k3 = e - a;
2126
+ float k4 = a - b - c + d;
2127
+ float k5 = a - c - e + g;
2128
+ float k6 = a - b - e + f;
2129
+ float k7 = -a + b + c - d + e - f - g + h;
2130
+ return vec4(
2131
+ a + k1 * u.x + k2 * u.y + k3 * u.z + k4 * u.x * u.y + k5 * u.y * u.z + k6 * u.z * u.x + k7 * u.x * u.y * u.z,
2132
+ du * vec3(
2133
+ k1 + k4 * u.y + k6 * u.z + k7 * u.y * u.z,
2134
+ k2 + k5 * u.z + k4 * u.x + k7 * u.z * u.x,
2135
+ k3 + k6 * u.x + k5 * u.y + k7 * u.x * u.y
2136
+ )
2137
+ );
2138
+ }
2139
+
2140
+ vec4 terrain(vec3 p, float fp) {
2141
+ vec4 s = vec4(0.0);
2142
+ vec3 dsum = vec3(0.0);
2143
+ float a = 0.5;
2144
+ float f = 1.0;
2145
+ for (int i = 0; i < 10; i++) {
2146
+ float w = lod(f * fp);
2147
+ if (w <= 0.0) break;
2148
+ vec4 n = noised(p * f + float(i) * vec3(3.1, 7.7, 1.3));
2149
+ vec3 g = n.yzw * f;
2150
+ dsum += g * a;
2151
+ float k = a * w / (1.0 + 0.35 * dot(dsum, dsum));
2152
+ s.x += k * (n.x - 0.5);
2153
+ s.yzw += k * g;
2154
+ a *= 0.5;
2155
+ f *= 2.03;
2156
+ }
2157
+ return s;
2158
+ }
2159
+ `;
2160
+ var MARS_SURFACE = `
2161
+ uniform float uCraters;
2162
+
2163
+ const float BUMP = 0.06;
2164
+
2165
+ float craterProfile(float x) {
2166
+ return 0.8 * (smoothstep(0.0, 1.0, x) - 1.0) + 0.3 * exp(-10.0 * (x - 1.0) * (x - 1.0));
2167
+ }
2168
+
2169
+ float craterLayer(vec3 p, float scale, float fp, float seed, inout vec3 grad) {
2170
+ vec3 g = p * scale + seed;
2171
+ vec3 cell = floor(g);
2172
+ vec3 r = hash33(cell + seed);
2173
+ if (r.x > 0.75 * uCraters) return 0.0;
2174
+ vec3 c = cell + 0.3 + 0.4 * hash33(cell * 1.7 + seed + 11.0);
2175
+ float R = mix(0.07, 0.18, r.y * r.y);
2176
+ vec3 d = g - c;
2177
+ float dist = length(d);
2178
+ float x = dist / R;
2179
+ if (x > 1.6) return 0.0;
2180
+ float w = 1.0 - smoothstep(0.1, 0.4, fp * scale / R);
2181
+ float depth = uCraters * mix(0.5, 1.0, r.z) * w;
2182
+ float dh = (craterProfile(x + 0.01) - craterProfile(x - 0.01)) / 0.02;
2183
+ grad += dh * d / max(dist, 1e-4) * depth * 0.8;
2184
+ return craterProfile(x) * w;
2185
+ }
2186
+
2187
+ vec3 shadeSurface(vec3 n, float ndv, float pix, vec3 sun, vec3 beta, float X) {
2188
+ float fp = pix / max(sqrt(ndv), 0.12);
2189
+
2190
+ float ang = -uTime * 0.01;
2191
+ float cs = cos(ang);
2192
+ float sn = sin(ang);
2193
+ mat3 spin = mat3(1.0, 0.0, 0.0, 0.0, cs, sn, 0.0, -sn, cs);
2194
+ vec3 q = spin * n;
2195
+
2196
+ vec4 ter = terrain(q * 4.0 + 2.0, fp * 4.0);
2197
+ vec3 grad = ter.yzw * 4.0 * BUMP;
2198
+ float crater = 0.0;
2199
+ crater += craterLayer(q, 5.0, fp, 1.3, grad);
2200
+ crater += craterLayer(q, 11.0, fp, 7.9, grad);
2201
+ crater += craterLayer(q, 23.0, fp, 4.1, grad);
2202
+ crater += craterLayer(q, 47.0, fp, 9.6, grad);
2203
+ crater += craterLayer(q, 97.0, fp, 5.7, grad);
2204
+ crater += craterLayer(q, 199.0, fp, 2.2, grad);
2205
+ grad = grad * spin;
2206
+ grad -= dot(grad, n) * n;
2207
+ vec3 nb = normalize(n - grad);
2208
+
2209
+ float big = fbm3(q * 1.7 + 4.0, fp * 1.7, 8);
2210
+ float mid = fbm3(q * 9.0 + 1.0, fp * 9.0, 8);
2211
+ float fine = fbm3(q * 70.0 + 3.0, fp * 70.0, 8);
2212
+ vec3 albedo = mix(uSurface, uTerrain, smoothstep(-0.12, 0.22, big + 0.35 * mid + 0.15 * fine));
2213
+ albedo *= 0.8 + 0.7 * (mid + 0.5) * 0.6 + 2.0 * ter.x + 0.5 * fine;
2214
+ albedo *= 1.0 + 0.3 * crater;
2215
+ albedo = max(albedo, 0.0);
2216
+
2217
+ float ngl = dot(n, sun);
2218
+ float mu0 = max(dot(nb, sun), 0.0);
2219
+ float lsl = mu0 / (mu0 + ndv + 1e-3);
2220
+ float brdf = mix(mu0, lsl, 0.35) * smoothstep(-0.03, 0.08, ngl);
2221
+ vec3 sunT = exp(-beta * H * chapman(X, 0.0, ngl));
2222
+ vec3 sky = uAtmosphere * uDensity * 0.05 * smoothstep(-0.3, 0.4, ngl);
2223
+ return albedo * (SUN_I * 0.9 * sunT * brdf + sky + 0.004);
2224
+ }
2225
+ `;
2226
+ var JUPITER_SURFACE = `
2227
+ const vec3 AXIS = vec3(0.0, 0.2955, -0.9553);
2228
+ const vec3 EAST0 = vec3(1.0, 0.0, 0.0);
2229
+ const vec3 MERIDIAN0 = vec3(0.0, 0.9553, 0.2955);
2230
+ const float FLOW_T = 24.0;
2231
+
2232
+ vec3 spinAxis(vec3 p, float a) {
2233
+ float c = cos(a);
2234
+ float s = sin(a);
2235
+ return p * c + cross(AXIS, p) * s + AXIS * dot(AXIS, p) * (1.0 - c);
2236
+ }
2237
+
2238
+ vec3 fromLatLon(float lat, float lon) {
2239
+ return cos(lat) * (cos(lon) * MERIDIAN0 + sin(lon) * EAST0) + sin(lat) * AXIS;
2240
+ }
2241
+
2242
+ vec2 harmonic(float lat, float k, float a, float ph, float fpLat) {
2243
+ float w = a * lod(k * fpLat / (2.0 * PI));
2244
+ return w * vec2(sin(k * lat + ph), k * cos(k * lat + ph));
2245
+ }
2246
+
2247
+ vec2 bandField(float lat, float fpLat) {
2248
+ return harmonic(lat, 61.0, 1.0, 0.9, fpLat)
2249
+ + harmonic(lat, 107.0, 0.55, 2.4, fpLat)
2250
+ + harmonic(lat, 211.0, 0.3, 4.1, fpLat)
2251
+ + harmonic(lat, 409.0, 0.16, 1.7, fpLat);
2252
+ }
2253
+
2254
+ float jet(float lat) {
2255
+ return 0.00011 * (61.0 * cos(61.0 * lat + 0.9) + 0.55 * 107.0 * cos(107.0 * lat + 2.4));
2256
+ }
2257
+
2258
+ vec2 cloudPhase(vec3 p, float lat, float t, float seed, float fp) {
2259
+ vec3 q = spinAxis(p, -jet(lat) * t);
2260
+ q += AXIS * dot(q, AXIS) * 2.0;
2261
+ q += seed * vec3(1.7, 9.2, 4.3);
2262
+ float wx = fbm3(q * 7.0, fp * 14.0, 5);
2263
+ float wy = fbm3(q * 7.0 + 5.2, fp * 14.0, 5);
2264
+ float detail = fbm3(q * 30.0 + vec3(wx, wy, wx - wy) * 9.0, fp * 60.0, 7);
2265
+ return vec2(wx, detail);
2266
+ }
2267
+
2268
+ vec2 vortexFrame(vec3 p, float lat0, float lon0, vec2 radii) {
2269
+ vec3 c = fromLatLon(lat0, lon0);
2270
+ vec3 east = normalize(cross(AXIS, c));
2271
+ vec3 north = cross(c, east);
2272
+ vec3 d = p - c;
2273
+ return vec2(dot(d, east), dot(d, north)) / radii;
2274
+ }
2275
+
2276
+ vec2 vortex(vec2 v, float spin, float seed, float fp) {
2277
+ float r = length(v);
2278
+ float ang = spin * uTime + 2.4 * (1.0 - smoothstep(0.0, 1.1, r));
2279
+ float c = cos(ang);
2280
+ float s = sin(ang);
2281
+ vec2 w = mat2(c, -s, s, c) * v;
2282
+ float tex = fbm3(vec3(w * 2.4, r * 6.0 + seed), fp * 6.0, 6);
2283
+ float mask = 1.0 - smoothstep(0.6, 1.0, r + tex * 0.5);
2284
+ return vec2(mask, tex);
2285
+ }
2286
+
2287
+ float vortexPush(vec2 v, float radiusY) {
2288
+ float r = length(v);
2289
+ return v.y / max(r, 1e-3) * radiusY * 0.7 * (1.0 - smoothstep(0.9, 2.6, r)) * smoothstep(0.0, 0.9, r);
2290
+ }
2291
+
2292
+ vec3 shadeSurface(vec3 n, float ndv, float pix, vec3 sun, vec3 beta, float X) {
2293
+ float fp = pix / max(sqrt(ndv), 0.12);
2294
+ float fpLat = pix / max(ndv, 0.03);
2295
+
2296
+ float lat = asin(clamp(dot(n, AXIS), -1.0, 1.0));
2297
+
2298
+ float cyc = uTime / FLOW_T;
2299
+ float phA = fract(cyc);
2300
+ float phB = fract(cyc + 0.5);
2301
+ float wA = 1.0 - abs(2.0 * phA - 1.0);
2302
+ vec2 A = cloudPhase(n, lat, phA * FLOW_T, mod(floor(cyc), 61.0), fp);
2303
+ vec2 B = cloudPhase(n, lat, phB * FLOW_T, mod(floor(cyc + 0.5), 61.0) + 0.5, fp);
2304
+ vec2 cl = (A * wA + B * (1.0 - wA)) / sqrt(wA * wA + (1.0 - wA) * (1.0 - wA));
2305
+
2306
+ const vec2 GRS_R = vec2(0.05, 0.028);
2307
+ vec2 grs = vortexFrame(n, 0.6, -0.1 + 0.03 * sin(uTime * 0.021), GRS_R);
2308
+ float push = vortexPush(grs, GRS_R.y);
2309
+ const vec2 OVAL_R = vec2(0.013, 0.0085);
2310
+ vec2 ovals[5];
2311
+ for (int i = 0; i < 5; i++) {
2312
+ float fi = float(i);
2313
+ float lon = fi * 0.085 - 0.3 + 0.02 * sin(uTime * 0.017 + fi * 1.9);
2314
+ ovals[i] = vortexFrame(n, 0.545 + 0.003 * sin(fi * 2.3), lon, OVAL_R);
2315
+ push += vortexPush(ovals[i], OVAL_R.y);
2316
+ }
2317
+
2318
+ float latB = lat + cl.x * 0.02 + push;
2319
+ vec2 band = bandField(latB, fpLat);
2320
+ float soft = 0.25 + abs(band.y) * fpLat;
2321
+ float belt = smoothstep(-soft, soft, band.x - 0.1);
2322
+ float edge = 1.0 - abs(2.0 * belt - 1.0);
2323
+
2324
+ float hue = 0.5 + 0.5 * sin(latB * 17.0 + 1.7);
2325
+ vec3 beltCol = mix(uTerrain, uTerrain * vec3(1.25, 0.9, 0.62), hue);
2326
+ vec3 zoneCol = mix(uSurface, uSurface * vec3(1.0, 0.9, 0.72), 1.0 - hue);
2327
+ vec3 albedo = mix(zoneCol, beltCol, belt);
2328
+ albedo *= 1.0 + cl.y * (1.6 + 2.4 * edge) + 0.1 * band.x;
2329
+ float festoon = smoothstep(0.05, 0.2, cl.y) * edge;
2330
+ albedo = mix(albedo, albedo * vec3(0.5, 0.62, 0.78), festoon * 0.55);
2331
+
2332
+ float rg = length(grs);
2333
+ vec2 gv = vortex(grs, 0.18, 3.0, fp / GRS_R.y);
2334
+ float collar = exp(-pow((rg - 1.1) / 0.22, 2.0));
2335
+ albedo = mix(albedo, zoneCol * (1.1 + cl.y), collar * 0.75);
2336
+ vec3 red = uTerrain * vec3(1.7, 0.66, 0.4);
2337
+ red *= (0.7 + 0.4 * smoothstep(0.0, 0.85, rg)) * (1.0 + 1.8 * gv.y);
2338
+ albedo = mix(albedo, red, gv.x);
2339
+
2340
+ for (int i = 0; i < 5; i++) {
2341
+ float r = length(ovals[i]);
2342
+ if (r < 1.6) {
2343
+ vec2 o = vortex(ovals[i], -0.4, float(i) * 3.1, fp / OVAL_R.y);
2344
+ albedo *= 1.0 - 0.35 * exp(-pow((r - 1.0) / 0.18, 2.0));
2345
+ albedo = mix(albedo, uSurface * (1.08 + 1.4 * o.y) * (0.9 + 0.15 * smoothstep(0.0, 0.7, r)), o.x * 0.92);
2346
+ }
2347
+ }
2348
+ albedo = max(albedo, 0.0);
2349
+
2350
+ float ngl = dot(n, sun);
2351
+ float mu0 = max(ngl, 0.0);
2352
+ float brdf = mu0 * smoothstep(-0.02, 0.1, ngl) + 0.015 * smoothstep(-0.12, 0.04, ngl);
2353
+ vec3 sunT = exp(-beta * H * chapman(X, 0.0, ngl));
2354
+ vec3 sky = uAtmosphere * uDensity * 0.05 * smoothstep(-0.3, 0.4, ngl);
2355
+ return albedo * (SUN_I * 0.9 * sunT * brdf + sky + 0.004);
2356
+ }
2357
+ `;
2358
+ var EARTH_SURFACE = `
2359
+ uniform float uCityLights;
2360
+
2361
+ const float CLOUD_H = 0.0016;
2362
+ const vec3 CITY_WARM = vec3(1.0, 0.5, 0.16);
2363
+ const vec3 CITY_COOL = vec3(1.0, 0.86, 0.66);
2364
+
2365
+ mat3 spinX(float a) {
2366
+ float c = cos(a);
2367
+ float s = sin(a);
2368
+ return mat3(1.0, 0.0, 0.0, 0.0, c, s, 0.0, -s, c);
2369
+ }
2370
+
2371
+ vec3 cloudWarp(vec3 p, float fp) {
2372
+ vec3 s = p * 1.8 + vec3(0.0, 0.0, uTime * 0.0035);
2373
+ return vec3(fbm3(s + 1.3, fp * 1.8, 4), fbm3(s + 7.1, fp * 1.8, 4), fbm3(s + 4.4, fp * 1.8, 4));
2374
+ }
2375
+
2376
+ float cloudCover(vec3 p, vec3 w, float fp) {
2377
+ float t = uTime * 0.006;
2378
+ float big = fbm3(p * 3.2 + w * 2.4 + vec3(t, 0.0, -t), fp * 3.2, 6);
2379
+ float puff = fbm3(p * 21.0 + w * 5.0 - vec3(0.0, t, t) * 3.0, fp * 21.0, 6);
2380
+ float c = big + 0.4 * puff * smoothstep(-0.15, 0.1, big);
2381
+ return smoothstep(0.03, 0.26, c + 0.02);
2382
+ }
2383
+
2384
+ // \u6BCF\u4E2A\u683C\u5B50\u81F3\u591A\u4E00\u5EA7\u805A\u843D\uFF0C\u4F4D\u7F6E\u5728\u683C\u5185\u5B8C\u5168\u968F\u673A\uFF1B\u67E5 2x2x2 \u90BB\u683C\u907F\u514D\u88AB\u683C\u5B50\u8FB9\u754C\u622A\u65AD
2385
+ float settlements(vec3 q, float scale, float fp, float P, float seed) {
2386
+ vec3 g = q * scale + seed * 17.0;
2387
+ vec3 base = floor(g - 0.5);
2388
+ float f2 = fp * scale * fp * scale * 0.25;
2389
+ float sum = 0.0;
2390
+ for (int i = 0; i < 8; i++) {
2391
+ float fi = float(i);
2392
+ vec3 cell = base + vec3(mod(fi, 2.0), mod(floor(fi * 0.5), 2.0), floor(fi * 0.25));
2393
+ vec3 r = hash33(cell + seed);
2394
+ float w = smoothstep(r.x, r.x + 0.15, P);
2395
+ if (w <= 0.0) continue;
2396
+ vec3 d = g - cell - hash33(cell * 1.7 + seed + 5.0);
2397
+ d -= dot(d, q) * q;
2398
+ float s2 = mix(0.03, 0.14, r.y * r.y);
2399
+ s2 *= s2;
2400
+ float t2 = s2 + f2;
2401
+ sum += w * exp(-dot(d, d) / t2) * s2 / t2 * (0.5 + 0.5 * r.z);
2402
+ }
2403
+ return sum;
2404
+ }
2405
+
2406
+ vec3 cityLights(vec3 q, float fp, float P) {
2407
+ float metro = settlements(q, 40.0, fp, P * 0.75, 1.0);
2408
+ float town = settlements(q, 170.0, fp, P * 0.45 + min(metro, 1.0) * 0.4, 2.0);
2409
+ float t = min(metro, 1.0);
2410
+ // \u8857\u533A\u9897\u7C92\uFF1A\u5E02\u4E2D\u5FC3\u8FDE\u6210\u4E00\u7247\uFF0C\u8D8A\u5F80\u90CA\u533A\u8D8A\u7834\u788E
2411
+ float grain = 0.6 * noise3(q * 1800.0) + 0.4 * noise3(q * 700.0 + 3.1);
2412
+ float lo = 0.72 - 0.55 * t;
2413
+ float mask = mix(0.2 + 0.6 * t, smoothstep(lo, lo + 0.2, grain), lod(fp * 1800.0));
2414
+ vec3 tint = mix(CITY_WARM, CITY_COOL, t * t * 0.8);
2415
+ return tint * metro * mask * 1.1 + CITY_WARM * min(town, 1.5) * 0.35;
2416
+ }
2417
+
2418
+ vec3 shadeSurface(vec3 n, float ndv, float pix, vec3 sun, vec3 beta, float X) {
2419
+ float fp = pix / max(sqrt(ndv), 0.12);
2420
+
2421
+ mat3 spin = spinX(-uTime * 0.008);
2422
+ vec3 q = spin * n;
2423
+
2424
+ vec3 cw = vec3(fbm3(q * 1.2 + 3.7, fp * 1.2, 4), fbm3(q * 1.2 + 9.1, fp * 1.2, 4), fbm3(q * 1.2 + 5.3, fp * 1.2, 4));
2425
+ float cont = fbm3(q * 1.9 + cw * 1.4 + 0.7, fp * 1.9, 6);
2426
+ vec4 ter = terrain(q * 7.0 + 2.0, fp * 7.0);
2427
+ float e = cont + 0.3 * ter.x - 0.035;
2428
+ float aa = fp * 4.0 + 5e-4;
2429
+ float land = smoothstep(-aa, aa, e);
2430
+ float alt = max(e, 0.0);
2431
+ float mtn = smoothstep(0.02, 0.14, alt + 0.15 * ter.x);
2432
+
2433
+ vec3 grad = ter.yzw * 7.0 * 0.022 * (0.35 + 2.0 * mtn) * land;
2434
+ grad = grad * spin;
2435
+ grad -= dot(grad, n) * n;
2436
+ vec3 nb = normalize(n - grad);
2437
+
2438
+ float mid = fbm3(q * 14.0 + 1.0, fp * 14.0, 6);
2439
+ float dry = 0.0;
2440
+ float snow = 0.0;
2441
+ vec3 ground = uTerrain;
2442
+ if (land > 0.0) {
2443
+ float fine = fbm3(q * 60.0 + 3.0, fp * 60.0, 5);
2444
+ float moist = fbm3(q * 2.6 + 8.0, fp * 2.6, 6) + 0.15 * exp(-alt * 25.0);
2445
+ dry = 1.0 - smoothstep(-0.12, 0.02, moist + 0.25 * mid);
2446
+ float wet = smoothstep(-0.02, 0.12, moist + 0.2 * mid);
2447
+ vec3 grass = uSurface * vec3(1.9, 1.55, 1.0) + vec3(0.02, 0.012, 0.0);
2448
+ vec3 la = mix(grass, uSurface, wet);
2449
+ la = mix(la, vec3(0.36, 0.25, 0.13) * (0.85 + 0.4 * mid), dry);
2450
+ la = mix(la, vec3(0.1, 0.085, 0.07), mtn * 0.8);
2451
+ snow = smoothstep(0.17, 0.22, alt + 0.06 * fine + 0.04 * mid + 0.1 * abs(q.x));
2452
+ la = mix(la, vec3(0.8), snow);
2453
+ la *= max(0.85 + 0.5 * fine + 0.25 * mid, 0.2);
2454
+ ground = la;
2455
+ }
2456
+ float depth = max(-e, 0.0);
2457
+ vec3 ocean = mix(uTerrain, uTerrain * vec3(0.6, 2.4, 2.0) + vec3(0.0, 0.02, 0.02), exp(-depth * 60.0));
2458
+ ocean *= 0.9 + 0.4 * mid;
2459
+ vec3 albedo = mix(ocean, ground, land);
2460
+
2461
+ float ngl = dot(n, sun);
2462
+ float mu0 = max(dot(nb, sun), 0.0);
2463
+ float lsl = mu0 / (mu0 + ndv + 1e-3);
2464
+ float day = smoothstep(-0.03, 0.08, ngl);
2465
+ float brdf = mix(mu0, lsl, 0.25) * day;
2466
+ vec3 sunT = exp(-beta * H * chapman(X, 0.0, ngl));
2467
+ vec3 sky = uAtmosphere * uDensity * 0.05 * smoothstep(-0.3, 0.4, ngl);
2468
+
2469
+ mat3 cspin = spinX(-uTime * 0.0095);
2470
+ vec3 c = cspin * n;
2471
+ vec3 w = cloudWarp(c, fp);
2472
+ float cov = cloudCover(c, w, fp);
2473
+ vec3 sunTan = sun - ngl * n;
2474
+ sunTan = cspin * (sunTan * inversesqrt(max(dot(sunTan, sunTan), 1e-6)));
2475
+ float shade = 1.0;
2476
+ float relief = 1.0;
2477
+ if (ngl > -0.06) {
2478
+ float reach = CLOUD_H * sqrt(max(1.0 - ngl * ngl, 0.0)) / max(ngl, 0.05);
2479
+ shade = 1.0 - 0.75 * cloudCover(c + sunTan * reach, w, fp);
2480
+ relief = clamp(1.0 - 2.2 * (cloudCover(c + sunTan * 0.006, w, fp) - cov), 0.3, 1.25);
2481
+ }
2482
+
2483
+ vec3 color = albedo * (SUN_I * 0.9 * sunT * brdf * shade + sky + 0.004);
2484
+
2485
+ if (land < 1.0) {
2486
+ vec3 v = normalize(vec3(0.0, 1.0 + ALT, 0.0) - n);
2487
+ float wind = fbm3(q * vec3(10.0, 10.0, 34.0) + 4.2, fp * 34.0, 5);
2488
+ float alpha = mix(0.1, 0.3, smoothstep(-0.18, 0.18, wind));
2489
+ float a2 = alpha * alpha;
2490
+ vec3 hv = normalize(sun + v);
2491
+ float nh = max(dot(n, hv), 0.0);
2492
+ float nl = max(ngl, 0.0);
2493
+ float nv = max(dot(n, v), 1e-3);
2494
+ float dd = nh * nh * (a2 - 1.0) + 1.0;
2495
+ float k = alpha * 0.5;
2496
+ float G = nl / (nl * (1.0 - k) + k) * nv / (nv * (1.0 - k) + k);
2497
+ float F = 0.02 + 0.98 * pow(1.0 - max(dot(v, hv), 0.0), 5.0);
2498
+ float spec = a2 / (PI * dd * dd) * G * F / (4.0 * nv) * day;
2499
+ float Fv = 0.02 + 0.98 * pow(1.0 - nv, 5.0);
2500
+ vec3 skyRefl = uAtmosphere * uDensity * 0.35 * smoothstep(-0.2, 0.35, ngl) * Fv;
2501
+ color += (SUN_I * 0.9 * sunT * spec * shade + skyRefl) * (1.0 - land);
2502
+ }
2503
+
2504
+ float night = (1.0 - smoothstep(-0.08, -0.01, ngl)) * uCityLights;
2505
+ vec3 lights = vec3(0.0);
2506
+ if (night > 0.0 && land > 0.0) {
2507
+ float pop = fbm3(q * 4.3 + 13.0, fp * 4.3, 5) + 0.35 * fbm3(q * 17.0 + 2.0, fp * 17.0, 4);
2508
+ float P = smoothstep(-0.02, 0.2, pop + 0.12 * exp(-alt * 22.0) - 0.25 * dry - 0.3 * mtn - 0.5 * snow);
2509
+ lights = cityLights(q, fp, P) * land * night;
2510
+ color += lights;
2511
+ }
2512
+
2513
+ float ngc = ngl + 0.055;
2514
+ vec3 sunTc = exp(-beta * H * chapman(X, CLOUD_H / H, ngl));
2515
+ float cBrdf = mix(max(ngc, 0.0), ngc / (abs(ngc) + ndv + 1e-3), 0.3) * smoothstep(0.0, 0.1, ngc) * relief;
2516
+ vec3 cloud = vec3(0.78) * (SUN_I * 0.9 * sunTc * max(cBrdf, 0.0) + sky * 2.0 + 0.004);
2517
+ cloud += lights * 0.12;
2518
+ return mix(color, cloud, cov);
2519
+ }
2520
+ `;
2521
+ var MAIN = `
2522
+ void main() {
2523
+ vec2 frag = gl_FragCoord.xy;
2524
+ vec2 uv = (frag - 0.5 * uResolution) / uResolution.y;
2525
+ vec2 ptr = uPointer / uResolution;
2526
+ float pixAngle = 1.0 / (uResolution.y * FL);
2527
+
2528
+ float D = 1.0 + ALT;
2529
+ float dip = acos(1.0 / D);
2530
+ float yaw = (ptr.x - 0.5) * 0.04;
2531
+ float pitch = dip + atan((0.5 - uHorizon) / FL) + (ptr.y - 0.5) * 0.015;
2532
+ vec3 ro = vec3(0.0, D, 0.0);
2533
+ vec3 fw = vec3(sin(yaw) * cos(pitch), -sin(pitch), -cos(yaw) * cos(pitch));
2534
+ vec3 right = normalize(cross(fw, vec3(0.0, 1.0, 0.0)));
2535
+ vec3 up = cross(right, fw);
2536
+ vec3 rd = normalize(uv.x * right + uv.y * up + FL * fw);
2537
+
2538
+ vec2 sunRef = mix(ptr, uSunPos, uSunFixed);
2539
+ float az = -0.85 + (sunRef.x - 0.5) * 1.2;
2540
+ float el = -0.24 - (sunRef.y - 0.5) * 0.3;
2541
+ vec3 sun = normalize(vec3(sin(az) * cos(el), sin(el), -cos(az) * cos(el)));
2542
+
2543
+ vec3 beta = uAtmosphere * 7.0 * uDensity + 1e-4;
2544
+ float X = 1.0 / H;
2545
+
2546
+ float b = dot(ro, rd);
2547
+ float tc = -b;
2548
+ float impact = sqrt(max(dot(ro, ro) - b * b, 0.0));
2549
+ float coverage = clamp(0.5 - (impact - 1.0) / (tc * pixAngle), 0.0, 1.0);
2550
+ float disc = b * b - dot(ro, ro) + 1.0;
2551
+ float tPlanet = disc > 0.0 ? -b - sqrt(disc) : tc;
2552
+
2553
+ float mu = dot(rd, sun);
2554
+ float sunAng = sqrt(max(2.0 * (1.0 - mu), 0.0));
2555
+ vec3 space = stars(rd, pixAngle);
2556
+ space += vec3(1.0, 0.96, 0.9) * 60.0 * smoothstep(SUN_R, SUN_R * 0.75, sunAng) * uSun;
2557
+
2558
+ vec3 background = space;
2559
+ if (coverage > 0.0) {
2560
+ vec3 n = normalize(ro + rd * tPlanet);
2561
+ float ndv = max(dot(n, -rd), 0.0);
2562
+ vec3 surface = shadeSurface(n, ndv, tPlanet * pixAngle, sun, beta, X);
2563
+ background = mix(space, surface, coverage);
2564
+ }
2565
+
2566
+ vec3 inscatter = vec3(0.0);
2567
+ vec3 trans = vec3(1.0);
2568
+ float bA = b * b - dot(ro, ro) + RT * RT;
2569
+ if (bA > 0.0) {
2570
+ float t0 = max(-b - sqrt(bA), 0.0);
2571
+ float t1 = mix(-b + sqrt(bA), tPlanet, coverage);
2572
+ float tm = clamp(tc, t0, t1);
2573
+ float jitter = fract(52.9829189 * fract(dot(frag, vec2(0.06711056, 0.00583715))));
2574
+ float du = 1.0 / float(HALF);
2575
+ float od = 0.0;
2576
+ vec3 scat = vec3(0.0);
2577
+ for (int i = 0; i < 2 * HALF; i++) {
2578
+ bool near = i < HALF;
2579
+ float u = ((near ? float(HALF - 1 - i) : float(i - HALF)) + jitter) * du;
2580
+ float len = near ? tm - t0 : t1 - tm;
2581
+ float t = near ? tm - len * u * u : tm + len * u * u;
2582
+ vec3 p = ro + rd * t;
2583
+ float r = length(p);
2584
+ float h = max(r - 1.0, 0.0) / H;
2585
+ float dens = exp(-h) * 2.0 * u * len * du;
2586
+ float odSun = H * chapman(X, h, dot(p, sun) / r);
2587
+ scat += dens * exp(-beta * (od + 0.5 * dens + odSun));
2588
+ od += dens;
2589
+ }
2590
+ trans = exp(-beta * od);
2591
+ float g = 0.76;
2592
+ float phaseR = 3.0 / (16.0 * PI) * (1.0 + mu * mu);
2593
+ float phaseM = (1.0 - g * g) / (4.0 * PI * pow(1.0 + g * g - 2.0 * g * mu, 1.5));
2594
+ inscatter = SUN_I * 7.0 * beta * scat * mix(phaseR, phaseM, 0.4 - 0.25 * coverage);
2595
+ }
2596
+
2597
+ vec3 color = inscatter + trans * background;
2598
+
2599
+ vec3 toCenter = -normalize(ro);
2600
+ float sunCos = dot(sun, toCenter);
2601
+ float clearance = acos(clamp(sunCos, -1.0, 1.0)) - asin(1.0 / D);
2602
+ float sunImpact = D * sqrt(max(1.0 - sunCos * sunCos, 0.0));
2603
+ float hs = max(sunImpact - 1.0, 0.0) / H;
2604
+ vec3 sunTrans = sunCos > 0.0 ? exp(-beta * H * 2.0 * sqrt(X + hs) * exp(-hs)) : vec3(1.0);
2605
+ float sunVis = smoothstep(-SUN_R, SUN_R * 2.0, clearance);
2606
+ vec2 sunUv = vec2(dot(sun, right), dot(sun, up)) / max(dot(sun, fw), 1e-3) * FL;
2607
+ vec2 sd = uv - sunUv;
2608
+ float glow = 0.9 * exp(-sunAng * 30.0) + 0.1 * exp(-sunAng * 7.0) + 0.015 * exp(-sunAng * 2.0);
2609
+ float streak = exp(-abs(sd.y) * 110.0) * exp(-abs(sd.x) * 3.0) * 0.08 * step(0.0, dot(sun, fw));
2610
+ color += vec3(1.0, 0.93, 0.85) * sunTrans * sunVis * (glow + streak) * uSun;
2611
+
2612
+ color *= 1.0 - 0.35 * dot(uv * vec2(0.6, 1.0), uv * vec2(0.6, 1.0));
2613
+ color = pow(aces(color * 0.9), vec3(1.0 / 2.2));
2614
+ color += (hash12(frag + fract(uTime)) - 0.5) / 255.0;
2615
+
2616
+ gl_FragColor = vec4(color, 1.0);
2617
+ }
2618
+ `;
2619
+ var SHADERS = {
2620
+ mars: COMMON + RELIEF + MARS_SURFACE + MAIN,
2621
+ jupiter: COMMON + JUPITER_SURFACE + MAIN,
2622
+ earth: COMMON + RELIEF + EARTH_SURFACE + MAIN
2623
+ };
2624
+ function Planet({
2625
+ children,
2626
+ className,
2627
+ planet = "mars",
2628
+ color,
2629
+ terrainColor,
2630
+ atmosphereColor,
2631
+ atmosphere,
2632
+ craters = 0.6,
2633
+ cityLights = true,
2634
+ stars = 0.6,
2635
+ horizon = 0.62,
2636
+ speed = 1,
2637
+ sun = false,
2638
+ sunPosition,
2639
+ interactive = true
2640
+ }) {
2641
+ const preset = PLANET_PRESETS[planet] ?? PLANET_PRESETS.mars;
2642
+ const surfaceColor = color ?? preset.color;
2643
+ const darkColor = terrainColor ?? preset.terrainColor;
2644
+ const airColor = atmosphereColor ?? preset.atmosphereColor;
2645
+ const density = atmosphere ?? preset.atmosphere;
2646
+ const canvasRef = useShaderCanvas({
2647
+ fragmentShader: SHADERS[planet] ?? SHADERS.mars,
2648
+ speed,
2649
+ interactive,
2650
+ onDraw: ({ gl, uniform }) => {
2651
+ const clamp01 = (value) => Math.min(1, Math.max(0, value));
2652
+ gl.uniform3fv(uniform("uSurface"), hexToLinearRgb(surfaceColor));
2653
+ gl.uniform3fv(uniform("uTerrain"), hexToLinearRgb(darkColor));
2654
+ gl.uniform3fv(uniform("uAtmosphere"), hexToLinearRgb(airColor));
2655
+ gl.uniform1f(uniform("uDensity"), clamp01(density));
2656
+ gl.uniform1f(uniform("uCraters"), clamp01(craters));
2657
+ gl.uniform1f(uniform("uCityLights"), cityLights ? 1 : 0);
2658
+ gl.uniform1f(uniform("uStars"), clamp01(stars));
2659
+ gl.uniform1f(uniform("uHorizon"), Math.min(0.95, Math.max(0.05, horizon)));
2660
+ gl.uniform1f(uniform("uSun"), sun ? 1 : 0);
2661
+ gl.uniform2f(uniform("uSunPos"), clamp01(sunPosition?.x ?? 0.5), clamp01(sunPosition?.y ?? 0.5));
2662
+ gl.uniform1f(uniform("uSunFixed"), sunPosition ? 1 : 0);
2663
+ }
2664
+ });
2665
+ return /* @__PURE__ */ jsxs(
2666
+ "div",
2667
+ {
2668
+ className: cn("elyri-planet", className),
2669
+ "data-planet": planet,
2670
+ style: {
2671
+ "--elyri-planet-color": surfaceColor,
2672
+ "--elyri-planet-atmosphere": airColor,
2673
+ "--elyri-planet-horizon": `${horizon * 100}%`
2674
+ },
2675
+ children: [
2676
+ /* @__PURE__ */ jsx("canvas", { ref: canvasRef, className: "elyri-planet__canvas", "aria-hidden": "true" }),
2677
+ children != null && /* @__PURE__ */ jsx("div", { className: "elyri-planet__content", children })
2678
+ ]
2679
+ }
2680
+ );
2681
+ }
2682
+ var FRAGMENT_SHADER11 = `
2683
+ precision highp float;
2684
+
2685
+ uniform vec2 uResolution;
2686
+ uniform float uTime;
2687
+ uniform vec2 uPointer;
2688
+ uniform vec3 uBase;
2689
+ uniform vec3 uHighlight;
2690
+ uniform float uFolds;
2691
+ uniform float uScheme;
2692
+
2693
+ float hash(vec2 p) {
2694
+ p = fract(p * vec2(123.34, 456.21));
2695
+ p += dot(p, p + 45.32);
2696
+ return fract(p.x * p.y);
2697
+ }
2698
+
2699
+ float silk(vec2 p, float t) {
2700
+ vec2 q = p;
2701
+ q.x += 0.55 * sin(q.y * 1.3 + t * 0.3);
2702
+ q.y += 0.45 * sin(q.x * 1.1 - t * 0.25);
2703
+ q.x += 0.2 * sin(q.y * 2.3 - t * 0.2);
2704
+ return sin(q.x * 1.6 + q.y * 0.9 + t * 0.2) * 0.5
2705
+ + sin(q.x * 0.7 - q.y * 1.9 - t * 0.15) * 0.35
2706
+ + sin((q.x + q.y) * 2.7 + t * 0.35) * 0.12;
2707
+ }
2708
+
2709
+ vec3 aces(vec3 x) {
2710
+ return clamp((x * (2.51 * x + 0.03)) / (x * (2.43 * x + 0.59) + 0.14), 0.0, 1.0);
2711
+ }
2712
+
2713
+ void main() {
2714
+ vec2 frag = vec2(gl_FragCoord.x, uResolution.y - gl_FragCoord.y);
2715
+ float minRes = min(uResolution.x, uResolution.y);
2716
+ vec2 p = frag / minRes * 2.6 * uFolds;
2717
+ float t = uTime;
2718
+
2719
+ float e = 0.01;
2720
+ float h = silk(p, t);
2721
+ vec2 grad = vec2(silk(p + vec2(e, 0.0), t) - silk(p - vec2(e, 0.0), t), silk(p + vec2(0.0, e), t) - silk(p - vec2(0.0, e), t)) / (2.0 * e);
2722
+ vec3 n = normalize(vec3(-grad * 0.55, 1.0));
2723
+
2724
+ vec3 V = vec3(0.0, 0.0, 1.0);
2725
+ vec3 toLight = vec3(uPointer - frag, minRes * 0.7);
2726
+ float dist = length(toLight);
2727
+ vec3 L = toLight / dist;
2728
+ vec3 H = normalize(L + V);
2729
+ float atten = 1.0 / (1.0 + 1.2 * pow(dist / minRes, 2.0));
2730
+ float NdotL = max(dot(n, L), 0.0);
2731
+ float NdotH = max(dot(n, H), 0.0);
2732
+ float NdotV = max(n.z, 0.0);
2733
+ float fres = pow(1.0 - NdotV, 3.0);
2734
+
2735
+ // \u8936\u76B1\u8C37\u5E95\u66F4\u6697
2736
+ float cavity = mix(0.35, 1.0, smoothstep(-1.0, 0.8, h));
2737
+
2738
+ vec3 color = uBase * (0.015 + 0.45 * NdotL * NdotL * atten) * cavity;
2739
+ color += mix(uBase, uHighlight, 0.5) * pow(NdotH, 14.0) * 0.45 * atten * cavity;
2740
+ color += uHighlight * pow(NdotH, 90.0) * 1.1 * atten;
2741
+
2742
+ // \u7EBF\u5F62\u706F\u5E26\u5728\u8936\u76B1\u4E0A\u5F62\u6210\u7684\u7F0E\u9762\u4EAE\u7EBF
2743
+ vec3 Rf = reflect(-V, n);
2744
+ vec2 toL = (uPointer - frag) / minRes;
2745
+ float a = -0.5 + 0.3 * sin(t * 0.13);
2746
+ vec2 dir = vec2(cos(a), sin(a));
2747
+ float s = dot(Rf.xy, dir) - dot(toL, dir) * 1.1;
2748
+ color += uHighlight * (exp(-s * s / 0.004) * 0.9 + exp(-s * s / 0.08) * 0.12) * (0.4 + atten) * cavity;
2749
+ color += mix(uBase, uHighlight, 0.3) * fres * 0.25 * cavity;
2750
+
2751
+ vec2 vc = frag / uResolution - 0.5;
2752
+ float vig = dot(vc, vc);
2753
+
2754
+ if (uScheme > 0.5) {
2755
+ // \u6D45\u8272\uFF1A\u5148\u8272\u8C03\u6620\u5C04\uFF0C\u518D\u505A\u4EAE\u6697\u955C\u50CF\uFF0C\u4FDD\u7559\u8272\u76F8 \u2014\u2014 \u6DF1\u5E95\u4EAE\u7EB9\u7FFB\u8F6C\u4E3A\u6D45\u5E95\u6DF1\u7EB9
2756
+ vec3 c = pow(aces(color * 1.1), vec3(1.0 / 2.2));
2757
+ float lum = dot(c, vec3(0.2126, 0.7152, 0.0722));
2758
+ color = clamp(c + (1.0 - 2.0 * lum), 0.0, 1.0) * (1.0 - vig * 0.35);
2759
+ } else {
2760
+ color *= 1.0 - vig * 1.2;
2761
+ color = pow(aces(color * 1.1), vec3(1.0 / 2.2));
2762
+ }
2763
+
2764
+ color += (hash(frag + fract(t)) - 0.5) / 255.0;
2765
+
2766
+ gl_FragColor = vec4(color, 1.0);
2767
+ }
2768
+ `;
2769
+ function SilkWaves({
2770
+ children,
2771
+ className,
2772
+ color = "#9a6424",
2773
+ highlightColor = "#ffe3b0",
2774
+ folds = 1,
2775
+ speed = 1,
2776
+ interactive = true
2777
+ }) {
2778
+ const scheme = useColorScheme();
2779
+ const canvasRef = useShaderCanvas({
2780
+ fragmentShader: FRAGMENT_SHADER11,
2781
+ speed,
2782
+ interactive,
2783
+ onDraw: ({ gl, uniform }) => {
2784
+ gl.uniform3fv(uniform("uBase"), hexToLinearRgb(color));
2785
+ gl.uniform3fv(uniform("uHighlight"), hexToLinearRgb(highlightColor));
2786
+ gl.uniform1f(uniform("uFolds"), Math.max(0.2, folds));
2787
+ gl.uniform1f(uniform("uScheme"), scheme === "light" ? 1 : 0);
2788
+ }
2789
+ });
2790
+ return /* @__PURE__ */ jsxs(
2791
+ "div",
2792
+ {
2793
+ className: cn("elyri-silk-waves", className),
2794
+ "data-scheme": scheme,
2795
+ style: { "--elyri-silk-waves-color": color },
2796
+ children: [
2797
+ /* @__PURE__ */ jsx("canvas", { ref: canvasRef, className: "elyri-silk-waves__canvas", "aria-hidden": "true" }),
2798
+ children != null && /* @__PURE__ */ jsx("div", { className: "elyri-silk-waves__content", children })
2799
+ ]
2800
+ }
2801
+ );
2802
+ }
2803
+ var TRAIL = 24;
2804
+ var TRAIL_STEP_MS = 40;
2805
+ var TRAIL_FADE_S = 1.6;
2806
+ var trailData = new Float32Array(TRAIL * 3);
2807
+ var FRAGMENT_SHADER12 = `
2808
+ precision highp float;
2809
+
2810
+ #define TRAIL ${TRAIL}
2811
+ #define PI 3.14159265
2812
+
2813
+ uniform vec2 uResolution;
2814
+ uniform float uTime;
2815
+ uniform vec2 uPointer;
2816
+ uniform vec3 uBase;
2817
+ uniform vec3 uSheen;
2818
+ uniform float uFolds;
2819
+ uniform float uFuzz;
2820
+ uniform float uVignette;
2821
+ uniform vec3 uTrail[TRAIL];
2822
+
2823
+ float hash(vec2 p) {
2824
+ p = fract(p * vec2(123.34, 456.21));
2825
+ p += dot(p, p + 45.32);
2826
+ return fract(p.x * p.y);
2827
+ }
2828
+
2829
+ float noise(vec2 p) {
2830
+ vec2 i = floor(p);
2831
+ vec2 f = fract(p);
2832
+ vec2 u = f * f * (3.0 - 2.0 * f);
2833
+ return mix(
2834
+ mix(hash(i), hash(i + vec2(1.0, 0.0)), u.x),
2835
+ mix(hash(i + vec2(0.0, 1.0)), hash(i + vec2(1.0, 1.0)), u.x),
2836
+ u.y
2837
+ );
2838
+ }
2839
+
2840
+ // \u5706\u6DA6\u7684\u8936\u80CC + \u6536\u7D27\u7684\u6298\u75D5\uFF1A|sin| \u5728\u96F6\u70B9\u5904\u5F62\u6210 V \u5F62\u8C37\uFF0Ck \u63A7\u5236\u8C37\u5E95\u9510\u5EA6
2841
+ float fold(float s, float k) {
2842
+ return sqrt(s * s + k);
2843
+ }
2844
+
2845
+ float drape(vec2 p, float t) {
2846
+ // \u8936\u76B1\u6CBF x \u5206\u5E03\u3001\u987A y \u5782\u4E0B\uFF0C\u5E76\u968F\u9AD8\u5EA6\u7F13\u6162\u6446\u52A8
2847
+ float sway = 0.28 * sin(p.y * 0.5 + t * 0.3) + 0.12 * sin(p.y * 1.15 - t * 0.23 + p.x * 0.3);
2848
+ float x = p.x + sway;
2849
+ float s1 = sin(x * 1.35 + 0.6 * sin(x * 0.37 + t * 0.08));
2850
+ float s2 = sin(x * 2.7 + p.y * 0.28 - t * 0.18 + 1.3);
2851
+ float h = 0.62 * fold(s1, 0.012) + 0.26 * fold(s2, 0.03);
2852
+ // \u8936\u80CC\u4E0A\u7684\u659C\u5411\u7EC6\u76B1
2853
+ h += 0.05 * sin(p.x * 4.0 - p.y * 5.5 + t * 0.25) * s1 * s1;
2854
+ h += 0.25 * noise(p * 0.5 + vec2(t * 0.04, -t * 0.03));
2855
+ return h;
2856
+ }
2857
+
2858
+ float pressAt(vec2 frag, float minRes) {
2859
+ float s = 0.0;
2860
+ for (int i = 0; i < TRAIL - 1; i++) {
2861
+ vec3 a = uTrail[i];
2862
+ vec3 b = uTrail[i + 1];
2863
+ float w = min(a.z, b.z);
2864
+ if (w <= 0.0) continue;
2865
+ vec2 pa = frag - a.xy;
2866
+ vec2 ba = b.xy - a.xy;
2867
+ float k = clamp(dot(pa, ba) / max(dot(ba, ba), 1e-3), 0.0, 1.0);
2868
+ vec2 d = (pa - ba * k) / minRes;
2869
+ s = max(s, w * exp(-dot(d, d) * 600.0));
2870
+ }
2871
+ return s;
2872
+ }
2873
+
2874
+ vec3 aces(vec3 x) {
2875
+ return clamp((x * (2.51 * x + 0.03)) / (x * (2.43 * x + 0.59) + 0.14), 0.0, 1.0);
2876
+ }
2877
+
2878
+ void main() {
2879
+ vec2 frag = vec2(gl_FragCoord.x, uResolution.y - gl_FragCoord.y);
2880
+ float minRes = min(uResolution.x, uResolution.y);
2881
+ vec2 uv = frag / minRes;
2882
+ float t = uTime;
2883
+
2884
+ // \u659C\u5411\u7EA6 16\xB0 \u7684\u5782\u5760
2885
+ mat2 rot = mat2(0.96, 0.28, -0.28, 0.96);
2886
+ vec2 p = rot * uv * 2.2 * uFolds;
2887
+
2888
+ // \u4E2D\u5FC3\u5DEE\u5206\u6C42\u6CD5\u7EBF\uFF0C\u6B65\u957F\u8DB3\u591F\u5C0F\u4EE5\u4FDD\u7559\u6298\u75D5\u7684\u9510\u5229\u8FB9\u7F18
2889
+ float e = 0.004;
2890
+ vec2 ex = vec2(e, 0.0);
2891
+ vec2 ey = vec2(0.0, e);
2892
+ float h = drape(p, t);
2893
+ vec2 grad = vec2(drape(p + ex, t) - drape(p - ex, t), drape(p + ey, t) - drape(p - ey, t)) / (2.0 * e);
2894
+ // \u68AF\u5EA6\u8F6C\u56DE\u5C4F\u5E55\u5750\u6807\u7CFB
2895
+ grad = grad * rot;
2896
+ vec3 n = normalize(vec3(-grad * 0.75, 1.0));
2897
+ float NdotV = max(n.z, 1e-3);
2898
+
2899
+ float pressed = pressAt(frag, minRes);
2900
+ // \u7ED2\u6BDB\uFF1A\u50CF\u7D20\u7EA7\u7684\u7EA4\u7EF4\u4EAE\u70B9\uFF0C\u6CBF\u5782\u5760\u65B9\u5411\u7565\u62C9\u957F\uFF1B\u5927\u5757\u788E\u7ED2\u660E\u6697\u4E0E\u4E4B\u540C\u6B65\u53D7 uFuzz \u63A7\u5236
2901
+ vec2 fp = rot * frag;
2902
+ float fiber = 0.55 * hash(floor(fp)) + 0.45 * hash(floor(vec2(fp.x * 0.5, fp.y * 0.2)) + 31.0);
2903
+ float nap = 1.0 + (noise(p * 0.9 + 3.1) - 0.5) * 0.7 * uFuzz;
2904
+ float fuzz = mix(1.0, 0.45 + 1.1 * fiber, uFuzz);
2905
+ float ao = mix(0.4, 1.0, smoothstep(0.1, 0.8, h));
2906
+
2907
+ // \u4E3B\u5149\uFF1A\u5DE6\u4E0A\u65B9\u56FA\u5B9A\u67D4\u5149\uFF0C\u4FDD\u8BC1\u5F62\u4F53\u7A33\u5B9A\u53EF\u8BFB\uFF1B\u8F85\u5149\uFF1A\u8DDF\u968F\u6307\u9488\u7684\u70B9\u5149
2908
+ vec2 ptr = uPointer / minRes;
2909
+ vec2 toPtr = ptr - uv;
2910
+ vec3 lights[2];
2911
+ lights[0] = normalize(vec3(-0.45, -0.55, 0.7));
2912
+ lights[1] = normalize(vec3(toPtr, 0.55));
2913
+ float power[2];
2914
+ power[0] = 1.5;
2915
+ power[1] = 1.6 / (1.0 + 3.0 * dot(toPtr, toPtr));
2916
+
2917
+ // Charlie \u7ED2\u9762\u5206\u5E03\uFF1A\u6B63\u5BF9\u5149\u5904\u5438\u5149\uFF0C\u63A0\u5C04\u89D2\u6563\u5C04\u51FA\u67D4\u5149
2918
+ const float INV_A = 1.0 / 0.38;
2919
+ vec3 color = uBase * 0.03 * ao;
2920
+ for (int i = 0; i < 2; i++) {
2921
+ vec3 L = lights[i];
2922
+ vec3 H = normalize(L + vec3(0.0, 0.0, 1.0));
2923
+ float NdotL = max(dot(n, L), 0.0);
2924
+ float NdotH = max(dot(n, H), 0.0);
2925
+ float sin2 = max(1.0 - NdotH * NdotH, 0.0078125);
2926
+ float D = (2.0 + INV_A) * pow(sin2, INV_A * 0.5) / (2.0 * PI);
2927
+ float vis = 1.0 / (4.0 * (NdotL + NdotV - NdotL * NdotV) + 1e-3);
2928
+ vec3 diffuse = uBase * (0.08 + 0.55 * NdotL) * ao;
2929
+ vec3 sheen = uSheen * D * vis * NdotL * nap * mix(fuzz, 1.0, pressed) * (1.0 - 0.85 * pressed);
2930
+ // \u538B\u5012\u7684\u7ED2\u6BDB\uFF1A\u987A\u6BDB\u65B9\u5411\u51FA\u73B0\u4E00\u70B9\u7F0E\u9762\u822C\u7684\u5149\u6CFD
2931
+ vec3 gloss = mix(uBase, uSheen, 0.6) * pow(NdotH, 40.0) * 0.35 * pressed;
2932
+ color += (diffuse * (1.0 - 0.3 * pressed) + sheen + gloss) * power[i];
2933
+ }
2934
+
2935
+ vec2 vc = frag / uResolution - 0.5;
2936
+ color *= max(1.0 - dot(vc, vc) * 1.2 * uVignette, 0.0);
2937
+ color = pow(aces(color * 1.2), vec3(1.0 / 2.2));
2938
+ color += (hash(frag + fract(t)) - 0.5) / 255.0;
2939
+
2940
+ gl_FragColor = vec4(color, 1.0);
2941
+ }
2942
+ `;
2943
+ function Velvet({
2944
+ children,
2945
+ className,
2946
+ color = "#6a1230",
2947
+ sheenColor = "#ffb3c8",
2948
+ folds = 1,
2949
+ fuzz = 0.4,
2950
+ vignette = 0.5,
2951
+ speed = 1,
2952
+ interactive = true
2953
+ }) {
2954
+ const trailRef = useRef([]);
2955
+ const canvasRef = useShaderCanvas({
2956
+ fragmentShader: FRAGMENT_SHADER12,
2957
+ speed,
2958
+ interactive,
2959
+ onDraw: ({ gl, uniform, dpr }) => {
2960
+ const now = performance.now();
2961
+ trailData.fill(0);
2962
+ trailRef.current.forEach((point, i) => {
2963
+ const life = Math.max(0, 1 - (now - point.t) / 1e3 / TRAIL_FADE_S);
2964
+ trailData[i * 3] = point.x * dpr;
2965
+ trailData[i * 3 + 1] = point.y * dpr;
2966
+ trailData[i * 3 + 2] = life * life;
2967
+ });
2968
+ gl.uniform3fv(uniform("uBase"), hexToLinearRgb(color));
2969
+ gl.uniform3fv(uniform("uSheen"), hexToLinearRgb(sheenColor));
2970
+ gl.uniform1f(uniform("uFolds"), Math.max(0.2, folds));
2971
+ gl.uniform1f(uniform("uFuzz"), Math.min(1, Math.max(0, fuzz)));
2972
+ gl.uniform1f(uniform("uVignette"), Math.min(1, Math.max(0, vignette)));
2973
+ gl.uniform3fv(uniform("uTrail"), trailData);
2974
+ }
2975
+ });
2976
+ useEffect(() => {
2977
+ trailRef.current = [];
2978
+ if (!interactive) return;
2979
+ const handlePointerMove = (event) => {
2980
+ const rect = canvasRef.current?.getBoundingClientRect();
2981
+ if (!rect) return;
2982
+ const x = event.clientX - rect.left;
2983
+ const y = event.clientY - rect.top;
2984
+ const trail = trailRef.current;
2985
+ const now = performance.now();
2986
+ const last = trail[trail.length - 1];
2987
+ if (last && now - last.t < TRAIL_STEP_MS) {
2988
+ last.x = x;
2989
+ last.y = y;
2990
+ return;
2991
+ }
2992
+ trail.push({ x, y, t: now });
2993
+ if (trail.length > TRAIL) trail.shift();
2994
+ };
2995
+ window.addEventListener("pointermove", handlePointerMove, { passive: true });
2996
+ return () => window.removeEventListener("pointermove", handlePointerMove);
2997
+ }, [interactive, canvasRef]);
2998
+ return /* @__PURE__ */ jsxs("div", { className: cn("elyri-velvet", className), style: { "--elyri-velvet-color": color }, children: [
2999
+ /* @__PURE__ */ jsx("canvas", { ref: canvasRef, className: "elyri-velvet__canvas", "aria-hidden": "true" }),
3000
+ children != null && /* @__PURE__ */ jsx("div", { className: "elyri-velvet__content", children })
3001
+ ] });
3002
+ }
3003
+ var DEFAULT_CHARACTERS = " .:-=+*#%@";
3004
+ var SCRAMBLE_WINDOW = 0.08;
3005
+ var FRAME_INTERVAL = 1e3 / 40;
3006
+ var SHIMMER_PERIOD = 4.5;
3007
+ var SHIMMER_WIDTH = 0.07;
3008
+ function AsciiImage({
3009
+ src,
3010
+ alt = "",
3011
+ className,
3012
+ columns = 100,
3013
+ characters = DEFAULT_CHARACTERS,
3014
+ colored = false,
3015
+ color,
3016
+ invert = false,
3017
+ animated = true,
3018
+ duration = 1200,
3019
+ shimmer = true,
3020
+ flicker = 0.01,
3021
+ interactive = true,
3022
+ radius = 90
3023
+ }) {
3024
+ const rootRef = useRef(null);
3025
+ const canvasRef = useRef(null);
3026
+ const prefersReducedMotion = usePrefersReducedMotion();
3027
+ const scheme = useColorScheme();
3028
+ const [image, setImage] = useState(null);
3029
+ const [failed, setFailed] = useState(false);
3030
+ const [width, setWidth] = useState(0);
3031
+ const [inView, setInView] = useState(false);
3032
+ const playedRef = useRef(false);
3033
+ useEffect(() => {
3034
+ let disposed = false;
3035
+ const img = new Image();
3036
+ img.crossOrigin = "anonymous";
3037
+ img.decoding = "async";
3038
+ img.onload = () => {
3039
+ if (disposed) return;
3040
+ const probe = document.createElement("canvas").getContext("2d");
3041
+ try {
3042
+ probe?.drawImage(img, 0, 0, 1, 1);
3043
+ probe?.getImageData(0, 0, 1, 1);
3044
+ } catch {
3045
+ setFailed(true);
3046
+ return;
3047
+ }
3048
+ setFailed(false);
3049
+ setImage(img);
3050
+ };
3051
+ img.onerror = () => {
3052
+ if (!disposed) setFailed(true);
3053
+ };
3054
+ img.src = src;
3055
+ return () => {
3056
+ disposed = true;
3057
+ };
3058
+ }, [src]);
3059
+ useEffect(() => {
3060
+ const root = rootRef.current;
3061
+ if (!root) return;
3062
+ const resize = new ResizeObserver(([entry]) => setWidth(Math.round(entry.contentRect.width)));
3063
+ const intersection = new IntersectionObserver(([entry]) => setInView(entry.isIntersecting));
3064
+ resize.observe(root);
3065
+ intersection.observe(root);
3066
+ return () => {
3067
+ resize.disconnect();
3068
+ intersection.disconnect();
3069
+ };
3070
+ }, []);
3071
+ useEffect(() => {
3072
+ const root = rootRef.current;
3073
+ const canvas = canvasRef.current;
3074
+ const ctx = canvas?.getContext("2d");
3075
+ if (!root || !canvas || !ctx || !image || !width || !image.naturalWidth) return;
3076
+ const style2 = getComputedStyle(root);
3077
+ const ramp = Array.from(characters || DEFAULT_CHARACTERS);
3078
+ const cols = Math.min(400, Math.max(8, Math.round(columns)));
3079
+ const font = (size) => `${style2.fontWeight} ${size}px ${style2.fontFamily}`;
3080
+ ctx.font = font(100);
3081
+ const cellW = width / cols;
3082
+ const fontSize = cellW * 100 / ctx.measureText("M").width;
3083
+ const cellH = fontSize;
3084
+ const rows = Math.max(1, Math.round(cols * image.naturalHeight / image.naturalWidth * (cellW / cellH)));
3085
+ const height = rows * cellH;
3086
+ const sampler = document.createElement("canvas");
3087
+ sampler.width = cols;
3088
+ sampler.height = rows;
3089
+ const sctx = sampler.getContext("2d", { willReadFrequently: true });
3090
+ if (!sctx) return;
3091
+ sctx.imageSmoothingQuality = "high";
3092
+ sctx.drawImage(image, 0, 0, cols, rows);
3093
+ let data;
3094
+ try {
3095
+ data = sctx.getImageData(0, 0, cols, rows).data;
3096
+ } catch {
3097
+ return;
3098
+ }
3099
+ const light = scheme === "light";
3100
+ const flip = light !== invert;
3101
+ const cells = [];
3102
+ for (let i = 0; i < cols * rows; i++) {
3103
+ const r = data[i * 4];
3104
+ const g = data[i * 4 + 1];
3105
+ const b = data[i * 4 + 2];
3106
+ const a = data[i * 4 + 3] / 255;
3107
+ const lum = (0.2126 * r + 0.7152 * g + 0.0722 * b) / 255;
3108
+ const glyph = Math.min(ramp.length - 1, Math.floor((flip ? 1 - lum : lum) * a * ramp.length));
3109
+ if (!ramp[glyph].trim()) continue;
3110
+ const col = i % cols;
3111
+ const row = Math.floor(i / cols);
3112
+ cells.push({
3113
+ x: col * cellW,
3114
+ y: row * cellH,
3115
+ glyph,
3116
+ r,
3117
+ g,
3118
+ b,
3119
+ // 自上而下显现,并带少许随机错落
3120
+ threshold: row / rows * (1 - SCRAMBLE_WINDOW - 0.2) + Math.random() * 0.2,
3121
+ diagonal: (col / cols + row / rows) / 2
3122
+ });
3123
+ }
3124
+ const dpr = Math.min(window.devicePixelRatio || 1, 2);
3125
+ canvas.width = Math.round(width * dpr);
3126
+ canvas.height = Math.round(height * dpr);
3127
+ canvas.style.height = `${height}px`;
3128
+ const noise = ramp.filter((char) => char.trim());
3129
+ const fill = style2.getPropertyValue("--elyri-ascii-color").trim() || style2.color;
3130
+ const highlight = light ? 0 : 255;
3131
+ const lift = Math.min(3, ramp.length - 1);
3132
+ const flickerRate = Math.min(1, Math.max(0, flicker));
3133
+ const reach = Math.max(1, radius);
3134
+ const motion = !prefersReducedMotion;
3135
+ const lens = motion && interactive;
3136
+ const pointer = { x: 0, y: 0, strength: 0, target: 0, tx: 0, ty: 0 };
3137
+ const draw = (progress, time) => {
3138
+ ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
3139
+ ctx.clearRect(0, 0, width, height);
3140
+ ctx.font = font(fontSize);
3141
+ ctx.textBaseline = "top";
3142
+ ctx.fillStyle = fill;
3143
+ const band = motion && shimmer ? time / SHIMMER_PERIOD % 1 * 1.8 - 0.4 : -10;
3144
+ for (const cell of cells) {
3145
+ let char = ramp[cell.glyph];
3146
+ if (progress < 1) {
3147
+ if (progress < cell.threshold) continue;
3148
+ if (progress < cell.threshold + SCRAMBLE_WINDOW) char = noise[Math.floor(Math.random() * noise.length)];
3149
+ }
3150
+ const offset = (cell.diagonal - band) / SHIMMER_WIDTH;
3151
+ let boost = offset > -3 && offset < 3 ? Math.exp(-offset * offset) : 0;
3152
+ let x = cell.x;
3153
+ let y = cell.y;
3154
+ if (pointer.strength > 0.01) {
3155
+ const dx = x + cellW / 2 - pointer.x;
3156
+ const dy = y + cellH / 2 - pointer.y;
3157
+ const d = Math.hypot(dx, dy);
3158
+ if (d < reach) {
3159
+ const t = (1 - d / reach) * pointer.strength;
3160
+ const push = t * t * cellW * 2.2;
3161
+ if (d > 0.01) {
3162
+ x += dx / d * push;
3163
+ y += dy / d * push;
3164
+ }
3165
+ boost = Math.max(boost, t);
3166
+ if (Math.random() < t * 0.35) char = noise[Math.floor(Math.random() * noise.length)];
3167
+ }
3168
+ }
3169
+ if (boost > 0.05) char = ramp[Math.min(ramp.length - 1, cell.glyph + Math.round(boost * lift))];
3170
+ if (flickerRate && Math.random() < flickerRate) char = noise[Math.floor(Math.random() * noise.length)];
3171
+ ctx.globalAlpha = 0.82 + 0.18 * boost;
3172
+ if (colored) {
3173
+ const mix = boost * 0.6;
3174
+ ctx.fillStyle = `rgb(${cell.r + (highlight - cell.r) * mix},${cell.g + (highlight - cell.g) * mix},${cell.b + (highlight - cell.b) * mix})`;
3175
+ }
3176
+ ctx.fillText(char, x, y);
3177
+ }
3178
+ ctx.globalAlpha = 1;
3179
+ };
3180
+ if (!motion) {
3181
+ draw(1, 0);
3182
+ return;
3183
+ }
3184
+ const reveal = animated && !playedRef.current;
3185
+ if (!inView) {
3186
+ draw(reveal ? 0 : 1, 0);
3187
+ return;
3188
+ }
3189
+ const continuous = shimmer || flickerRate > 0 || lens;
3190
+ const start = performance.now();
3191
+ let frame = 0;
3192
+ let last = 0;
3193
+ const tick = (now) => {
3194
+ frame = requestAnimationFrame(tick);
3195
+ if (now - last < FRAME_INTERVAL) return;
3196
+ const dt = last ? Math.min((now - last) / 1e3, 0.1) : 0;
3197
+ last = now;
3198
+ const progress = reveal ? Math.min(1, (now - start) / Math.max(1, duration)) : 1;
3199
+ if (progress >= 1) playedRef.current = true;
3200
+ const ease = 1 - Math.exp(-dt * 12);
3201
+ pointer.x += (pointer.tx - pointer.x) * ease;
3202
+ pointer.y += (pointer.ty - pointer.y) * ease;
3203
+ pointer.strength += (pointer.target - pointer.strength) * (1 - Math.exp(-dt * 6));
3204
+ draw(progress, (now - start) / 1e3);
3205
+ if (!continuous && progress >= 1) cancelAnimationFrame(frame);
3206
+ };
3207
+ frame = requestAnimationFrame(tick);
3208
+ const handlePointerMove = (event) => {
3209
+ const rect = canvas.getBoundingClientRect();
3210
+ const x = event.clientX - rect.left;
3211
+ const y = event.clientY - rect.top;
3212
+ const inside = x >= -reach && y >= -reach && x <= rect.width + reach && y <= rect.height + reach;
3213
+ if (inside && pointer.target === 0 && pointer.strength < 0.01) {
3214
+ pointer.x = x;
3215
+ pointer.y = y;
3216
+ }
3217
+ pointer.tx = x;
3218
+ pointer.ty = y;
3219
+ pointer.target = inside ? 1 : 0;
3220
+ };
3221
+ const handlePointerLeave = () => {
3222
+ pointer.target = 0;
3223
+ };
3224
+ if (lens) {
3225
+ window.addEventListener("pointermove", handlePointerMove, { passive: true });
3226
+ document.documentElement.addEventListener("pointerleave", handlePointerLeave);
3227
+ }
3228
+ return () => {
3229
+ cancelAnimationFrame(frame);
3230
+ window.removeEventListener("pointermove", handlePointerMove);
3231
+ document.documentElement.removeEventListener("pointerleave", handlePointerLeave);
3232
+ };
3233
+ }, [
3234
+ image,
3235
+ width,
3236
+ columns,
3237
+ characters,
3238
+ colored,
3239
+ color,
3240
+ invert,
3241
+ scheme,
3242
+ animated,
3243
+ duration,
3244
+ shimmer,
3245
+ flicker,
3246
+ interactive,
3247
+ radius,
3248
+ inView,
3249
+ prefersReducedMotion
3250
+ ]);
3251
+ const style = color ? { "--elyri-ascii-color": color } : void 0;
3252
+ return /* @__PURE__ */ jsx("div", { ref: rootRef, className: cn("elyri-ascii-image", className), style, children: failed ? /* @__PURE__ */ jsx("img", { className: "elyri-ascii-image__fallback", src, alt }) : /* @__PURE__ */ jsx(
3253
+ "canvas",
3254
+ {
3255
+ ref: canvasRef,
3256
+ className: "elyri-ascii-image__canvas",
3257
+ role: alt ? "img" : void 0,
3258
+ "aria-label": alt || void 0,
3259
+ "aria-hidden": alt ? void 0 : true
3260
+ }
3261
+ ) });
3262
+ }
3263
+ function GradientText({ children, className, colors, animated = true, speed = 0.5 }) {
3264
+ const style = {
3265
+ ...colors?.length ? { "--elyri-gradient-colors": colors.join(", ") } : null,
3266
+ "--elyri-gradient-speed": speed
3267
+ };
3268
+ return /* @__PURE__ */ jsx("span", { className: cn("elyri-gradient-text", animated && "elyri-gradient-text--animated", className), style, children });
3269
+ }
3270
+ function Marquee({
3271
+ children,
3272
+ className,
3273
+ direction = "left",
3274
+ duration = 20,
3275
+ repeat = 2,
3276
+ gap = 0,
3277
+ infinite = true,
3278
+ pauseOnHover = true
3279
+ }) {
3280
+ const rootRef = useRef(null);
3281
+ const trackRef = useRef(null);
3282
+ const [paused, setPaused] = useState(false);
3283
+ const prefersReducedMotion = usePrefersReducedMotion();
3284
+ const minCopies = Math.max(2, Math.floor(repeat));
3285
+ const [copies, setCopies] = useState(minCopies);
3286
+ const vertical = direction === "up" || direction === "down";
3287
+ const reverse = direction === "right" || direction === "down";
3288
+ useEffect(() => {
3289
+ const root = rootRef.current;
3290
+ const track = trackRef.current;
3291
+ if (!root || !track) return;
3292
+ const measure = () => {
3293
+ const first = track.firstElementChild;
3294
+ if (!first) return;
3295
+ const copySize = vertical ? first.offsetHeight : first.offsetWidth;
3296
+ const rootSize = vertical ? root.clientHeight : root.clientWidth;
3297
+ const trackSize = vertical ? track.offsetHeight : track.offsetWidth;
3298
+ if (!copySize) return;
3299
+ if (vertical && rootSize >= trackSize) {
3300
+ setCopies(minCopies);
3301
+ return;
3302
+ }
3303
+ setCopies(Math.max(minCopies, Math.ceil((rootSize + copySize) / copySize)));
3304
+ };
3305
+ const observer = new ResizeObserver(measure);
3306
+ observer.observe(root);
3307
+ if (track.firstElementChild) observer.observe(track.firstElementChild);
3308
+ return () => observer.disconnect();
3309
+ }, [vertical, minCopies]);
3310
+ useEffect(() => {
3311
+ const node = rootRef.current;
3312
+ if (!node || prefersReducedMotion) return;
3313
+ let inView = true;
3314
+ const sync = () => setPaused(!inView || document.hidden);
3315
+ const observer = new IntersectionObserver(([entry]) => {
3316
+ inView = entry.isIntersecting;
3317
+ sync();
3318
+ });
3319
+ observer.observe(node);
3320
+ document.addEventListener("visibilitychange", sync);
3321
+ return () => {
3322
+ observer.disconnect();
3323
+ document.removeEventListener("visibilitychange", sync);
3324
+ };
3325
+ }, [prefersReducedMotion]);
3326
+ return /* @__PURE__ */ jsx(
3327
+ "div",
3328
+ {
3329
+ ref: rootRef,
3330
+ className: cn(
3331
+ "elyri-marquee",
3332
+ vertical && "elyri-marquee--vertical",
3333
+ reverse && "elyri-marquee--reverse",
3334
+ !infinite && "elyri-marquee--once",
3335
+ paused && "elyri-marquee--paused",
3336
+ pauseOnHover && "elyri-marquee--pausable",
3337
+ prefersReducedMotion && "elyri-marquee--static",
3338
+ className
3339
+ ),
3340
+ style: {
3341
+ "--elyri-marquee-duration": `${duration}s`,
3342
+ "--elyri-marquee-repeat": copies,
3343
+ "--elyri-marquee-gap": `${gap}px`
3344
+ },
3345
+ children: /* @__PURE__ */ jsx("div", { ref: trackRef, className: "elyri-marquee__track", children: Array.from({ length: copies }, (_, index) => (
3346
+ // 复制份仅用于视觉衔接,对辅助技术隐藏,避免重复朗读
3347
+ /* @__PURE__ */ jsx("div", { className: "elyri-marquee__copy", "aria-hidden": index > 0 ? true : void 0, children }, index)
3348
+ )) })
3349
+ }
3350
+ );
3351
+ }
3352
+ var FRICTION = 0.86;
3353
+ function ParticleText({
3354
+ children,
3355
+ className,
3356
+ color,
3357
+ particleSize = 2,
3358
+ gap = 3,
3359
+ radius = 80,
3360
+ force = 1,
3361
+ assemble = true
3362
+ }) {
3363
+ const rootRef = useRef(null);
3364
+ const canvasRef = useRef(null);
3365
+ const prefersReducedMotion = usePrefersReducedMotion();
3366
+ const scheme = useColorScheme();
3367
+ const [ready, setReady] = useState(false);
3368
+ const particlesRef = useRef([]);
3369
+ const padRef = useRef(0);
3370
+ const optionsRef = useRef({ particleSize, force });
3371
+ const wakeRef = useRef(null);
3372
+ useEffect(() => {
3373
+ optionsRef.current = { particleSize, force };
3374
+ wakeRef.current?.();
3375
+ }, [particleSize, force, color, scheme]);
3376
+ useEffect(() => {
3377
+ const root = rootRef.current;
3378
+ const canvas = canvasRef.current;
3379
+ const ctx = canvas?.getContext("2d");
3380
+ if (!root || !canvas || !ctx) return;
3381
+ const style2 = getComputedStyle(root);
3382
+ const interactive = radius > 0 && !prefersReducedMotion;
3383
+ const pointer = { x: 0, y: 0, active: false };
3384
+ let width = 0;
3385
+ let height = 0;
3386
+ let dpr = 1;
3387
+ let visible = false;
3388
+ let running = false;
3389
+ let frame = 0;
3390
+ let buildFrame = 0;
3391
+ let last = 0;
3392
+ let lastColor = "";
3393
+ const build = () => {
3394
+ const w = root.offsetWidth;
3395
+ const h = root.offsetHeight;
3396
+ if (!w || !h) return;
3397
+ const pad = Math.ceil(Math.max(radius, 0) + 16);
3398
+ dpr = Math.min(window.devicePixelRatio || 1, 2);
3399
+ width = w + pad * 2;
3400
+ height = h + pad * 2;
3401
+ canvas.width = Math.round(width * dpr);
3402
+ canvas.height = Math.round(height * dpr);
3403
+ canvas.style.width = `${width}px`;
3404
+ canvas.style.height = `${height}px`;
3405
+ canvas.style.left = canvas.style.top = `${-pad}px`;
3406
+ const sampler = document.createElement("canvas");
3407
+ sampler.width = Math.ceil(w);
3408
+ sampler.height = Math.ceil(h);
3409
+ const sctx = sampler.getContext("2d", { willReadFrequently: true });
3410
+ if (!sctx) return;
3411
+ sctx.font = `${style2.fontStyle} ${style2.fontWeight} ${style2.fontSize} ${style2.fontFamily}`;
3412
+ if ("letterSpacing" in sctx) sctx.letterSpacing = style2.letterSpacing === "normal" ? "0px" : style2.letterSpacing;
3413
+ const align = style2.textAlign;
3414
+ const alignRight = align === "right" || align === "end";
3415
+ sctx.textAlign = align === "center" ? "center" : alignRight ? "right" : "left";
3416
+ const originX = align === "center" ? w / 2 : alignRight ? w : 0;
3417
+ const lines = children.split("\n");
3418
+ const lineHeight = h / lines.length;
3419
+ lines.forEach((line, index) => {
3420
+ const m = sctx.measureText(line);
3421
+ const ascent = m.fontBoundingBoxAscent ?? m.actualBoundingBoxAscent;
3422
+ const descent = m.fontBoundingBoxDescent ?? m.actualBoundingBoxDescent;
3423
+ sctx.fillText(line, originX, index * lineHeight + (lineHeight - ascent - descent) / 2 + ascent);
3424
+ });
3425
+ const data = sctx.getImageData(0, 0, sampler.width, sampler.height).data;
3426
+ const step = Math.max(1, gap);
3427
+ const targets = [];
3428
+ for (let y = step / 2; y < sampler.height; y += step) {
3429
+ for (let x = step / 2; x < sampler.width; x += step) {
3430
+ if (data[(Math.floor(y) * sampler.width + Math.floor(x)) * 4 + 3] > 128) targets.push([x + pad, y + pad]);
3431
+ }
3432
+ }
3433
+ for (let i = targets.length - 1; i > 0; i--) {
3434
+ const j = Math.floor(Math.random() * (i + 1));
3435
+ [targets[i], targets[j]] = [targets[j], targets[i]];
3436
+ }
3437
+ const prev = particlesRef.current;
3438
+ const shift = pad - padRef.current;
3439
+ const scatter = assemble && !prefersReducedMotion && prev.length === 0;
3440
+ particlesRef.current = targets.map(([tx, ty], index) => {
3441
+ const k = 0.03 + Math.random() * 0.04;
3442
+ if (prefersReducedMotion) return { x: tx, y: ty, vx: 0, vy: 0, tx, ty, k };
3443
+ const source = prev[index] ?? (prev.length ? prev[Math.floor(Math.random() * prev.length)] : null);
3444
+ if (source) return { x: source.x + shift, y: source.y + shift, vx: source.vx, vy: source.vy, tx, ty, k };
3445
+ if (scatter) return { x: Math.random() * width, y: Math.random() * height, vx: 0, vy: 0, tx, ty, k };
3446
+ return { x: tx, y: ty, vx: 0, vy: 0, tx, ty, k };
3447
+ });
3448
+ padRef.current = pad;
3449
+ lastColor = "";
3450
+ setReady(true);
3451
+ wake();
3452
+ };
3453
+ const tick = (now) => {
3454
+ const dt = Math.min((now - last) / 16.667, 3);
3455
+ last = now;
3456
+ const { particleSize: size, force: strength } = optionsRef.current;
3457
+ const damping = Math.pow(FRICTION, dt);
3458
+ const r2 = radius * radius;
3459
+ const push = Math.max(0, strength) * 3 * dt;
3460
+ let active = false;
3461
+ for (const p of particlesRef.current) {
3462
+ p.vx += (p.tx - p.x) * p.k * dt;
3463
+ p.vy += (p.ty - p.y) * p.k * dt;
3464
+ if (pointer.active) {
3465
+ const dx = p.x - pointer.x;
3466
+ const dy = p.y - pointer.y;
3467
+ const d2 = dx * dx + dy * dy;
3468
+ if (d2 < r2 && d2 > 0.01) {
3469
+ const d = Math.sqrt(d2);
3470
+ const f = (1 - d / radius) * push;
3471
+ p.vx += dx / d * f;
3472
+ p.vy += dy / d * f;
3473
+ }
3474
+ }
3475
+ p.vx *= damping;
3476
+ p.vy *= damping;
3477
+ p.x += p.vx * dt;
3478
+ p.y += p.vy * dt;
3479
+ if (!active && Math.abs(p.vx) + Math.abs(p.vy) + Math.abs(p.tx - p.x) + Math.abs(p.ty - p.y) > 0.05) {
3480
+ active = true;
3481
+ }
3482
+ }
3483
+ const fill = style2.getPropertyValue("--elyri-particle-color").trim() || style2.color;
3484
+ if (fill !== lastColor) {
3485
+ lastColor = fill;
3486
+ active = true;
3487
+ }
3488
+ ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
3489
+ ctx.clearRect(0, 0, width, height);
3490
+ ctx.fillStyle = fill;
3491
+ const half = size / 2;
3492
+ for (const p of particlesRef.current) ctx.fillRect(p.x - half, p.y - half, size, size);
3493
+ if (visible && (active || pointer.active)) {
3494
+ frame = requestAnimationFrame(tick);
3495
+ } else {
3496
+ running = false;
3497
+ }
3498
+ };
3499
+ function wake() {
3500
+ if (running || !visible) return;
3501
+ running = true;
3502
+ last = performance.now();
3503
+ frame = requestAnimationFrame(tick);
3504
+ }
3505
+ wakeRef.current = wake;
3506
+ const scheduleBuild = () => {
3507
+ cancelAnimationFrame(buildFrame);
3508
+ buildFrame = requestAnimationFrame(build);
3509
+ };
3510
+ const handlePointerMove = (event) => {
3511
+ const rect = canvas.getBoundingClientRect();
3512
+ const x = event.clientX - rect.left;
3513
+ const y = event.clientY - rect.top;
3514
+ const inside = x >= 0 && y >= 0 && x <= rect.width && y <= rect.height;
3515
+ if (!inside && !pointer.active) return;
3516
+ pointer.x = x;
3517
+ pointer.y = y;
3518
+ pointer.active = inside;
3519
+ wake();
3520
+ };
3521
+ const handlePointerLeave = () => {
3522
+ pointer.active = false;
3523
+ };
3524
+ const intersection = new IntersectionObserver(([entry]) => {
3525
+ visible = entry.isIntersecting;
3526
+ if (visible) wake();
3527
+ });
3528
+ const resize = new ResizeObserver(scheduleBuild);
3529
+ intersection.observe(root);
3530
+ resize.observe(root);
3531
+ document.fonts?.addEventListener("loadingdone", scheduleBuild);
3532
+ if (interactive) {
3533
+ window.addEventListener("pointermove", handlePointerMove, { passive: true });
3534
+ document.documentElement.addEventListener("pointerleave", handlePointerLeave);
3535
+ }
3536
+ build();
3537
+ return () => {
3538
+ cancelAnimationFrame(frame);
3539
+ cancelAnimationFrame(buildFrame);
3540
+ intersection.disconnect();
3541
+ resize.disconnect();
3542
+ document.fonts?.removeEventListener("loadingdone", scheduleBuild);
3543
+ window.removeEventListener("pointermove", handlePointerMove);
3544
+ document.documentElement.removeEventListener("pointerleave", handlePointerLeave);
3545
+ wakeRef.current = null;
3546
+ };
3547
+ }, [children, gap, radius, assemble, prefersReducedMotion]);
3548
+ const style = color ? { "--elyri-particle-color": color } : void 0;
3549
+ return /* @__PURE__ */ jsxs(
3550
+ "span",
3551
+ {
3552
+ ref: rootRef,
3553
+ className: cn("elyri-particle-text", ready && "elyri-particle-text--ready", className),
3554
+ style,
3555
+ children: [
3556
+ /* @__PURE__ */ jsx("span", { className: "elyri-particle-text__label", children }),
3557
+ /* @__PURE__ */ jsx("canvas", { ref: canvasRef, className: "elyri-particle-text__canvas", "aria-hidden": "true" })
3558
+ ]
3559
+ }
3560
+ );
3561
+ }
3562
+ var CHAR_STAGGER = 30;
3563
+ function RotatingText({
3564
+ words,
3565
+ className,
3566
+ interval = 2500,
3567
+ duration = 500,
3568
+ variant = "flip"
3569
+ }) {
3570
+ const [index, setIndex] = useState(0);
3571
+ const [leaving, setLeaving] = useState(null);
3572
+ const [width, setWidth] = useState();
3573
+ const wordRef = useRef(null);
3574
+ const prefersReducedMotion = usePrefersReducedMotion();
3575
+ const [prevWords, setPrevWords] = useState(words);
3576
+ if (prevWords !== words) {
3577
+ setPrevWords(words);
3578
+ setIndex(0);
3579
+ setLeaving(null);
3580
+ }
3581
+ const count = words.length;
3582
+ const current = words[index] ?? words[0] ?? "";
3583
+ useEffect(() => {
3584
+ if (count < 2) return;
3585
+ const timer = window.setInterval(() => {
3586
+ setLeaving(prefersReducedMotion ? null : current);
3587
+ setIndex((value) => (value + 1) % count);
3588
+ }, interval);
3589
+ return () => window.clearInterval(timer);
3590
+ }, [count, current, interval, prefersReducedMotion]);
3591
+ useEffect(() => {
3592
+ if (leaving == null) return;
3593
+ const timer = window.setTimeout(() => setLeaving(null), duration + CHAR_STAGGER * leaving.length);
3594
+ return () => window.clearTimeout(timer);
3595
+ }, [leaving, duration]);
3596
+ useLayoutEffect(() => {
3597
+ const word = wordRef.current;
3598
+ if (word) setWidth(word.offsetWidth);
3599
+ }, [current]);
3600
+ const style = {
3601
+ "--elyri-rotating-text-duration": `${duration}ms`,
3602
+ width: width != null ? `${width}px` : void 0
3603
+ };
3604
+ const renderChars = (word) => Array.from(word).map((char, charIndex) => /* @__PURE__ */ jsx(
3605
+ "span",
3606
+ {
3607
+ className: "elyri-rotating-text__char",
3608
+ style: {
3609
+ "--elyri-rotating-text-index": charIndex,
3610
+ "--elyri-rotating-text-stagger": `${CHAR_STAGGER}ms`
3611
+ },
3612
+ "aria-hidden": "true",
3613
+ children: char === " " ? "\xA0" : char
3614
+ },
3615
+ `${char}-${charIndex}`
3616
+ ));
3617
+ return /* @__PURE__ */ jsxs(
3618
+ "span",
3619
+ {
3620
+ className: cn("elyri-rotating-text", variant !== "flip" && `elyri-rotating-text--${variant}`, className),
3621
+ style,
3622
+ "aria-label": current,
3623
+ children: [
3624
+ /* @__PURE__ */ jsx("span", { ref: wordRef, className: "elyri-rotating-text__word elyri-rotating-text__word--entering", children: renderChars(current) }),
3625
+ leaving != null && /* @__PURE__ */ jsx("span", { className: "elyri-rotating-text__word elyri-rotating-text__word--leaving", "aria-hidden": "true", children: renderChars(leaving) })
3626
+ ]
3627
+ }
3628
+ );
3629
+ }
3630
+ var DEFAULT_CHARACTERS2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!<>-_\\/[]{}=+*^?#";
3631
+ var isSpace = (char) => /\s/.test(char);
3632
+ function ScrambleText({
3633
+ children,
3634
+ className,
3635
+ trigger = "view",
3636
+ speed = 40,
3637
+ stagger = 60,
3638
+ characters = DEFAULT_CHARACTERS2,
3639
+ accentColor,
3640
+ glow,
3641
+ once = true
3642
+ }) {
3643
+ const elementRef = useRef(null);
3644
+ const prefersReducedMotion = usePrefersReducedMotion();
3645
+ const [runId, setRunId] = useState(0);
3646
+ const [frame, setFrame] = useState(null);
3647
+ const chars = useMemo(() => Array.from(children), [children]);
3648
+ useEffect(() => {
3649
+ const element = elementRef.current;
3650
+ if (!element || prefersReducedMotion || trigger !== "view") return;
3651
+ const observer = new IntersectionObserver(
3652
+ ([entry]) => {
3653
+ if (!entry.isIntersecting) return;
3654
+ setRunId((id) => id + 1);
3655
+ if (once) observer.disconnect();
3656
+ },
3657
+ { threshold: 0.1 }
3658
+ );
3659
+ observer.observe(element);
3660
+ return () => observer.disconnect();
3661
+ }, [trigger, once, prefersReducedMotion]);
3662
+ useEffect(() => {
3663
+ if (runId === 0 || prefersReducedMotion) return;
3664
+ const pool = Array.from(characters || DEFAULT_CHARACTERS2);
3665
+ const start = performance.now();
3666
+ const step = () => {
3667
+ const resolved = Math.floor((performance.now() - start) / stagger);
3668
+ if (resolved >= chars.length) {
3669
+ setFrame(null);
3670
+ return true;
3671
+ }
3672
+ setFrame({
3673
+ resolved,
3674
+ chars: chars.map(
3675
+ (char, index) => index < resolved || isSpace(char) ? char : pool[Math.floor(Math.random() * pool.length)]
3676
+ )
3677
+ });
3678
+ return false;
3679
+ };
3680
+ if (step()) return;
3681
+ const timer = setInterval(() => {
3682
+ if (step()) clearInterval(timer);
3683
+ }, speed);
3684
+ return () => clearInterval(timer);
3685
+ }, [runId, chars, characters, speed, stagger, prefersReducedMotion]);
3686
+ const replay = () => {
3687
+ if (!frame && !prefersReducedMotion) setRunId((id) => id + 1);
3688
+ };
3689
+ const hoverHandlers = trigger === "hover" ? { onPointerEnter: replay, onFocus: replay } : null;
3690
+ const style = {
3691
+ ...accentColor ? { "--elyri-scramble-accent": accentColor } : null,
3692
+ ...glow !== void 0 ? { "--elyri-scramble-glow": Math.max(0, glow) } : null
3693
+ };
3694
+ return /* @__PURE__ */ jsxs("span", { ref: elementRef, className: cn("elyri-scramble-text", className), style, ...hoverHandlers, children: [
3695
+ /* @__PURE__ */ jsx("span", { className: "elyri-scramble-text__sr", children }),
3696
+ /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: chars.map((char, index) => {
3697
+ const isScrambled = frame !== null && index >= frame.resolved && !isSpace(char);
3698
+ return /* @__PURE__ */ jsxs(
3699
+ "span",
3700
+ {
3701
+ className: cn("elyri-scramble-text__char", isScrambled && "elyri-scramble-text__char--scrambled"),
3702
+ children: [
3703
+ char,
3704
+ isScrambled && /* @__PURE__ */ jsx("span", { className: "elyri-scramble-text__glyph", children: frame.chars[index] })
3705
+ ]
3706
+ },
3707
+ index
3708
+ );
3709
+ }) })
3710
+ ] });
3711
+ }
3712
+ function ScrollMarquee({
3713
+ children,
3714
+ className,
3715
+ direction = "left",
3716
+ speed = 0.5,
3717
+ rows = 1,
3718
+ repeat = 6,
3719
+ infinite = true
3720
+ }) {
3721
+ const rootRef = useRef(null);
3722
+ const firstCopyRef = useRef(null);
3723
+ const prefersReducedMotion = usePrefersReducedMotion();
3724
+ useEffect(() => {
3725
+ const root = rootRef.current;
3726
+ if (!root || prefersReducedMotion) return;
3727
+ let frame = 0;
3728
+ const update = () => {
3729
+ frame = 0;
3730
+ const copyWidth = firstCopyRef.current?.offsetWidth ?? 0;
3731
+ if (!copyWidth) return;
3732
+ const offset = window.scrollY * speed;
3733
+ const shift = infinite ? -(offset % copyWidth) : -Math.min(offset, copyWidth);
3734
+ root.style.setProperty("--elyri-scroll-marquee-shift", `${shift}px`);
3735
+ root.style.setProperty("--elyri-scroll-marquee-copy-width", `${copyWidth}px`);
3736
+ };
3737
+ const onScroll = () => {
3738
+ if (frame) return;
3739
+ frame = requestAnimationFrame(update);
3740
+ };
3741
+ update();
3742
+ window.addEventListener("scroll", onScroll, { passive: true });
3743
+ window.addEventListener("resize", onScroll);
3744
+ return () => {
3745
+ window.removeEventListener("scroll", onScroll);
3746
+ window.removeEventListener("resize", onScroll);
3747
+ if (frame) cancelAnimationFrame(frame);
3748
+ };
3749
+ }, [prefersReducedMotion, speed, rows, repeat, infinite]);
3750
+ const rowCount = Math.max(1, Math.floor(rows));
3751
+ const copyCount = Math.max(1, Math.floor(repeat));
3752
+ const baseDirection = direction === "left" ? 1 : -1;
3753
+ return /* @__PURE__ */ jsx("div", { ref: rootRef, className: cn("elyri-scroll-marquee", className), children: Array.from({ length: rowCount }, (_, rowIndex) => {
3754
+ const dir = rowIndex % 2 === 0 ? baseDirection : -baseDirection;
3755
+ return /* @__PURE__ */ jsx(
3756
+ "div",
3757
+ {
3758
+ className: "elyri-scroll-marquee__row",
3759
+ style: {
3760
+ "--elyri-scroll-marquee-dir": dir,
3761
+ // 向右滚动时先整体左移一份内容宽度,避免左侧露白
3762
+ "--elyri-scroll-marquee-base": dir === 1 ? 0 : -1
3763
+ },
3764
+ children: Array.from({ length: copyCount }, (_2, copyIndex) => /* @__PURE__ */ jsx(
3765
+ "span",
3766
+ {
3767
+ ref: rowIndex === 0 && copyIndex === 0 ? firstCopyRef : void 0,
3768
+ className: "elyri-scroll-marquee__copy",
3769
+ children
3770
+ },
3771
+ copyIndex
3772
+ ))
3773
+ },
3774
+ rowIndex
3775
+ );
3776
+ }) });
3777
+ }
3778
+ var LEAD_RATIO = 0.65;
3779
+ var segmenter = typeof Intl !== "undefined" && "Segmenter" in Intl ? new Intl.Segmenter(void 0, { granularity: "word" }) : null;
3780
+ var splitWords = (text) => {
3781
+ if (!segmenter) return splitTextUnits(text, "word");
3782
+ const segments = [];
3783
+ let index = 0;
3784
+ let wordPart = 0;
3785
+ let spacePart = 0;
3786
+ let pending = "";
3787
+ for (const { segment: value, isWordLike } of segmenter.segment(text)) {
3788
+ if (/^\s+$/.test(value)) {
3789
+ segments.push({ kind: "space", key: `space-${spacePart++}`, value });
3790
+ continue;
3791
+ }
3792
+ if (isWordLike) {
3793
+ segments.push({ kind: "word", key: `word-${wordPart++}`, units: [{ char: pending + value, index: index++ }] });
3794
+ pending = "";
3795
+ continue;
3796
+ }
3797
+ const last = segments[segments.length - 1];
3798
+ if (!pending && last?.kind === "word") {
3799
+ last.units[0].char += value;
3800
+ } else {
3801
+ pending += value;
3802
+ }
3803
+ }
3804
+ if (pending) {
3805
+ segments.push({ kind: "word", key: `word-${wordPart}`, units: [{ char: pending, index }] });
3806
+ }
3807
+ return segments;
3808
+ };
3809
+ function ScrollReveal({
3810
+ children,
3811
+ className,
3812
+ by = "word",
3813
+ height = 280,
3814
+ dimOpacity = 0.15,
3815
+ blur = 0
3816
+ }) {
3817
+ const rootRef = useRef(null);
3818
+ const contentRef = useRef(null);
3819
+ const prefersReducedMotion = usePrefersReducedMotion();
3820
+ const segments = useMemo(
3821
+ () => by === "word" ? splitWords(children) : splitTextUnits(children, "char"),
3822
+ [children, by]
3823
+ );
3824
+ const count = useMemo(
3825
+ () => segments.reduce((total, segment) => segment.kind === "word" ? total + segment.units.length : total, 0),
3826
+ [segments]
3827
+ );
3828
+ useEffect(() => {
3829
+ const root = rootRef.current;
3830
+ const content = contentRef.current;
3831
+ if (!root || !content || prefersReducedMotion) return;
3832
+ let frame = 0;
3833
+ const update = () => {
3834
+ frame = 0;
3835
+ const travel = content.offsetHeight;
3836
+ if (travel <= 0) return;
3837
+ const { clientHeight, scrollHeight, scrollTop } = root;
3838
+ const range = scrollHeight - clientHeight;
3839
+ if (range <= 0) {
3840
+ root.style.setProperty("--elyri-scroll-reveal-progress", "1");
3841
+ return;
3842
+ }
3843
+ const line = scrollTop + clientHeight * LEAD_RATIO;
3844
+ const tailStart = Math.max(0, range - clientHeight);
3845
+ const tail = Math.min(1, Math.max(0, (scrollTop - tailStart) / clientHeight));
3846
+ const head = scrollTop >= range - 1 ? travel : line + tail * (travel - range - clientHeight * LEAD_RATIO);
3847
+ root.style.setProperty("--elyri-scroll-reveal-progress", String(Math.min(1, Math.max(0, head / travel))));
3848
+ };
3849
+ const onScroll = () => {
3850
+ if (frame) return;
3851
+ frame = requestAnimationFrame(update);
3852
+ };
3853
+ update();
3854
+ root.addEventListener("scroll", onScroll, { passive: true });
3855
+ const observer = new ResizeObserver(onScroll);
3856
+ observer.observe(content);
3857
+ return () => {
3858
+ root.removeEventListener("scroll", onScroll);
3859
+ observer.disconnect();
3860
+ if (frame) cancelAnimationFrame(frame);
3861
+ };
3862
+ }, [prefersReducedMotion, count]);
3863
+ const style = {
3864
+ "--elyri-scroll-reveal-height": `${height}px`,
3865
+ "--elyri-scroll-reveal-dim": dimOpacity,
3866
+ "--elyri-scroll-reveal-blur": `${blur}px`,
3867
+ "--elyri-scroll-reveal-count": count
3868
+ };
3869
+ return /* @__PURE__ */ jsx(
3870
+ "div",
3871
+ {
3872
+ ref: rootRef,
3873
+ className: cn("elyri-scroll-reveal", blur > 0 && "elyri-scroll-reveal--blur", className),
3874
+ style,
3875
+ children: /* @__PURE__ */ jsx("div", { ref: contentRef, className: "elyri-scroll-reveal__content", "aria-label": children, children: segments.map(
3876
+ (segment) => segment.kind === "space" ? segment.value : /* @__PURE__ */ jsx("span", { className: "elyri-scroll-reveal__word", children: segment.units.map((unit) => /* @__PURE__ */ jsx(
3877
+ "span",
3878
+ {
3879
+ className: "elyri-scroll-reveal__unit",
3880
+ style: { "--elyri-scroll-reveal-index": unit.index },
3881
+ "aria-hidden": "true",
3882
+ children: unit.char
3883
+ },
3884
+ unit.index
3885
+ )) }, segment.key)
3886
+ ) })
3887
+ }
3888
+ );
3889
+ }
3890
+ var splitText = (text, by) => {
3891
+ let index = 0;
3892
+ return text.split(/(\s+)/).filter(Boolean).map((part, partIndex) => {
3893
+ if (/^\s+$/.test(part)) return { kind: "space", key: `space-${partIndex}`, value: part };
3894
+ const units = by === "char" ? Array.from(part) : [part];
3895
+ return {
3896
+ kind: "word",
3897
+ key: `word-${partIndex}`,
3898
+ units: units.map((char) => ({ char, index: index++ }))
3899
+ };
3900
+ });
3901
+ };
3902
+ function SplitReveal({
3903
+ children,
3904
+ className,
3905
+ by = "char",
3906
+ delay = 0,
3907
+ stagger = 40,
3908
+ duration = 600,
3909
+ distance = 24,
3910
+ once = true
3911
+ }) {
3912
+ const elementRef = useRef(null);
3913
+ const prefersReducedMotion = usePrefersReducedMotion();
3914
+ const [isIntersecting, setIsIntersecting] = useState(false);
3915
+ useEffect(() => {
3916
+ const element = elementRef.current;
3917
+ if (!element || prefersReducedMotion) return;
3918
+ const observer = new IntersectionObserver(
3919
+ ([entry]) => {
3920
+ if (entry.isIntersecting) {
3921
+ setIsIntersecting(true);
3922
+ if (once) observer.disconnect();
3923
+ return;
3924
+ }
3925
+ if (!once) setIsIntersecting(false);
3926
+ },
3927
+ { threshold: 0.1 }
3928
+ );
3929
+ observer.observe(element);
3930
+ return () => observer.disconnect();
3931
+ }, [once, prefersReducedMotion]);
3932
+ const segments = useMemo(() => splitText(children, by), [children, by]);
3933
+ const style = {
3934
+ "--elyri-split-reveal-delay": `${delay}ms`,
3935
+ "--elyri-split-reveal-stagger": `${stagger}ms`,
3936
+ "--elyri-split-reveal-duration": `${duration}ms`,
3937
+ "--elyri-split-reveal-distance": `${distance}px`
3938
+ };
3939
+ const isVisible = prefersReducedMotion || isIntersecting;
3940
+ return /* @__PURE__ */ jsx(
3941
+ "span",
3942
+ {
3943
+ ref: elementRef,
3944
+ className: cn("elyri-split-reveal", isVisible && "elyri-split-reveal--visible", className),
3945
+ style,
3946
+ children: segments.map(
3947
+ (segment) => segment.kind === "space" ? segment.value : /* @__PURE__ */ jsx("span", { className: "elyri-split-reveal__word", children: segment.units.map((unit) => /* @__PURE__ */ jsx(
3948
+ "span",
3949
+ {
3950
+ className: "elyri-split-reveal__unit",
3951
+ style: { "--elyri-split-reveal-index": unit.index },
3952
+ children: unit.char
3953
+ },
3954
+ unit.index
3955
+ )) }, segment.key)
3956
+ )
3957
+ }
3958
+ );
3959
+ }
3960
+ function Typewriter({
3961
+ children,
3962
+ className,
3963
+ speed = 60,
3964
+ startDelay = 200,
3965
+ loop = false,
3966
+ loopDelay = 1200,
3967
+ cursor = true
3968
+ }) {
3969
+ const text = children;
3970
+ const prefersReducedMotion = usePrefersReducedMotion();
3971
+ const [count, setCount] = useState(0);
3972
+ useEffect(() => {
3973
+ if (prefersReducedMotion || !text) return;
3974
+ let timer;
3975
+ let typed = 0;
3976
+ const typeNext = (wait) => {
3977
+ timer = setTimeout(() => {
3978
+ typed += 1;
3979
+ setCount(typed);
3980
+ if (typed < text.length) {
3981
+ typeNext(speed);
3982
+ return;
3983
+ }
3984
+ if (loop) {
3985
+ timer = setTimeout(() => {
3986
+ typed = 0;
3987
+ setCount(0);
3988
+ typeNext(speed);
3989
+ }, loopDelay);
3990
+ }
3991
+ }, wait);
3992
+ };
3993
+ timer = setTimeout(() => {
3994
+ typed = 0;
3995
+ setCount(0);
3996
+ typeNext(0);
3997
+ }, startDelay);
3998
+ return () => clearTimeout(timer);
3999
+ }, [text, speed, startDelay, loop, loopDelay, prefersReducedMotion]);
4000
+ const visible = prefersReducedMotion ? text : text.slice(0, count);
4001
+ return /* @__PURE__ */ jsxs("span", { className: cn("elyri-typewriter", className), children: [
4002
+ /* @__PURE__ */ jsx("span", { className: "elyri-typewriter__text", children: visible }),
4003
+ cursor && /* @__PURE__ */ jsx("span", { className: "elyri-typewriter__cursor", "aria-hidden": "true" })
4004
+ ] });
4005
+ }
4006
+ function WaveText({ children, className, amplitude = 6, speed = 1200, stagger = 60 }) {
4007
+ const segments = useMemo(() => splitTextUnits(children, "char"), [children]);
4008
+ const style = {
4009
+ "--elyri-wave-text-amplitude": `${amplitude}px`,
4010
+ "--elyri-wave-text-speed": `${speed}ms`,
4011
+ "--elyri-wave-text-stagger": `${stagger}ms`
4012
+ };
4013
+ return /* @__PURE__ */ jsx("span", { className: cn("elyri-wave-text", className), style, "aria-label": children, children: segments.map(
4014
+ (segment) => segment.kind === "space" ? segment.value : /* @__PURE__ */ jsx("span", { className: "elyri-wave-text__word", children: segment.units.map((unit) => /* @__PURE__ */ jsx(
4015
+ "span",
4016
+ {
4017
+ className: "elyri-wave-text__unit",
4018
+ style: { "--elyri-wave-text-index": unit.index },
4019
+ "aria-hidden": "true",
4020
+ children: unit.char
4021
+ },
4022
+ unit.index
4023
+ )) }, segment.key)
4024
+ ) });
4025
+ }
4026
+
4027
+ export { AsciiImage, Aurora, Caustics, Contours, CountUp, DotField, DragonScales, FadeIn, Filaments, FlipTiles, GradientText, Halo, Magnetic, Marquee, MeshGradient, ParticleText, PixelVortex, Planet, RotatingText, ScaleIn, ScrambleText, ScrollMarquee, ScrollReveal, SilkWaves, SplitReveal, Spotlight, Tilt, Typewriter, Velvet, WaveText };
4028
+ //# sourceMappingURL=index.js.map
4029
+ //# sourceMappingURL=index.js.map