mirror of
https://github.com/DavidHDev/vue-bits.git
synced 2026-03-07 22:49:31 -07:00
1 line
8.4 KiB
JSON
1 line
8.4 KiB
JSON
{"name":"Threads","title":"Threads","description":"Animated pattern of lines forming a fabric-like motion.","type":"registry:component","add":"when-added","files":[{"type":"registry:component","role":"file","content":"<template>\n <div ref=\"containerRef\" class=\"w-full h-full relative\" />\n</template>\n\n<script setup lang=\"ts\">\nimport { onMounted, onUnmounted, watch, useTemplateRef } from 'vue';\nimport { Renderer, Program, Mesh, Triangle, Color } from 'ogl';\nimport type { OGLRenderingContext } from 'ogl';\n\ninterface Props {\n color?: [number, number, number];\n amplitude?: number;\n distance?: number;\n enableMouseInteraction?: boolean;\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n color: () => [1, 1, 1] as [number, number, number],\n amplitude: 1,\n distance: 0,\n enableMouseInteraction: false\n});\n\nconst containerRef = useTemplateRef<HTMLDivElement>('containerRef');\n\nlet renderer: Renderer | null = null;\nlet gl: OGLRenderingContext | null = null;\nlet program: Program | null = null;\nlet mesh: Mesh | null = null;\nlet animationId: number | null = null;\nlet currentMouse = [0.5, 0.5];\nlet targetMouse = [0.5, 0.5];\n\nconst vertexShader = `\nattribute vec2 position;\nattribute vec2 uv;\nvarying vec2 vUv;\nvoid main() {\n vUv = uv;\n gl_Position = vec4(position, 0.0, 1.0);\n}\n`;\n\nconst fragmentShader = `\nprecision highp float;\n\nuniform float iTime;\nuniform vec3 iResolution;\nuniform vec3 uColor;\nuniform float uAmplitude;\nuniform float uDistance;\nuniform vec2 uMouse;\n\n#define PI 3.1415926538\n\nconst int u_line_count = 40;\nconst float u_line_width = 7.0;\nconst float u_line_blur = 10.0;\n\nfloat Perlin2D(vec2 P) {\n vec2 Pi = floor(P);\n vec4 Pf_Pfmin1 = P.xyxy - vec4(Pi, Pi + 1.0);\n vec4 Pt = vec4(Pi.xy, Pi.xy + 1.0);\n Pt = Pt - floor(Pt * (1.0 / 71.0)) * 71.0;\n Pt += vec2(26.0, 161.0).xyxy;\n Pt *= Pt;\n Pt = Pt.xzxz * Pt.yyww;\n vec4 hash_x = fract(Pt * (1.0 / 951.135664));\n vec4 hash_y = fract(Pt * (1.0 / 642.949883));\n vec4 grad_x = hash_x - 0.49999;\n vec4 grad_y = hash_y - 0.49999;\n vec4 grad_results = inversesqrt(grad_x * grad_x + grad_y * grad_y)\n * (grad_x * Pf_Pfmin1.xzxz + grad_y * Pf_Pfmin1.yyww);\n grad_results *= 1.4142135623730950;\n vec2 blend = Pf_Pfmin1.xy * Pf_Pfmin1.xy * Pf_Pfmin1.xy\n * (Pf_Pfmin1.xy * (Pf_Pfmin1.xy * 6.0 - 15.0) + 10.0);\n vec4 blend2 = vec4(blend, vec2(1.0 - blend));\n return dot(grad_results, blend2.zxzx * blend2.wwyy);\n}\n\nfloat pixel(float count, vec2 resolution) {\n return (1.0 / max(resolution.x, resolution.y)) * count;\n}\n\nfloat lineFn(vec2 st, float width, float perc, float offset, vec2 mouse, float time, float amplitude, float distance) {\n float split_offset = (perc * 0.4);\n float split_point = 0.1 + split_offset;\n\n float amplitude_normal = smoothstep(split_point, 0.7, st.x);\n float amplitude_strength = 0.5;\n float finalAmplitude = amplitude_normal * amplitude_strength\n * amplitude * (1.0 + (mouse.y - 0.5) * 0.2);\n\n float time_scaled = time / 10.0 + (mouse.x - 0.5) * 1.0;\n float blur = smoothstep(split_point, split_point + 0.05, st.x) * perc;\n\n float xnoise = mix(\n Perlin2D(vec2(time_scaled, st.x + perc) * 2.5),\n Perlin2D(vec2(time_scaled, st.x + time_scaled) * 3.5) / 1.5,\n st.x * 0.3\n );\n\n float y = 0.5 + (perc - 0.5) * distance + xnoise / 2.0 * finalAmplitude;\n\n float line_start = smoothstep(\n y + (width / 2.0) + (u_line_blur * pixel(1.0, iResolution.xy) * blur),\n y,\n st.y\n );\n\n float line_end = smoothstep(\n y,\n y - (width / 2.0) - (u_line_blur * pixel(1.0, iResolution.xy) * blur),\n st.y\n );\n\n return clamp(\n (line_start - line_end) * (1.0 - smoothstep(0.0, 1.0, pow(perc, 0.3))),\n 0.0,\n 1.0\n );\n}\n\nvoid mainImage(out vec4 fragColor, in vec2 fragCoord) {\n vec2 uv = fragCoord / iResolution.xy;\n\n float line_strength = 1.0;\n for (int i = 0; i < u_line_count; i++) {\n float p = float(i) / float(u_line_count);\n line_strength *= (1.0 - lineFn(\n uv,\n u_line_width * pixel(1.0, iResolution.xy) * (1.0 - p),\n p,\n (PI * 1.0) * p,\n uMouse,\n iTime,\n uAmplitude,\n uDistance\n ));\n }\n\n float colorVal = 1.0 - line_strength;\n fragColor = vec4(uColor * colorVal, colorVal);\n}\n\nvoid main() {\n mainImage(gl_FragColor, gl_FragCoord.xy);\n}\n`;\n\nconst resize = () => {\n if (!containerRef.value || !renderer || !program) return;\n\n const container = containerRef.value;\n const { clientWidth, clientHeight } = container;\n renderer.setSize(clientWidth, clientHeight);\n program.uniforms.iResolution.value.r = clientWidth;\n program.uniforms.iResolution.value.g = clientHeight;\n program.uniforms.iResolution.value.b = clientWidth / clientHeight;\n};\n\nconst handleMouseMove = (e: MouseEvent) => {\n if (!containerRef.value) return;\n\n const rect = containerRef.value.getBoundingClientRect();\n const x = (e.clientX - rect.left) / rect.width;\n const y = 1.0 - (e.clientY - rect.top) / rect.height;\n targetMouse = [x, y];\n};\n\nconst handleMouseLeave = () => {\n targetMouse = [0.5, 0.5];\n};\n\nconst update = (t: number) => {\n if (!program || !renderer || !mesh) return;\n\n if (props.enableMouseInteraction) {\n const smoothing = 0.05;\n currentMouse[0] += smoothing * (targetMouse[0] - currentMouse[0]);\n currentMouse[1] += smoothing * (targetMouse[1] - currentMouse[1]);\n program.uniforms.uMouse.value[0] = currentMouse[0];\n program.uniforms.uMouse.value[1] = currentMouse[1];\n } else {\n program.uniforms.uMouse.value[0] = 0.5;\n program.uniforms.uMouse.value[1] = 0.5;\n }\n\n program.uniforms.iTime.value = t * 0.001;\n renderer.render({ scene: mesh });\n animationId = requestAnimationFrame(update);\n};\n\nconst initializeScene = () => {\n if (!containerRef.value) return;\n\n cleanup();\n\n const container = containerRef.value;\n renderer = new Renderer({ alpha: true });\n gl = renderer.gl;\n gl.clearColor(0, 0, 0, 0);\n gl.enable(gl.BLEND);\n gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);\n\n const geometry = new Triangle(gl);\n program = new Program(gl, {\n vertex: vertexShader,\n fragment: fragmentShader,\n uniforms: {\n iTime: { value: 0 },\n iResolution: {\n value: new Color(gl.canvas.width, gl.canvas.height, gl.canvas.width / gl.canvas.height)\n },\n uColor: { value: new Color(...props.color) },\n uAmplitude: { value: props.amplitude },\n uDistance: { value: props.distance },\n uMouse: { value: new Float32Array([0.5, 0.5]) }\n }\n });\n\n mesh = new Mesh(gl, { geometry, program });\n\n const canvas = gl.canvas as HTMLCanvasElement;\n canvas.style.width = '100%';\n canvas.style.height = '100%';\n canvas.style.display = 'block';\n\n container.appendChild(canvas);\n\n window.addEventListener('resize', resize);\n if (props.enableMouseInteraction) {\n container.addEventListener('mousemove', handleMouseMove);\n container.addEventListener('mouseleave', handleMouseLeave);\n }\n\n resize();\n animationId = requestAnimationFrame(update);\n};\n\nconst cleanup = () => {\n if (animationId) {\n cancelAnimationFrame(animationId);\n animationId = null;\n }\n\n window.removeEventListener('resize', resize);\n\n if (containerRef.value) {\n containerRef.value.removeEventListener('mousemove', handleMouseMove);\n containerRef.value.removeEventListener('mouseleave', handleMouseLeave);\n\n const canvas = containerRef.value.querySelector('canvas');\n if (canvas) {\n containerRef.value.removeChild(canvas);\n }\n }\n\n if (gl) {\n gl.getExtension('WEBGL_lose_context')?.loseContext();\n }\n\n renderer = null;\n gl = null;\n program = null;\n mesh = null;\n currentMouse = [0.5, 0.5];\n targetMouse = [0.5, 0.5];\n};\n\nonMounted(() => {\n initializeScene();\n});\n\nonUnmounted(() => {\n cleanup();\n});\n\nwatch(\n [() => props.color, () => props.amplitude, () => props.distance, () => props.enableMouseInteraction],\n () => {\n initializeScene();\n },\n { deep: true }\n);\n</script>\n","path":"Threads/Threads.vue","_imports_":[],"registryDependencies":[],"dependencies":[],"devDependencies":[]}],"registryDependencies":[],"dependencies":[{"ecosystem":"js","name":"ogl","version":"^1.0.11"}],"devDependencies":[],"categories":["Backgrounds"]} |