'use client'; import { cls } from '@/lib/utils'; import { Canvas, useFrame, useThree } from '@react-three/fiber'; import { useMemo, useRef, useCallback, memo } from 'react'; import * as THREE from 'three'; interface CanvasRevealEffectProps { animationSpeed?: number; opacities?: number[]; colors?: number[][]; containerClassName?: string; dotSize?: number; showGradient?: boolean; } const CanvasRevealEffect = ({ animationSpeed = 0.4, opacities = [0.2, 0.2, 0.2, 0.4, 0.4, 0.4, 0.7, 0.6, 0.6, 0.9], colors = [[0, 255, 255]], containerClassName = "", dotSize = 3, showGradient = true, }: CanvasRevealEffectProps) => { return (
{showGradient && (
)}
); }; interface DotMatrixProps { colors?: number[][]; opacities?: number[]; totalSize?: number; dotSize?: number; shader?: string; center?: ('x' | 'y')[]; } const DotMatrix = ({ colors = [[0, 0, 0]], opacities = [0.04, 0.04, 0.04, 0.04, 0.04, 0.08, 0.08, 0.08, 0.08, 0.14], totalSize = 4, dotSize = 2, shader = '', center = ['x', 'y'], }: DotMatrixProps) => { const uniforms = useMemo(() => { let colorsArray = [ colors[0], colors[0], colors[0], colors[0], colors[0], colors[0], ]; if (colors.length === 2) { colorsArray = [ colors[0], colors[0], colors[0], colors[1], colors[1], colors[1], ]; } else if (colors.length === 3) { colorsArray = [ colors[0], colors[0], colors[1], colors[1], colors[2], colors[2], ]; } return { u_colors: { value: colorsArray.map((color) => [ color[0] / 255, color[1] / 255, color[2] / 255, ]), type: 'uniform3fv', }, u_opacities: { value: opacities, type: 'uniform1fv', }, u_total_size: { value: totalSize, type: 'uniform1f', }, u_dot_size: { value: dotSize, type: 'uniform1f', }, }; }, [colors, opacities, totalSize, dotSize]); return ( ); }; type Uniforms = { [key: string]: { value: number[] | number[][] | number; type: string; }; }; const ShaderMaterial = ({ source, uniforms, maxFps = 60, }: { source: string; maxFps?: number; uniforms: Uniforms; }) => { const { size } = useThree(); const ref = useRef(null); let lastFrameTime = 0; useFrame(({ clock }) => { if (!ref.current) return; const timestamp = clock.getElapsedTime(); if (timestamp - lastFrameTime < 1 / maxFps) { return; } lastFrameTime = timestamp; const material = ref.current.material as THREE.ShaderMaterial; const timeLocation = material.uniforms.u_time; timeLocation.value = timestamp; }); const getUniforms = useCallback(() => { const preparedUniforms: Record = {}; for (const uniformName in uniforms) { const uniform = uniforms[uniformName] as { type: string; value: number | number[] | number[][] }; switch (uniform.type) { case 'uniform1f': preparedUniforms[uniformName] = { value: uniform.value, type: '1f' }; break; case 'uniform3f': preparedUniforms[uniformName] = { value: new THREE.Vector3().fromArray(uniform.value as number[]), type: '3f', }; break; case 'uniform1fv': preparedUniforms[uniformName] = { value: uniform.value, type: '1fv' }; break; case 'uniform3fv': preparedUniforms[uniformName] = { value: (uniform.value as number[][]).map((v: number[]) => new THREE.Vector3().fromArray(v) ), type: '3fv', }; break; case 'uniform2f': preparedUniforms[uniformName] = { value: new THREE.Vector2().fromArray(uniform.value as number[]), type: '2f', }; break; default: console.error(`Invalid uniform type for '${uniformName}'.`); break; } } preparedUniforms['u_time'] = { value: 0, type: '1f' }; preparedUniforms['u_resolution'] = { value: new THREE.Vector2(size.width * 2, size.height * 2), }; return preparedUniforms; }, [uniforms, size.width, size.height]); const material = useMemo(() => { const materialObject = new THREE.ShaderMaterial({ vertexShader: ` precision mediump float; in vec2 coordinates; uniform vec2 u_resolution; out vec2 fragCoord; void main(){ float x = position.x; float y = position.y; gl_Position = vec4(x, y, 0.0, 1.0); fragCoord = (position.xy + vec2(1.0)) * 0.5 * u_resolution; fragCoord.y = u_resolution.y - fragCoord.y; } `, fragmentShader: source, uniforms: getUniforms(), glslVersion: THREE.GLSL3, blending: THREE.CustomBlending, blendSrc: THREE.SrcAlphaFactor, blendDst: THREE.OneFactor, }); return materialObject; }, [source, getUniforms]); return ( }> ); }; interface ShaderProps { source: string; uniforms: Uniforms; maxFps?: number; } const Shader = ({ source, uniforms, maxFps = 60 }: ShaderProps) => { return ( ); }; CanvasRevealEffect.displayName = 'CanvasRevealEffect'; export default memo(CanvasRevealEffect);