Spaces:
Running
Running
Download InteractiveWaves.jsx from ishraddhapawar/openenv: direct link, hf CLI and curl.
- Browser
- Download file 9.73 kB
-
https://huggingface.co/spaces/ishraddhapawar/openenv/resolve/main/InteractiveWaves.jsx
- Command line
-
hf download hf://spaces/ishraddhapawar/openenv/InteractiveWaves.jsx
-
curl -L -o InteractiveWaves.jsx https://huggingface.co/spaces/ishraddhapawar/openenv/resolve/main/InteractiveWaves.jsx
9.73 kB
| /** | |
| * InteractiveWavesBackground β Perlin noise canvas waves | |
| * Converted from TypeScript/Tailwind to JSX/vanilla CSS | |
| */ | |
| import React, { useRef, useEffect } from 'react'; | |
| // ββ Perlin Noise Engine βββββββββββββββββββββββββββββββββββββββββ | |
| class Grad { | |
| constructor(x, y, z) { this.x = x; this.y = y; this.z = z; } | |
| dot2(x, y) { return this.x * x + this.y * y; } | |
| } | |
| class Noise { | |
| constructor(seed = 0) { | |
| this.grad3 = [ | |
| new Grad(1,1,0), new Grad(-1,1,0), new Grad(1,-1,0), new Grad(-1,-1,0), | |
| new Grad(1,0,1), new Grad(-1,0,1), new Grad(1,0,-1), new Grad(-1,0,-1), | |
| new Grad(0,1,1), new Grad(0,-1,1), new Grad(0,1,-1), new Grad(0,-1,-1) | |
| ]; | |
| this.p = [151,160,137,91,90,15,131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240, | |
| 21,10,23,190,6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,88, | |
| 237,149,56,87,174,20,125,136,171,168,68,175,74,165,71,134,139,48,27,166,77,146,158,231,83, | |
| 111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,102,143,54,65,25,63,161,1,216, | |
| 80,73,209,76,132,187,208,89,18,169,200,196,135,130,116,188,159,86,164,100,109,198,173,186, | |
| 3,64,52,217,226,250,124,123,5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58, | |
| 17,182,189,28,42,223,183,170,213,119,248,152,2,44,154,163,70,221,153,101,155,167,43,172,9, | |
| 129,22,39,253,19,98,108,110,79,113,224,232,178,185,112,104,218,246,97,228,251,34,242,193, | |
| 238,210,144,12,191,179,162,241,81,51,145,235,249,14,239,107,49,192,214,31,181,199,106,157, | |
| 184,84,204,176,115,121,50,45,127,4,150,254,138,236,205,93,222,114,67,29,24,72,243,141,128, | |
| 195,78,66,215,61,156,180]; | |
| this.perm = new Array(512); | |
| this.gradP = new Array(512); | |
| this.seed(seed); | |
| } | |
| seed(seed) { | |
| if (seed > 0 && seed < 1) seed *= 65536; | |
| seed = Math.floor(seed); | |
| if (seed < 256) seed |= seed << 8; | |
| for (let i = 0; i < 256; i++) { | |
| let v = i & 1 ? this.p[i] ^ (seed & 255) : this.p[i] ^ ((seed >> 8) & 255); | |
| this.perm[i] = this.perm[i + 256] = v; | |
| this.gradP[i] = this.gradP[i + 256] = this.grad3[v % 12]; | |
| } | |
| } | |
| fade(t) { return t * t * t * (t * (t * 6 - 15) + 10); } | |
| lerp(a, b, t) { return (1 - t) * a + t * b; } | |
| perlin2(x, y) { | |
| let X = Math.floor(x), Y = Math.floor(y); | |
| x -= X; y -= Y; X &= 255; Y &= 255; | |
| const n00 = this.gradP[X + this.perm[Y]].dot2(x, y); | |
| const n01 = this.gradP[X + this.perm[Y + 1]].dot2(x, y - 1); | |
| const n10 = this.gradP[X + 1 + this.perm[Y]].dot2(x - 1, y); | |
| const n11 = this.gradP[X + 1 + this.perm[Y + 1]].dot2(x - 1, y - 1); | |
| const u = this.fade(x); | |
| return this.lerp(this.lerp(n00, n10, u), this.lerp(n01, n11, u), this.fade(y)); | |
| } | |
| } | |
| // ββ Component βββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| export default function InteractiveWavesBackground({ | |
| lineColor = 'rgba(61, 112, 104, 0.15)', | |
| backgroundColor = 'transparent', | |
| waveSpeedX = 0.0125, | |
| waveSpeedY = 0.005, | |
| waveAmpX = 32, | |
| waveAmpY = 16, | |
| xGap = 10, | |
| yGap = 32, | |
| friction = 0.925, | |
| tension = 0.005, | |
| maxCursorMove = 100, | |
| }) { | |
| const containerRef = useRef(null); | |
| const canvasRef = useRef(null); | |
| const ctxRef = useRef(null); | |
| const boundingRef = useRef({ width: 0, height: 0, left: 0, top: 0 }); | |
| const noiseRef = useRef(new Noise(Math.random())); | |
| const linesRef = useRef([]); | |
| const mouseRef = useRef({ x: -10, y: 0, lx: 0, ly: 0, sx: 0, sy: 0, v: 0, vs: 0, a: 0, set: false }); | |
| const configRef = useRef({ lineColor, waveSpeedX, waveSpeedY, waveAmpX, waveAmpY, friction, tension, maxCursorMove, xGap, yGap }); | |
| const frameIdRef = useRef(null); | |
| useEffect(() => { | |
| configRef.current = { lineColor, waveSpeedX, waveSpeedY, waveAmpX, waveAmpY, friction, tension, maxCursorMove, xGap, yGap }; | |
| }, [lineColor, waveSpeedX, waveSpeedY, waveAmpX, waveAmpY, friction, tension, maxCursorMove, xGap, yGap]); | |
| useEffect(() => { | |
| const canvas = canvasRef.current; | |
| const container = containerRef.current; | |
| if (!canvas || !container) return; | |
| ctxRef.current = canvas.getContext('2d'); | |
| function setSize() { | |
| const rect = container.getBoundingClientRect(); | |
| boundingRef.current = { width: rect.width, height: rect.height, left: rect.left, top: rect.top }; | |
| canvas.width = rect.width; | |
| canvas.height = rect.height; | |
| } | |
| function setLines() { | |
| const { width, height } = boundingRef.current; | |
| linesRef.current = []; | |
| const oW = width + 200, oH = height + 30; | |
| const { xGap, yGap } = configRef.current; | |
| const totalLines = Math.ceil(oW / xGap); | |
| const totalPoints = Math.ceil(oH / yGap); | |
| const xStart = (width - xGap * totalLines) / 2; | |
| const yStart = (height - yGap * totalPoints) / 2; | |
| for (let i = 0; i <= totalLines; i++) { | |
| const pts = []; | |
| for (let j = 0; j <= totalPoints; j++) { | |
| pts.push({ x: xStart + xGap * i, y: yStart + yGap * j, wave: { x: 0, y: 0 }, cursor: { x: 0, y: 0, vx: 0, vy: 0 } }); | |
| } | |
| linesRef.current.push(pts); | |
| } | |
| } | |
| function movePoints(time) { | |
| const lines = linesRef.current; | |
| const mouse = mouseRef.current; | |
| const noise = noiseRef.current; | |
| const cfg = configRef.current; | |
| lines.forEach(pts => { | |
| pts.forEach(p => { | |
| const move = noise.perlin2((p.x + time * cfg.waveSpeedX) * 0.002, (p.y + time * cfg.waveSpeedY) * 0.0015) * 12; | |
| p.wave.x = Math.cos(move) * cfg.waveAmpX; | |
| p.wave.y = Math.sin(move) * cfg.waveAmpY; | |
| const dx = p.x - mouse.sx, dy = p.y - mouse.sy; | |
| const dist = Math.hypot(dx, dy); | |
| const l = Math.max(175, mouse.vs); | |
| if (dist < l) { | |
| const s = 1 - dist / l; | |
| const f = Math.cos(dist * 0.001) * s; | |
| p.cursor.vx += Math.cos(mouse.a) * f * l * mouse.vs * 0.00065; | |
| p.cursor.vy += Math.sin(mouse.a) * f * l * mouse.vs * 0.00065; | |
| } | |
| p.cursor.vx += (0 - p.cursor.x) * cfg.tension; | |
| p.cursor.vy += (0 - p.cursor.y) * cfg.tension; | |
| p.cursor.vx *= cfg.friction; | |
| p.cursor.vy *= cfg.friction; | |
| p.cursor.x += p.cursor.vx * 2; | |
| p.cursor.y += p.cursor.vy * 2; | |
| p.cursor.x = Math.min(cfg.maxCursorMove, Math.max(-cfg.maxCursorMove, p.cursor.x)); | |
| p.cursor.y = Math.min(cfg.maxCursorMove, Math.max(-cfg.maxCursorMove, p.cursor.y)); | |
| }); | |
| }); | |
| } | |
| function moved(point, withCursor = true) { | |
| return { | |
| x: Math.round((point.x + point.wave.x + (withCursor ? point.cursor.x : 0)) * 10) / 10, | |
| y: Math.round((point.y + point.wave.y + (withCursor ? point.cursor.y : 0)) * 10) / 10, | |
| }; | |
| } | |
| function drawLines() { | |
| const { width, height } = boundingRef.current; | |
| const ctx = ctxRef.current; | |
| if (!ctx) return; | |
| ctx.clearRect(0, 0, width, height); | |
| ctx.beginPath(); | |
| ctx.strokeStyle = configRef.current.lineColor; | |
| linesRef.current.forEach(points => { | |
| let p1 = moved(points[0], false); | |
| ctx.moveTo(p1.x, p1.y); | |
| points.forEach((p, idx) => { | |
| const isLast = idx === points.length - 1; | |
| p1 = moved(p, !isLast); | |
| const p2 = moved(points[idx + 1] || points[points.length - 1], !isLast); | |
| ctx.lineTo(p1.x, p1.y); | |
| if (isLast) ctx.moveTo(p2.x, p2.y); | |
| }); | |
| }); | |
| ctx.stroke(); | |
| } | |
| function tick(t) { | |
| const mouse = mouseRef.current; | |
| mouse.sx += (mouse.x - mouse.sx) * 0.1; | |
| mouse.sy += (mouse.y - mouse.sy) * 0.1; | |
| const dx = mouse.x - mouse.lx, dy = mouse.y - mouse.ly; | |
| mouse.v = Math.hypot(dx, dy); | |
| mouse.vs += (mouse.v - mouse.vs) * 0.1; | |
| mouse.vs = Math.min(100, mouse.vs); | |
| mouse.lx = mouse.x; | |
| mouse.ly = mouse.y; | |
| mouse.a = Math.atan2(dy, dx); | |
| movePoints(t); | |
| drawLines(); | |
| frameIdRef.current = requestAnimationFrame(tick); | |
| } | |
| function onResize() { setSize(); setLines(); } | |
| function onMouseMove(e) { | |
| const mouse = mouseRef.current; | |
| const b = boundingRef.current; | |
| mouse.x = e.clientX - b.left; | |
| mouse.y = e.clientY - b.top; | |
| if (!mouse.set) { mouse.sx = mouse.x; mouse.sy = mouse.y; mouse.lx = mouse.x; mouse.ly = mouse.y; mouse.set = true; } | |
| } | |
| function onTouchMove(e) { | |
| const t = e.touches[0]; | |
| const mouse = mouseRef.current; | |
| const b = boundingRef.current; | |
| mouse.x = t.clientX - b.left; | |
| mouse.y = t.clientY - b.top; | |
| if (!mouse.set) { mouse.sx = mouse.x; mouse.sy = mouse.y; mouse.lx = mouse.x; mouse.ly = mouse.y; mouse.set = true; } | |
| } | |
| setSize(); | |
| setLines(); | |
| frameIdRef.current = requestAnimationFrame(tick); | |
| window.addEventListener('resize', onResize); | |
| window.addEventListener('mousemove', onMouseMove); | |
| window.addEventListener('touchmove', onTouchMove, { passive: false }); | |
| return () => { | |
| window.removeEventListener('resize', onResize); | |
| window.removeEventListener('mousemove', onMouseMove); | |
| window.removeEventListener('touchmove', onTouchMove); | |
| if (frameIdRef.current !== null) cancelAnimationFrame(frameIdRef.current); | |
| }; | |
| }, []); | |
| return ( | |
| <div | |
| ref={containerRef} | |
| style={{ | |
| position: 'fixed', | |
| top: 0, | |
| left: 0, | |
| width: '100%', | |
| height: '100%', | |
| overflow: 'hidden', | |
| backgroundColor, | |
| zIndex: 0, | |
| }} | |
| > | |
| <canvas ref={canvasRef} style={{ display: 'block', width: '100%', height: '100%' }} /> | |
| </div> | |
| ); | |
| } | |