Spaces:
Running
Running
| document.addEventListener('DOMContentLoaded', () => { | |
| // --- 1. ANIMATION SETUP (GSAP) --- | |
| // Custom "SplitText" implementation for letters/lines since we don't have the plugin | |
| const h1 = document.getElementById('hero-title'); | |
| const p = document.getElementById('hero-desc'); | |
| const ctas = document.getElementById('hero-ctas'); | |
| const bgContainer = document.getElementById('canvas-container'); | |
| // Helper to wrap lines/words (Simple version: just treat whole block for now to ensure smoothness, | |
| // or split by words if user wants granulatiry. Let's do simple fade up for stability). | |
| // Initial States | |
| gsap.set(bgContainer, { filter: "blur(28px)" }); | |
| gsap.set([h1, p, ctas], { opacity: 0, y: 24, filter: "blur(8px)" }); | |
| // Timeline | |
| const tl = gsap.timeline({ defaults: { ease: "power2.out" } }); | |
| tl.to(bgContainer, { filter: "blur(0px)", duration: 1.5 }, 0) | |
| .to(h1, { opacity: 1, y: 0, filter: "blur(0px)", duration: 1 }, 0.3) | |
| .to(p, { opacity: 1, y: 0, filter: "blur(0px)", duration: 1 }, 0.5) | |
| .to(ctas, { opacity: 1, y: 0, filter: "blur(0px)", duration: 0.8 }, 0.7); | |
| // --- 2. THREE.JS SHADER SETUP --- | |
| const container = document.getElementById('canvas-container'); | |
| // Scence Setup | |
| const scene = new THREE.Scene(); | |
| const camera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0.1, 10); | |
| camera.position.z = 1; | |
| const renderer = new THREE.WebGLRenderer({ alpha: true }); | |
| renderer.setSize(window.innerWidth, window.innerHeight); | |
| container.appendChild(renderer.domElement); | |
| // Shader Code (Ported from React component) | |
| const vertexShader = ` | |
| varying vec2 vUv; | |
| void main() { | |
| vUv = uv; | |
| gl_Position = vec4(position, 1.0); | |
| } | |
| `; | |
| const fragmentShader = ` | |
| precision highp float; | |
| varying vec2 vUv; | |
| uniform float u_time; | |
| uniform vec3 u_resolution; | |
| #define STEP 256 | |
| #define EPS .001 | |
| float smin( float a, float b, float k ) { | |
| float h = clamp( 0.5+0.5*(b-a)/k, 0.0, 1.0 ); | |
| return mix( b, a, h ) - k*h*(1.0-h); | |
| } | |
| const mat2 m = mat2(.8,.6,-.6,.8); | |
| float noise( in vec2 x ) { | |
| return sin(1.5*x.x)*sin(1.5*x.y); | |
| } | |
| float fbm6( vec2 p ) { | |
| float f = 0.0; | |
| f += 0.500000*(0.5+0.5*noise( p )); p = m*p*2.02; | |
| f += 0.250000*(0.5+0.5*noise( p )); p = m*p*2.03; | |
| f += 0.125000*(0.5+0.5*noise( p )); p = m*p*2.01; | |
| f += 0.062500*(0.5+0.5*noise( p )); p = m*p*2.04; | |
| f += 0.015625*(0.5+0.5*noise( p )); | |
| return f/0.96875; | |
| } | |
| mat2 getRot(float a) { | |
| float sa = sin(a), ca = cos(a); | |
| return mat2(ca,-sa,sa,ca); | |
| } | |
| vec3 _position; | |
| float sphere(vec3 center, float radius) { | |
| return distance(_position,center) - radius; | |
| } | |
| float swingPlane(float height) { | |
| vec3 pos = _position + vec3(0.,0.,u_time * 5.5); | |
| float def = fbm6(pos.xz * .25) * 0.5; | |
| float way = pow(abs(pos.x) * 34. ,2.5) *.0000125; | |
| def *= way; | |
| float ch = height + def; | |
| return max(pos.y - ch,0.); | |
| } | |
| float map(vec3 pos) { | |
| _position = pos; | |
| float dist; | |
| dist = swingPlane(0.); | |
| float sminFactor = 5.25; | |
| dist = smin(dist,sphere(vec3(0.,-15.,80.),60.),sminFactor); | |
| return dist; | |
| } | |
| void mainImage( out vec4 fragColor, in vec2 fragCoord ) { | |
| vec2 uv = (fragCoord.xy-.5*u_resolution.xy)/u_resolution.y; | |
| vec3 rayOrigin = vec3(uv + vec2(0.,6.), -1. ); | |
| vec3 rayDir = normalize(vec3(uv , 1.)); | |
| rayDir.zy = getRot(.15) * rayDir.zy; | |
| vec3 position = rayOrigin; | |
| float curDist; | |
| int nbStep = 0; | |
| for(int i = 0; i < STEP; i++) { | |
| // Approximate texture fetch simply as noise or remove if unneeded | |
| // The original code had texture(iChannel0...) which we don't have. | |
| // We'll skip the texture displacement for simplicity in vanilla port | |
| // or replicate with simple noise if needed. | |
| // Simplified raymarching step: | |
| curDist = map(position); | |
| if(curDist < EPS) break; | |
| position += rayDir * curDist * .5; | |
| nbStep++; | |
| } | |
| float f; | |
| float dist = distance(rayOrigin,position); | |
| f = dist /(98.); | |
| f = float(nbStep) / float(STEP); | |
| f *= .9; | |
| vec3 col = vec3(f); | |
| fragColor = vec4(col,1.0); | |
| } | |
| void main() { | |
| mainImage(gl_FragColor, vUv * u_resolution.xy); | |
| } | |
| `; | |
| // Uniforms | |
| const uniforms = { | |
| u_time: { value: 0 }, | |
| u_resolution: { value: new THREE.Vector3(window.innerWidth, window.innerHeight, 1) } | |
| }; | |
| // Mesh | |
| const geometry = new THREE.PlaneGeometry(2, 2); | |
| const material = new THREE.ShaderMaterial({ | |
| vertexShader, | |
| fragmentShader, | |
| uniforms | |
| }); | |
| const mesh = new THREE.Mesh(geometry, material); | |
| scene.add(mesh); | |
| // Resize Handler | |
| window.addEventListener('resize', () => { | |
| renderer.setSize(window.innerWidth, window.innerHeight); | |
| uniforms.u_resolution.value.set(window.innerWidth, window.innerHeight, 1); | |
| }); | |
| // Animation Loop | |
| const clock = new THREE.Clock(); // Use THREE clock for u_time | |
| function animate() { | |
| requestAnimationFrame(animate); | |
| uniforms.u_time.value = clock.getElapsedTime() * 0.5; // Match React's * 0.5 | |
| renderer.render(scene, camera); | |
| } | |
| animate(); | |
| }); | |