-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Expand file tree
/
Copy pathColorBends-JS-TW.json
More file actions
18 lines (18 loc) · 10.3 KB
/
ColorBends-JS-TW.json
File metadata and controls
18 lines (18 loc) · 10.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
"name": "ColorBends-JS-TW",
"title": "ColorBends",
"description": "Vibrant color bends with smooth flowing animation.",
"type": "registry:component",
"files": [
{
"type": "registry:component",
"path": "ColorBends/ColorBends.jsx",
"content": "import { useEffect, useRef } from 'react';\nimport * as THREE from 'three';\n\nconst MAX_COLORS = 8;\n\nconst frag = `\n#define MAX_COLORS ${MAX_COLORS}\nuniform vec2 uCanvas;\nuniform float uTime;\nuniform float uSpeed;\nuniform vec2 uRot;\nuniform int uColorCount;\nuniform vec3 uColors[MAX_COLORS];\nuniform int uTransparent;\nuniform float uScale;\nuniform float uFrequency;\nuniform float uWarpStrength;\nuniform vec2 uPointer; // in NDC [-1,1]\nuniform float uMouseInfluence;\nuniform float uParallax;\nuniform float uNoise;\nvarying vec2 vUv;\n\nvoid main() {\n float t = uTime * uSpeed;\n vec2 p = vUv * 2.0 - 1.0;\n p += uPointer * uParallax * 0.1;\n vec2 rp = vec2(p.x * uRot.x - p.y * uRot.y, p.x * uRot.y + p.y * uRot.x);\n vec2 q = vec2(rp.x * (uCanvas.x / uCanvas.y), rp.y);\n q /= max(uScale, 0.0001);\n q /= 0.5 + 0.2 * dot(q, q);\n q += 0.2 * cos(t) - 7.56;\n vec2 toward = (uPointer - rp);\n q += toward * uMouseInfluence * 0.2;\n\n vec3 col = vec3(0.0);\n float a = 1.0;\n\n if (uColorCount > 0) {\n vec2 s = q;\n vec3 sumCol = vec3(0.0);\n float cover = 0.0;\n for (int i = 0; i < MAX_COLORS; ++i) {\n if (i >= uColorCount) break;\n s -= 0.01;\n vec2 r = sin(1.5 * (s.yx * uFrequency) + 2.0 * cos(s * uFrequency));\n float m0 = length(r + sin(5.0 * r.y * uFrequency - 3.0 * t + float(i)) / 4.0);\n float kBelow = clamp(uWarpStrength, 0.0, 1.0);\n float kMix = pow(kBelow, 0.3); // strong response across 0..1\n float gain = 1.0 + max(uWarpStrength - 1.0, 0.0); // allow >1 to amplify displacement\n vec2 disp = (r - s) * kBelow;\n vec2 warped = s + disp * gain;\n float m1 = length(warped + sin(5.0 * warped.y * uFrequency - 3.0 * t + float(i)) / 4.0);\n float m = mix(m0, m1, kMix);\n float w = 1.0 - exp(-6.0 / exp(6.0 * m));\n sumCol += uColors[i] * w;\n cover = max(cover, w);\n }\n col = clamp(sumCol, 0.0, 1.0);\n a = uTransparent > 0 ? cover : 1.0;\n } else {\n vec2 s = q;\n for (int k = 0; k < 3; ++k) {\n s -= 0.01;\n vec2 r = sin(1.5 * (s.yx * uFrequency) + 2.0 * cos(s * uFrequency));\n float m0 = length(r + sin(5.0 * r.y * uFrequency - 3.0 * t + float(k)) / 4.0);\n float kBelow = clamp(uWarpStrength, 0.0, 1.0);\n float kMix = pow(kBelow, 0.3);\n float gain = 1.0 + max(uWarpStrength - 1.0, 0.0);\n vec2 disp = (r - s) * kBelow;\n vec2 warped = s + disp * gain;\n float m1 = length(warped + sin(5.0 * warped.y * uFrequency - 3.0 * t + float(k)) / 4.0);\n float m = mix(m0, m1, kMix);\n col[k] = 1.0 - exp(-6.0 / exp(6.0 * m));\n }\n a = uTransparent > 0 ? max(max(col.r, col.g), col.b) : 1.0;\n }\n\n if (uNoise > 0.0001) {\n float n = fract(sin(dot(gl_FragCoord.xy + vec2(uTime), vec2(12.9898, 78.233))) * 43758.5453123);\n col += (n - 0.5) * uNoise;\n col = clamp(col, 0.0, 1.0);\n }\n\n vec3 rgb = (uTransparent > 0) ? col * a : col;\n gl_FragColor = vec4(rgb, a);\n}\n`;\n\nconst vert = `\nvarying vec2 vUv;\nvoid main() {\n vUv = uv;\n gl_Position = vec4(position, 1.0);\n}\n`;\n\nexport default function ColorBends({\n className,\n style,\n rotation = 45,\n speed = 0.2,\n colors = [],\n transparent = true,\n autoRotate = 0,\n scale = 1,\n frequency = 1,\n warpStrength = 1,\n mouseInfluence = 1,\n parallax = 0.5,\n noise = 0.1\n}) {\n const containerRef = useRef(null);\n const rendererRef = useRef(null);\n const rafRef = useRef(null);\n const materialRef = useRef(null);\n const resizeObserverRef = useRef(null);\n const rotationRef = useRef(rotation);\n const autoRotateRef = useRef(autoRotate);\n const pointerTargetRef = useRef(new THREE.Vector2(0, 0));\n const pointerCurrentRef = useRef(new THREE.Vector2(0, 0));\n const pointerSmoothRef = useRef(8);\n\n useEffect(() => {\n const container = containerRef.current;\n const scene = new THREE.Scene();\n const camera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1);\n\n const geometry = new THREE.PlaneGeometry(2, 2);\n const uColorsArray = Array.from({ length: MAX_COLORS }, () => new THREE.Vector3(0, 0, 0));\n const material = new THREE.ShaderMaterial({\n vertexShader: vert,\n fragmentShader: frag,\n uniforms: {\n uCanvas: { value: new THREE.Vector2(1, 1) },\n uTime: { value: 0 },\n uSpeed: { value: speed },\n uRot: { value: new THREE.Vector2(1, 0) },\n uColorCount: { value: 0 },\n uColors: { value: uColorsArray },\n uTransparent: { value: transparent ? 1 : 0 },\n uScale: { value: scale },\n uFrequency: { value: frequency },\n uWarpStrength: { value: warpStrength },\n uPointer: { value: new THREE.Vector2(0, 0) },\n uMouseInfluence: { value: mouseInfluence },\n uParallax: { value: parallax },\n uNoise: { value: noise }\n },\n premultipliedAlpha: true,\n transparent: true\n });\n materialRef.current = material;\n\n const mesh = new THREE.Mesh(geometry, material);\n scene.add(mesh);\n\n const renderer = new THREE.WebGLRenderer({\n antialias: false,\n powerPreference: 'high-performance',\n alpha: true\n });\n rendererRef.current = renderer;\n // Three r152+ uses outputColorSpace and SRGBColorSpace\n renderer.outputColorSpace = THREE.SRGBColorSpace;\n renderer.setPixelRatio(Math.min(window.devicePixelRatio || 1, 2));\n renderer.setClearColor(0x000000, transparent ? 0 : 1);\n renderer.domElement.style.width = '100%';\n renderer.domElement.style.height = '100%';\n renderer.domElement.style.display = 'block';\n container.appendChild(renderer.domElement);\n\n const clock = new THREE.Clock();\n\n const handleResize = () => {\n const w = container.clientWidth || 1;\n const h = container.clientHeight || 1;\n renderer.setSize(w, h, false);\n material.uniforms.uCanvas.value.set(w, h);\n };\n\n handleResize();\n\n if ('ResizeObserver' in window) {\n const ro = new ResizeObserver(handleResize);\n ro.observe(container);\n resizeObserverRef.current = ro;\n } else {\n window.addEventListener('resize', handleResize);\n }\n\n const loop = () => {\n const dt = clock.getDelta();\n const elapsed = clock.elapsedTime;\n material.uniforms.uTime.value = elapsed;\n\n const deg = (rotationRef.current % 360) + autoRotateRef.current * elapsed;\n const rad = (deg * Math.PI) / 180;\n const c = Math.cos(rad);\n const s = Math.sin(rad);\n material.uniforms.uRot.value.set(c, s);\n\n const cur = pointerCurrentRef.current;\n const tgt = pointerTargetRef.current;\n const amt = Math.min(1, dt * pointerSmoothRef.current);\n cur.lerp(tgt, amt);\n material.uniforms.uPointer.value.copy(cur);\n renderer.render(scene, camera);\n rafRef.current = requestAnimationFrame(loop);\n };\n rafRef.current = requestAnimationFrame(loop);\n\n return () => {\n if (rafRef.current !== null) cancelAnimationFrame(rafRef.current);\n if (resizeObserverRef.current) resizeObserverRef.current.disconnect();\n else window.removeEventListener('resize', handleResize);\n geometry.dispose();\n material.dispose();\n renderer.dispose();\n renderer.forceContextLoss();\n if (renderer.domElement && renderer.domElement.parentElement === container) {\n container.removeChild(renderer.domElement);\n }\n };\n }, [frequency, mouseInfluence, noise, parallax, scale, speed, transparent, warpStrength]);\n\n useEffect(() => {\n const material = materialRef.current;\n const renderer = rendererRef.current;\n if (!material) return;\n\n rotationRef.current = rotation;\n autoRotateRef.current = autoRotate;\n material.uniforms.uSpeed.value = speed;\n material.uniforms.uScale.value = scale;\n material.uniforms.uFrequency.value = frequency;\n material.uniforms.uWarpStrength.value = warpStrength;\n material.uniforms.uMouseInfluence.value = mouseInfluence;\n material.uniforms.uParallax.value = parallax;\n material.uniforms.uNoise.value = noise;\n\n const toVec3 = hex => {\n const h = hex.replace('#', '').trim();\n const v =\n h.length === 3\n ? [parseInt(h[0] + h[0], 16), parseInt(h[1] + h[1], 16), parseInt(h[2] + h[2], 16)]\n : [parseInt(h.slice(0, 2), 16), parseInt(h.slice(2, 4), 16), parseInt(h.slice(4, 6), 16)];\n return new THREE.Vector3(v[0] / 255, v[1] / 255, v[2] / 255);\n };\n\n const arr = (colors || []).filter(Boolean).slice(0, MAX_COLORS).map(toVec3);\n for (let i = 0; i < MAX_COLORS; i++) {\n const vec = material.uniforms.uColors.value[i];\n if (i < arr.length) vec.copy(arr[i]);\n else vec.set(0, 0, 0);\n }\n material.uniforms.uColorCount.value = arr.length;\n\n material.uniforms.uTransparent.value = transparent ? 1 : 0;\n if (renderer) renderer.setClearColor(0x000000, transparent ? 0 : 1);\n }, [\n rotation,\n autoRotate,\n speed,\n scale,\n frequency,\n warpStrength,\n mouseInfluence,\n parallax,\n noise,\n colors,\n transparent\n ]);\n\n useEffect(() => {\n const material = materialRef.current;\n const container = containerRef.current;\n if (!material || !container) return;\n\n const handlePointerMove = e => {\n const rect = container.getBoundingClientRect();\n const x = ((e.clientX - rect.left) / (rect.width || 1)) * 2 - 1;\n const y = -(((e.clientY - rect.top) / (rect.height || 1)) * 2 - 1);\n pointerTargetRef.current.set(x, y);\n };\n\n container.addEventListener('pointermove', handlePointerMove);\n return () => {\n container.removeEventListener('pointermove', handlePointerMove);\n };\n }, []);\n\n return <div ref={containerRef} className={`w-full h-full relative overflow-hidden ${className}`} style={style} />;\n}\n"
}
],
"registryDependencies": [],
"dependencies": [
"three@^0.167.1"
]
}