@toriistudio/shader-ui 0.0.1 → 0.0.2

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.
package/dist/index.mjs CHANGED
@@ -20,6 +20,7 @@ function useScene({
20
20
  const onCreateRef = useRef(onCreate);
21
21
  const onRenderRef = useRef(onRender);
22
22
  const onResizeRef = useRef(onResize);
23
+ const sizeRef = useRef({ width: 0, height: 0 });
23
24
  useEffect(() => {
24
25
  onCreateRef.current = onCreate;
25
26
  }, [onCreate]);
@@ -34,43 +35,83 @@ function useScene({
34
35
  const container = containerRef.current;
35
36
  if (!container) return;
36
37
  const resolvedPixelRatio = pixelRatio ?? (typeof window !== "undefined" ? Math.min(window.devicePixelRatio, 2) : 1);
38
+ const initialWidth = container.clientWidth || 1;
39
+ const initialHeight = container.clientHeight || 1;
40
+ sizeRef.current = { width: initialWidth, height: initialHeight };
37
41
  const renderer = new THREE.WebGLRenderer({
38
42
  alpha: true,
39
43
  antialias: true,
40
44
  ...rendererOptions
41
45
  });
42
46
  renderer.setPixelRatio(resolvedPixelRatio);
43
- renderer.setSize(container.clientWidth, container.clientHeight, false);
47
+ renderer.setSize(initialWidth, initialHeight, false);
44
48
  renderer.setClearColor(0, 0);
45
49
  renderer.domElement.style.width = "100%";
46
50
  renderer.domElement.style.height = "100%";
47
51
  container.appendChild(renderer.domElement);
48
52
  const scene = new THREE.Scene();
49
- const camera = new THREE.PerspectiveCamera(
50
- cameraOptions?.fov ?? 55,
51
- container.clientWidth / Math.max(1, container.clientHeight),
52
- cameraOptions?.near ?? 0.1,
53
- cameraOptions?.far ?? 500
54
- );
55
- const [x = 0, y = 0, z = 15] = cameraOptions?.position ?? [];
56
- camera.position.set(x, y, z);
53
+ let camera;
54
+ if (cameraOptions?.type === "orthographic") {
55
+ const halfWidth = initialWidth / 2;
56
+ const halfHeight = initialHeight / 2;
57
+ const orthoCamera = new THREE.OrthographicCamera(
58
+ -halfWidth,
59
+ halfWidth,
60
+ halfHeight,
61
+ -halfHeight,
62
+ cameraOptions?.near ?? 0.1,
63
+ cameraOptions?.far ?? 1e3
64
+ );
65
+ const [, , z = 10] = cameraOptions?.position ?? [];
66
+ orthoCamera.position.set(0, 0, z);
67
+ camera = orthoCamera;
68
+ } else {
69
+ const perspectiveCamera = new THREE.PerspectiveCamera(
70
+ cameraOptions?.fov ?? 55,
71
+ initialWidth / Math.max(1, initialHeight),
72
+ cameraOptions?.near ?? 0.1,
73
+ cameraOptions?.far ?? 500
74
+ );
75
+ const [x = 0, y = 0, z = 15] = cameraOptions?.position ?? [];
76
+ perspectiveCamera.position.set(x, y, z);
77
+ camera = perspectiveCamera;
78
+ }
57
79
  const clock = new THREE.Clock();
58
- const context = { renderer, scene, camera, clock };
80
+ const context = {
81
+ renderer,
82
+ scene,
83
+ camera,
84
+ clock,
85
+ size: { ...sizeRef.current }
86
+ };
59
87
  contextRef.current = context;
60
88
  const teardownCreate = onCreateRef.current?.(context);
89
+ let elapsedTime = 0;
61
90
  let animationFrameId = 0;
62
91
  const renderLoop = () => {
63
- onRenderRef.current?.(context);
92
+ const delta = clock.getDelta();
93
+ elapsedTime += delta;
94
+ context.size = { ...sizeRef.current };
95
+ onRenderRef.current?.(context, delta, elapsedTime);
64
96
  renderer.render(scene, camera);
65
97
  animationFrameId = requestAnimationFrame(renderLoop);
66
98
  };
67
99
  animationFrameId = requestAnimationFrame(renderLoop);
68
100
  const resizeObserver = new ResizeObserver(() => {
69
- const width = container.clientWidth;
70
- const height = container.clientHeight;
101
+ const width = container.clientWidth || 1;
102
+ const height = container.clientHeight || 1;
71
103
  renderer.setSize(width, height, false);
72
- camera.aspect = width / Math.max(1, height);
104
+ sizeRef.current = { width, height };
105
+ if (camera instanceof THREE.PerspectiveCamera) {
106
+ camera.aspect = width / Math.max(1, height);
107
+ } else if (camera instanceof THREE.OrthographicCamera) {
108
+ camera.left = -width / 2;
109
+ camera.right = width / 2;
110
+ camera.top = height / 2;
111
+ camera.bottom = -height / 2;
112
+ }
73
113
  camera.updateProjectionMatrix();
114
+ context.size = { ...sizeRef.current };
74
115
  onResizeRef.current?.(context, { width, height });
75
116
  });
76
117
  resizeObserver.observe(container);
@@ -130,15 +171,14 @@ function ShaderArt({
130
171
  assetsRef.current = null;
131
172
  };
132
173
  }, []);
133
- const handleRender = useCallback((context) => {
134
- shaderUniformsRef.current.iTime.value = context.clock.getElapsedTime();
135
- const canvas = context.renderer.domElement;
136
- shaderUniformsRef.current.iResolution.value.set(
137
- canvas.width,
138
- canvas.height,
139
- 1
140
- );
141
- }, []);
174
+ const handleRender = useCallback(
175
+ (context, _delta, elapsedTime) => {
176
+ shaderUniformsRef.current.iTime.value = elapsedTime;
177
+ const { width: width2, height: height2 } = context.size;
178
+ shaderUniformsRef.current.iResolution.value.set(width2, height2, 1);
179
+ },
180
+ []
181
+ );
142
182
  const { containerRef } = useScene({
143
183
  onCreate: handleCreate,
144
184
  onRender: handleRender
@@ -285,7 +325,12 @@ function FractalFlower({
285
325
  const points = new THREE3.Points(geometry, material);
286
326
  points.frustumCulled = false;
287
327
  scene.add(points);
288
- assetsRef.current = { points, geometry, material, uniforms: uniformValues };
328
+ assetsRef.current = {
329
+ points,
330
+ geometry,
331
+ material,
332
+ uniforms: uniformValues
333
+ };
289
334
  return () => {
290
335
  scene.remove(points);
291
336
  geometry.dispose();
@@ -293,25 +338,29 @@ function FractalFlower({
293
338
  assetsRef.current = null;
294
339
  };
295
340
  },
296
- [attributes.eValues, attributes.kValues, attributes.positions, attributes.rotations]
341
+ [
342
+ attributes.eValues,
343
+ attributes.kValues,
344
+ attributes.positions,
345
+ attributes.rotations
346
+ ]
297
347
  );
298
348
  const handleRender = useCallback2(
299
- (context) => {
349
+ (context, delta, elapsedTime) => {
300
350
  const assets = assetsRef.current;
301
351
  if (!assets) return;
302
352
  const uniforms = assets.uniforms;
303
- uniforms.uTime.value = context.clock.getElapsedTime();
304
- const canvas = context.renderer.domElement;
305
- uniforms.uResolution.value.set(canvas.width, canvas.height);
353
+ uniforms.uTime.value = elapsedTime;
354
+ const { width: width2, height: height2 } = context.size;
355
+ uniforms.uResolution.value.set(width2, height2);
306
356
  const basePointSize = Math.max(
307
357
  2,
308
- canvas.height / 400 * (uniforms.uPetalRadius.value * 32)
358
+ height2 / 400 * (uniforms.uPetalRadius.value * 32)
309
359
  );
310
360
  const pointSize = Math.min(basePointSize, 6);
311
361
  uniforms.uPointSize.value = pointSize;
312
362
  const morph = morphRef.current;
313
363
  if (Math.abs(morph.target - morph.progress) > 1e-3) {
314
- const delta = context.clock.getDelta();
315
364
  const direction = Math.sign(morph.target - morph.progress);
316
365
  morph.progress = clamp(
317
366
  morph.progress + direction * delta * MORPH_SPEED,
@@ -462,11 +511,14 @@ function OranoParticles({
462
511
  particlesRef.current = null;
463
512
  };
464
513
  }, []);
465
- const handleRender = useCallback3((context) => {
466
- const assets = particlesRef.current;
467
- if (!assets) return;
468
- assets.uniforms.uTime.value = context.clock.getElapsedTime();
469
- }, []);
514
+ const handleRender = useCallback3(
515
+ (_context, _delta, elapsedTime) => {
516
+ const assets = particlesRef.current;
517
+ if (!assets) return;
518
+ assets.uniforms.uTime.value = elapsedTime;
519
+ },
520
+ []
521
+ );
470
522
  const { containerRef } = useScene({
471
523
  onCreate: handleCreate,
472
524
  onRender: handleRender
@@ -525,8 +577,477 @@ function OranoParticles({
525
577
  }
526
578
  );
527
579
  }
580
+
581
+ // src/components/MenuGlitch.tsx
582
+ import { gsap } from "gsap";
583
+ import {
584
+ useCallback as useCallback4,
585
+ useEffect as useEffect5,
586
+ useMemo as useMemo2,
587
+ useRef as useRef5
588
+ } from "react";
589
+ import * as THREE5 from "three";
590
+
591
+ // src/shaders/menu-glitch/fragment.glsl
592
+ var fragment_default4 = "#define M_PI 3.1415926535897932384626433832795\n\nuniform sampler2D tDiffuse;\nuniform sampler2D tGradient;\n\nuniform float uTime;\n\nuniform float uTileProgressVertical;\nuniform float uTileProgressHorizontal;\nuniform float uTileAmplitude;\nuniform vec2 uTileFrequency;\nuniform vec2 uTileOffset;\n\nuniform float uWaveProgress;\nuniform float uWaveAmplitude;\nuniform vec2 uWaveStrength;\n\nuniform float uGradientProgress;\nuniform float uGradientOffset;\nuniform float uGradientAmplitude;\n\nuniform float uBlueProgress;\nuniform float uBlueAmplitude;\n\nuniform float uWhiteTileChances;\nuniform float uWhiteTileFrequency;\nuniform float uWhiteTileStrength;\n\nuniform float uSaturation;\n\nvarying vec2 vUv;\n\n\n// --------------------------------------------------\n// Utilities\n// --------------------------------------------------\n\nfloat random(in vec2 st) {\n return fract(sin(dot(st.xy, vec2(12.9898, 78.233))) * 43758.5453123);\n}\n\nvec2 getTileCoord(vec2 pos, vec2 frequency) {\n vec2 coord = vec2(\n floor(pos.x * frequency.x),\n floor(pos.y * frequency.y)\n );\n\n coord /= frequency;\n return coord;\n}\n\nfloat toSin(float value) {\n return (sin((value - 0.5) * M_PI) + 1.0) * 0.5;\n}\n\n\n// --------------------------------------------------\n// Main\n// --------------------------------------------------\n\nvoid main() {\n\n // ------------------------\n // Tiles\n // ------------------------\n vec2 tileCoord1 = getTileCoord(vUv, uTileFrequency * 1.8942);\n vec2 tileCoord2 = getTileCoord(vUv, uTileFrequency * 1.0);\n vec2 tileCoord3 = getTileCoord(vUv, uTileFrequency * 2.1245);\n\n vec2 tileCoord = tileCoord2 +\n step(random(tileCoord1), 0.5) * (tileCoord3 - tileCoord2);\n\n float tileRandom = random(tileCoord);\n float tileAngle = tileRandom * M_PI * 2.0;\n\n vec2 tileOffset = vec2(sin(tileAngle), cos(tileAngle)) * uTileOffset;\n\n float tileProgress = 1.0 -\n (distance(tileCoord.y, uTileProgressVertical) / (uTileAmplitude * 0.5));\n\n tileProgress = clamp(tileProgress, 0.0, 1.0);\n\n float tileProgressHorizontal = 1.0 -\n (distance(tileCoord.x, uTileProgressHorizontal) / (uTileAmplitude * 0.5));\n tileProgressHorizontal = clamp(tileProgressHorizontal, 0.0, 1.0);\n\n // ------------------------\n // Wave\n // ------------------------\n float waveProgress = 1.0 -\n (distance(vUv.x, uWaveProgress) / (uWaveAmplitude * 0.5));\n\n waveProgress = clamp(waveProgress, 0.0, 1.0);\n\n vec2 waveOffset = toSin(waveProgress) * uWaveStrength;\n\n\n // ------------------------\n // Gradient\n // ------------------------\n float gradientProgress = (tileCoord.x - uGradientProgress) / uGradientAmplitude;\n gradientProgress += (tileRandom - 0.5) * uGradientOffset;\n\n vec4 gradientColor = texture2D(\n tGradient,\n vec2(clamp(gradientProgress, 0.0, 1.0), 0.0)\n );\n\n\n // ------------------------\n // Blue tint\n // ------------------------\n float blueProgress = (tileCoord.x - uBlueProgress) / uBlueAmplitude;\n blueProgress += tileOffset.x;\n blueProgress = clamp(blueProgress, 0.0, 1.0);\n\n\n // ------------------------\n // White flickering tiles\n // ------------------------\n float whiteTileProgress =\n sin(uTime * uWhiteTileFrequency + tileRandom * M_PI * 2.0) * 0.5 + 0.5;\n\n whiteTileProgress =\n clamp(whiteTileProgress - (1.0 - uWhiteTileChances), 0.0, 1.0) *\n (1.0 / uWhiteTileChances) *\n uWhiteTileStrength;\n\n\n // ------------------------\n // Final color pipeline\n // ------------------------\n vec2 uv = vUv;\n\n // Apply tile offset\n uv += tileOffset * tileProgress;\n\n // Apply waves (optional)\n // uv += waveOffset;\n\n // Repeat UV\n uv = mod(uv, vec2(1.0));\n\n // Base color removed so overlay only contains glitch elements\n vec4 color = vec4(0.0);\n\n // Add gradient\n color.rgb += gradientColor.rgb * gradientColor.a * tileProgressHorizontal;\n\n // Add white flicker effect\n color.rgb += vec3(whiteTileProgress) * tileProgressHorizontal;\n\n // Blue tone shift\n vec3 blueColor = vec3((color.r + color.g + color.b) / 3.0) *\n vec3(0.3, 0.5, 1.0);\n\n color.rgb = mix(color.rgb, blueColor, vec3(blueProgress));\n\n // Saturation\n color.rgb *= uSaturation;\n\n float effectAlpha =\n clamp(tileProgressHorizontal * (tileProgress * 0.6 + whiteTileProgress + gradientColor.a), 0.0, 1.0);\n\n // Output\n gl_FragColor = vec4(color.rgb, effectAlpha);\n}\n";
593
+
594
+ // src/shaders/menu-glitch/vertex.glsl
595
+ var vertex_default4 = "varying vec2 vUv;\n\nvoid main() {\n vUv = uv;\n gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);\n}\n";
596
+
597
+ // src/components/MenuGlitch.tsx
598
+ import { jsx as jsx4 } from "react/jsx-runtime";
599
+ var GRADIENT_DATA_URL = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAABCAYAAAAxWXB3AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyhpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTM4IDc5LjE1OTgyNCwgMjAxNi8wOS8xNC0wMTowOTowMSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTcgKE1hY2ludG9zaCkiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6RTVBRkY1OTJDREMyMTFFODhGRjFFRjgxRjM2QjM2MDMiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6RTVBRkY1OTNDREMyMTFFODhGRjFFRjgxRjM2QjM2MDMiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpFNUFGRjU5MENEQzIxMUU4OEZGMUVGODFGMzZCMzYwMyIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpFNUFGRjU5MUNEQzIxMUU4OEZGMUVGODFGMzZCMzYwMyIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/Pksi3ywAAADMSURBVHjarFDLDoMwDDMtUDjtME3TtMcn7f9/hngBCoTSTjsMydixnVZqxTcAX51Quwsad1M80PonWvdCUA7+rrii82eEulcG+hoTdwsrQtRB0UZu3KxrN8OPqIBKgRHxI8qaUTD1RuY2p90V+hOjmWix2uSSeGLPKrDdKc5GHzqZLPWY7XEG+F1PLBktpiuzf8ilkMvqcZfLLqf1ltnu7rLh4G88ZPxh4uUOmjMYO3aPqz8Yve0tGiaDduNr/xn8cWbBY8HLzQd8BBgAgOx+ERYDbIEAAAAASUVORK5CYII=";
600
+ var BASE_TRANSITION_PROGRESS = 0.5;
601
+ var SHOW_BASE_DURATION = 2.72;
602
+ var HIDE_BASE_DURATION = 1.8;
603
+ var SHOW_PRIMARY_DURATION = 1.1;
604
+ var MIN_GLITCH_DURATION = 0.25;
605
+ function MenuGlitch({
606
+ settings,
607
+ onShowDone,
608
+ onHideDone,
609
+ className,
610
+ style,
611
+ width,
612
+ height,
613
+ ...divProps
614
+ }) {
615
+ const diffuseTexture = useMemo2(() => createDiffuseTexture(), []);
616
+ const gradientTextureRef = useRef5(new THREE5.Texture());
617
+ const sanitizedValues = useMemo2(() => {
618
+ const clamp2 = THREE5.MathUtils.clamp;
619
+ return {
620
+ tileAmplitude: Math.max(0.1, settings.tileAmplitude),
621
+ planeScale: Math.max(0.1, settings.planeScale ?? 1),
622
+ tileOffsetX: settings.tileOffset.x,
623
+ tileOffsetY: settings.tileOffset.y,
624
+ tileFrequencyX: Math.max(0.1, settings.tileFrequency.x),
625
+ tileFrequencyY: Math.max(0.1, settings.tileFrequency.y),
626
+ gradientAmplitude: Math.max(0.05, settings.gradientAmplitude),
627
+ gradientOffset: settings.gradientOffset,
628
+ gradientProgress: settings.gradientProgress ?? 1,
629
+ blueAmplitude: Math.max(0.05, settings.blueAmplitude),
630
+ blueProgress: settings.blueProgress ?? 1,
631
+ waveAmplitude: Math.max(0.05, settings.waveAmplitude),
632
+ waveProgress: settings.waveProgress ?? 0.5,
633
+ waveStrengthX: settings.waveStrength.x,
634
+ waveStrengthY: settings.waveStrength.y,
635
+ whiteTileChances: clamp2(settings.whiteTileChances, 0.05, 1),
636
+ whiteTileFrequency: Math.max(0.01, settings.whiteTileFrequency),
637
+ whiteTileStrength: Math.max(0, settings.whiteTileStrength),
638
+ saturation: Math.max(0, settings.saturation),
639
+ duration: Math.max(
640
+ MIN_GLITCH_DURATION,
641
+ settings.duration ?? SHOW_PRIMARY_DURATION
642
+ ),
643
+ debug: settings.debug
644
+ };
645
+ }, [settings]);
646
+ const sanitizedRef = useRef5(sanitizedValues);
647
+ sanitizedRef.current = sanitizedValues;
648
+ const sizeRef = useRef5({ width: 0, height: 0 });
649
+ const assetsRef = useRef5(null);
650
+ const animationVisibleRef = useRef5(false);
651
+ const shaderUniforms = useRef5({
652
+ tDiffuse: { value: diffuseTexture },
653
+ tGradient: { value: gradientTextureRef.current },
654
+ uTime: { value: 0 },
655
+ uTileProgressVertical: { value: -0.5 },
656
+ uTileProgressHorizontal: { value: -0.5 },
657
+ uTileAmplitude: { value: settings.tileAmplitude },
658
+ uTileOffset: {
659
+ value: new THREE5.Vector2(settings.tileOffset.x, settings.tileOffset.y)
660
+ },
661
+ uTileFrequency: {
662
+ value: new THREE5.Vector2(
663
+ settings.tileFrequency.x,
664
+ settings.tileFrequency.y
665
+ )
666
+ },
667
+ uWaveProgress: { value: settings.waveProgress },
668
+ uWaveAmplitude: { value: settings.waveAmplitude },
669
+ uWaveStrength: {
670
+ value: new THREE5.Vector2(
671
+ settings.waveStrength.x,
672
+ settings.waveStrength.y
673
+ )
674
+ },
675
+ uGradientProgress: { value: settings.gradientProgress },
676
+ uGradientOffset: { value: settings.gradientOffset },
677
+ uGradientAmplitude: { value: settings.gradientAmplitude },
678
+ uBlueProgress: { value: settings.blueProgress },
679
+ uBlueAmplitude: { value: settings.blueAmplitude },
680
+ uWhiteTileChances: { value: settings.whiteTileChances },
681
+ uWhiteTileFrequency: { value: settings.whiteTileFrequency },
682
+ uWhiteTileStrength: { value: settings.whiteTileStrength },
683
+ uSaturation: { value: settings.saturation }
684
+ });
685
+ useEffect5(() => {
686
+ let disposed = false;
687
+ const loader = new THREE5.TextureLoader();
688
+ loader.load(
689
+ GRADIENT_DATA_URL,
690
+ (texture) => {
691
+ if (disposed) return;
692
+ texture.wrapS = THREE5.ClampToEdgeWrapping;
693
+ texture.wrapT = THREE5.ClampToEdgeWrapping;
694
+ texture.magFilter = THREE5.LinearFilter;
695
+ texture.minFilter = THREE5.LinearFilter;
696
+ texture.colorSpace = THREE5.SRGBColorSpace;
697
+ gradientTextureRef.current = texture;
698
+ shaderUniforms.current.tGradient.value = texture;
699
+ },
700
+ void 0,
701
+ () => {
702
+ if (disposed) return;
703
+ gradientTextureRef.current = new THREE5.Texture();
704
+ shaderUniforms.current.tGradient.value = gradientTextureRef.current;
705
+ }
706
+ );
707
+ return () => {
708
+ disposed = true;
709
+ };
710
+ }, []);
711
+ const updateMeshScale = useCallback4(() => {
712
+ const assets = assetsRef.current;
713
+ if (!assets) return;
714
+ const base = Math.max(
715
+ sizeRef.current.width || 1,
716
+ sizeRef.current.height || 1
717
+ );
718
+ const scale = base * sanitizedRef.current.planeScale;
719
+ assets.mesh.scale.set(scale, scale, 1);
720
+ }, []);
721
+ const updateVisibility = useCallback4(() => {
722
+ const mesh = assetsRef.current?.mesh;
723
+ if (!mesh) return;
724
+ mesh.visible = sanitizedRef.current.debug || animationVisibleRef.current;
725
+ }, []);
726
+ const handleCreate = useCallback4(
727
+ ({ scene, size }) => {
728
+ const geometry = new THREE5.PlaneGeometry(1, 1);
729
+ const material = new THREE5.ShaderMaterial({
730
+ vertexShader: vertex_default4,
731
+ fragmentShader: fragment_default4,
732
+ uniforms: shaderUniforms.current,
733
+ depthWrite: false,
734
+ depthTest: false,
735
+ transparent: true,
736
+ blending: THREE5.AdditiveBlending
737
+ });
738
+ const mesh = new THREE5.Mesh(geometry, material);
739
+ scene.add(mesh);
740
+ assetsRef.current = { mesh, geometry, material };
741
+ sizeRef.current = size;
742
+ updateMeshScale();
743
+ updateVisibility();
744
+ return () => {
745
+ scene.remove(mesh);
746
+ geometry.dispose();
747
+ material.dispose();
748
+ assetsRef.current = null;
749
+ };
750
+ },
751
+ [updateMeshScale, updateVisibility]
752
+ );
753
+ const handleRender = useCallback4(
754
+ (_context, _delta, elapsedTime) => {
755
+ shaderUniforms.current.uTime.value = elapsedTime;
756
+ },
757
+ []
758
+ );
759
+ const handleResize = useCallback4(
760
+ (_context, size) => {
761
+ sizeRef.current = size;
762
+ updateMeshScale();
763
+ },
764
+ [updateMeshScale]
765
+ );
766
+ const { containerRef } = useScene({
767
+ camera: { type: "orthographic", position: [0, 0, 10], near: 0.1, far: 100 },
768
+ onCreate: handleCreate,
769
+ onRender: handleRender,
770
+ onResize: handleResize
771
+ });
772
+ const showTimeline = useRef5(null);
773
+ const hideTimeline = useRef5(null);
774
+ const transitionTimeline = useRef5(null);
775
+ const timelineParams = useMemo2(
776
+ () => ({
777
+ tileAmplitude: sanitizedValues.tileAmplitude,
778
+ gradientAmplitude: sanitizedValues.gradientAmplitude,
779
+ gradientOffset: sanitizedValues.gradientOffset,
780
+ tileOffsetX: sanitizedValues.tileOffsetX,
781
+ duration: sanitizedValues.duration
782
+ }),
783
+ [
784
+ sanitizedValues.duration,
785
+ sanitizedValues.gradientAmplitude,
786
+ sanitizedValues.gradientOffset,
787
+ sanitizedValues.tileAmplitude,
788
+ sanitizedValues.tileOffsetX
789
+ ]
790
+ );
791
+ const primeShowAnimation = useCallback4(() => {
792
+ const uniforms = shaderUniforms.current;
793
+ uniforms.uTileProgressHorizontal.value = 1 + timelineParams.tileAmplitude * 0.5;
794
+ uniforms.uGradientProgress.value = 1 + timelineParams.gradientOffset;
795
+ uniforms.uBlueProgress.value = 1 + timelineParams.tileOffsetX;
796
+ uniforms.uSaturation.value = 1;
797
+ uniforms.uWhiteTileStrength.value = 0;
798
+ }, [
799
+ timelineParams.gradientOffset,
800
+ timelineParams.tileAmplitude,
801
+ timelineParams.tileOffsetX
802
+ ]);
803
+ const buildTimelines = useCallback4(() => {
804
+ const uniforms = shaderUniforms.current;
805
+ const glitchDuration = sanitizedRef.current.duration;
806
+ const desiredShowTotal = glitchDuration * (SHOW_BASE_DURATION / SHOW_PRIMARY_DURATION);
807
+ const desiredHideTotal = glitchDuration * (HIDE_BASE_DURATION / SHOW_PRIMARY_DURATION);
808
+ const showScale = desiredShowTotal / SHOW_BASE_DURATION;
809
+ const hideScale = desiredHideTotal / HIDE_BASE_DURATION;
810
+ const showAt = (time) => time / SHOW_BASE_DURATION * desiredShowTotal;
811
+ const hideAt = (time) => time / HIDE_BASE_DURATION * desiredHideTotal;
812
+ showTimeline.current?.kill();
813
+ hideTimeline.current?.kill();
814
+ transitionTimeline.current?.kill();
815
+ const showTl = gsap.timeline({ paused: true });
816
+ showTl.add(() => {
817
+ animationVisibleRef.current = true;
818
+ updateVisibility();
819
+ }, 0);
820
+ showTl.fromTo(
821
+ uniforms.uTileProgressHorizontal,
822
+ { value: 1 + timelineParams.tileAmplitude * 0.5 },
823
+ {
824
+ value: -timelineParams.tileAmplitude * 0.5,
825
+ duration: glitchDuration,
826
+ ease: "sine.inOut"
827
+ },
828
+ 0
829
+ );
830
+ showTl.fromTo(
831
+ uniforms.uGradientProgress,
832
+ { value: 1 + timelineParams.gradientOffset },
833
+ {
834
+ value: -timelineParams.gradientOffset * 0.5 - timelineParams.gradientAmplitude,
835
+ duration: 0.6 * showScale,
836
+ ease: "sine.inOut"
837
+ },
838
+ showAt(0.1)
839
+ );
840
+ showTl.fromTo(
841
+ uniforms.uBlueProgress,
842
+ { value: 1 + timelineParams.tileOffsetX },
843
+ {
844
+ value: -timelineParams.tileOffsetX * 0.5 - timelineParams.gradientAmplitude,
845
+ duration: 0.6 * showScale,
846
+ ease: "sine.inOut"
847
+ },
848
+ showAt(0.125)
849
+ );
850
+ showTl.fromTo(
851
+ uniforms.uSaturation,
852
+ { value: 1 },
853
+ { value: 1.01, duration: 0.52 * showScale, ease: "sine.inOut" },
854
+ 0
855
+ );
856
+ showTl.fromTo(
857
+ uniforms.uSaturation,
858
+ { value: 1 },
859
+ { value: 2.5, duration: 0.1 * showScale, ease: "sine.inOut" },
860
+ showAt(0.32)
861
+ );
862
+ showTl.fromTo(
863
+ uniforms.uSaturation,
864
+ { value: 2.5 },
865
+ { value: 1, duration: 1.8 * showScale, ease: "sine.out" },
866
+ showAt(0.92)
867
+ );
868
+ showTl.fromTo(
869
+ uniforms.uWhiteTileStrength,
870
+ { value: 0 },
871
+ { value: 0.1, duration: 1 * showScale, ease: "sine.inOut" },
872
+ showAt(0.3)
873
+ );
874
+ const hideTl = gsap.timeline({ paused: true });
875
+ hideTl.fromTo(
876
+ uniforms.uWhiteTileStrength,
877
+ { value: 0.1 },
878
+ { value: 0, duration: 1 * hideScale, ease: "sine.inOut" },
879
+ 0
880
+ );
881
+ hideTl.fromTo(
882
+ uniforms.uSaturation,
883
+ { value: 1 },
884
+ { value: 1.5, duration: 0.4 * hideScale, ease: "sine.inOut" },
885
+ hideAt(0.4)
886
+ );
887
+ hideTl.fromTo(
888
+ uniforms.uSaturation,
889
+ { value: 1.5 },
890
+ { value: 1, duration: 1 * hideScale, ease: "sine.inOut" },
891
+ hideAt(0.8)
892
+ );
893
+ hideTl.fromTo(
894
+ uniforms.uBlueProgress,
895
+ {
896
+ value: -timelineParams.tileOffsetX * 0.5 - timelineParams.gradientAmplitude
897
+ },
898
+ {
899
+ value: 1 + timelineParams.tileOffsetX,
900
+ duration: 0.6 * hideScale,
901
+ ease: "sine.inOut"
902
+ },
903
+ hideAt(0.2)
904
+ );
905
+ hideTl.fromTo(
906
+ uniforms.uTileProgressHorizontal,
907
+ { value: -timelineParams.tileAmplitude * 0.5 },
908
+ {
909
+ value: 1 + timelineParams.tileAmplitude * 0.5,
910
+ duration: 0.9 * hideScale,
911
+ ease: "sine.inOut"
912
+ },
913
+ hideAt(0.2)
914
+ );
915
+ hideTl.fromTo(
916
+ uniforms.uGradientProgress,
917
+ {
918
+ value: -timelineParams.gradientOffset * 0.5 - timelineParams.gradientAmplitude
919
+ },
920
+ {
921
+ value: 1 + timelineParams.gradientOffset,
922
+ duration: 0.6 * hideScale,
923
+ ease: "sine.inOut"
924
+ },
925
+ hideAt(0.225)
926
+ );
927
+ hideTl.add(() => {
928
+ animationVisibleRef.current = false;
929
+ updateVisibility();
930
+ onHideDone?.();
931
+ }, hideAt(HIDE_BASE_DURATION));
932
+ showTl.call(
933
+ () => {
934
+ onShowDone?.();
935
+ },
936
+ void 0,
937
+ showAt(0.65)
938
+ );
939
+ showTimeline.current = showTl;
940
+ hideTimeline.current = hideTl;
941
+ }, [onHideDone, onShowDone, timelineParams, updateVisibility]);
942
+ useEffect5(() => {
943
+ const uniforms = shaderUniforms.current;
944
+ uniforms.tDiffuse.value = diffuseTexture;
945
+ if (gradientTextureRef.current) {
946
+ uniforms.tGradient.value = gradientTextureRef.current;
947
+ }
948
+ uniforms.uTileAmplitude.value = sanitizedValues.tileAmplitude;
949
+ uniforms.uTileOffset.value.set(
950
+ sanitizedValues.tileOffsetX,
951
+ sanitizedValues.tileOffsetY
952
+ );
953
+ uniforms.uTileFrequency.value.set(
954
+ sanitizedValues.tileFrequencyX,
955
+ sanitizedValues.tileFrequencyY
956
+ );
957
+ uniforms.uWaveAmplitude.value = sanitizedValues.waveAmplitude;
958
+ uniforms.uWaveProgress.value = sanitizedValues.waveProgress;
959
+ uniforms.uWaveStrength.value.set(
960
+ sanitizedValues.waveStrengthX,
961
+ sanitizedValues.waveStrengthY
962
+ );
963
+ uniforms.uGradientProgress.value = sanitizedValues.gradientProgress;
964
+ uniforms.uGradientOffset.value = sanitizedValues.gradientOffset;
965
+ uniforms.uGradientAmplitude.value = sanitizedValues.gradientAmplitude;
966
+ uniforms.uBlueProgress.value = sanitizedValues.blueProgress;
967
+ uniforms.uBlueAmplitude.value = sanitizedValues.blueAmplitude;
968
+ uniforms.uWhiteTileChances.value = sanitizedValues.whiteTileChances;
969
+ uniforms.uWhiteTileFrequency.value = sanitizedValues.whiteTileFrequency;
970
+ uniforms.uWhiteTileStrength.value = sanitizedValues.whiteTileStrength;
971
+ uniforms.uSaturation.value = sanitizedValues.saturation;
972
+ uniforms.uTileProgressVertical.value = BASE_TRANSITION_PROGRESS;
973
+ updateMeshScale();
974
+ updateVisibility();
975
+ }, [diffuseTexture, sanitizedValues, updateMeshScale, updateVisibility]);
976
+ useEffect5(() => {
977
+ buildTimelines();
978
+ return () => {
979
+ showTimeline.current?.kill();
980
+ hideTimeline.current?.kill();
981
+ transitionTimeline.current?.kill();
982
+ };
983
+ }, [buildTimelines]);
984
+ useEffect5(() => {
985
+ const mesh = assetsRef.current?.mesh;
986
+ if (mesh) {
987
+ mesh.visible = sanitizedValues.debug || animationVisibleRef.current;
988
+ }
989
+ }, [sanitizedValues.debug]);
990
+ const lastShowSignal = useRef5(settings.showSignal);
991
+ useEffect5(() => {
992
+ if (settings.showSignal === lastShowSignal.current) {
993
+ return;
994
+ }
995
+ lastShowSignal.current = settings.showSignal;
996
+ hideTimeline.current?.pause(0);
997
+ primeShowAnimation();
998
+ showTimeline.current?.seek(0).play();
999
+ }, [primeShowAnimation, settings.showSignal]);
1000
+ const lastHideSignal = useRef5(settings.hideSignal);
1001
+ useEffect5(() => {
1002
+ if (settings.hideSignal === lastHideSignal.current) {
1003
+ return;
1004
+ }
1005
+ lastHideSignal.current = settings.hideSignal;
1006
+ showTimeline.current?.pause(0);
1007
+ hideTimeline.current?.seek(0).play();
1008
+ }, [settings.hideSignal]);
1009
+ return /* @__PURE__ */ jsx4(
1010
+ "div",
1011
+ {
1012
+ ref: containerRef,
1013
+ className,
1014
+ style: {
1015
+ width: width ?? "100%",
1016
+ height: height ?? "100%",
1017
+ ...style
1018
+ },
1019
+ ...divProps
1020
+ }
1021
+ );
1022
+ }
1023
+ function createDiffuseTexture() {
1024
+ const size = 256;
1025
+ const data = new Uint8Array(size * size * 4);
1026
+ for (let y = 0; y < size; y += 1) {
1027
+ for (let x = 0; x < size; x += 1) {
1028
+ const i = (y * size + x) * 4;
1029
+ const stripe = Math.sin(y / size * Math.PI * 8) * 0.5 + 0.5;
1030
+ const tint = 0.25 + Math.sin(x / size * Math.PI * 2) * 0.15;
1031
+ const base = THREE5.MathUtils.clamp(stripe * 0.7 + tint, 0, 1);
1032
+ data[i] = base * 255;
1033
+ data[i + 1] = base * 200;
1034
+ data[i + 2] = base * 150;
1035
+ data[i + 3] = 255;
1036
+ }
1037
+ }
1038
+ const texture = new THREE5.DataTexture(data, size, size, THREE5.RGBAFormat);
1039
+ texture.needsUpdate = true;
1040
+ texture.colorSpace = THREE5.SRGBColorSpace;
1041
+ texture.wrapS = THREE5.RepeatWrapping;
1042
+ texture.wrapT = THREE5.RepeatWrapping;
1043
+ texture.magFilter = THREE5.LinearFilter;
1044
+ texture.minFilter = THREE5.LinearMipmapLinearFilter;
1045
+ texture.generateMipmaps = true;
1046
+ return texture;
1047
+ }
528
1048
  export {
529
1049
  FractalFlower,
1050
+ MenuGlitch,
530
1051
  OranoParticles,
531
1052
  ShaderArt
532
1053
  };