Files
vue-bits/public/r/LiquidChrome.json
David Haz e621971723 jsrepo v3
2025-12-15 23:50:24 +02:00

1 line
6.3 KiB
JSON

{"name":"LiquidChrome","title":"LiquidChrome","description":"Liquid metallic chrome shader with flowing reflective surface.","type":"registry:component","add":"when-added","files":[{"type":"registry:component","role":"file","content":"<script setup lang=\"ts\">\nimport { onMounted, onUnmounted, watch, useTemplateRef } from 'vue';\nimport { Renderer, Program, Mesh, Triangle } from 'ogl';\n\ninterface LiquidChromeProps {\n baseColor?: number[];\n speed?: number;\n amplitude?: number;\n frequencyX?: number;\n frequencyY?: number;\n interactive?: boolean;\n}\n\nconst props = withDefaults(defineProps<LiquidChromeProps>(), {\n baseColor: () => [0.1, 0.1, 0.1],\n speed: 0.2,\n amplitude: 0.5,\n frequencyX: 3,\n frequencyY: 2,\n interactive: true\n});\n\nconst containerRef = useTemplateRef<HTMLDivElement>('containerRef');\n\nlet cleanupAnimation: (() => void) | null = null;\n\nconst setupAnimation = () => {\n const container = containerRef.value;\n if (!container) return;\n\n const renderer = new Renderer({ antialias: true });\n const gl = renderer.gl;\n gl.clearColor(1, 1, 1, 1);\n\n const vertexShader = `\n attribute vec2 position;\n attribute vec2 uv;\n varying vec2 vUv;\n void main() {\n vUv = uv;\n gl_Position = vec4(position, 0.0, 1.0);\n }\n `;\n\n const fragmentShader = `\n precision highp float;\n uniform float uTime;\n uniform vec3 uResolution;\n uniform vec3 uBaseColor;\n uniform float uAmplitude;\n uniform float uFrequencyX;\n uniform float uFrequencyY;\n uniform vec2 uMouse;\n varying vec2 vUv;\n\n vec4 renderImage(vec2 uvCoord) {\n vec2 fragCoord = uvCoord * uResolution.xy;\n vec2 uv = (2.0 * fragCoord - uResolution.xy) / min(uResolution.x, uResolution.y);\n\n for (float i = 1.0; i < 10.0; i++){\n uv.x += uAmplitude / i * cos(i * uFrequencyX * uv.y + uTime + uMouse.x * 3.14159);\n uv.y += uAmplitude / i * cos(i * uFrequencyY * uv.x + uTime + uMouse.y * 3.14159);\n }\n\n vec2 diff = (uvCoord - uMouse);\n float dist = length(diff);\n float falloff = exp(-dist * 20.0);\n float ripple = sin(10.0 * dist - uTime * 2.0) * 0.03;\n uv += (diff / (dist + 0.0001)) * ripple * falloff;\n\n vec3 color = uBaseColor / abs(sin(uTime - uv.y - uv.x));\n return vec4(color, 1.0);\n }\n\n void main() {\n vec4 col = vec4(0.0);\n int samples = 0;\n for (int i = -1; i <= 1; i++){\n for (int j = -1; j <= 1; j++){\n vec2 offset = vec2(float(i), float(j)) * (1.0 / min(uResolution.x, uResolution.y));\n col += renderImage(vUv + offset);\n samples++;\n }\n }\n gl_FragColor = col / float(samples);\n }\n `;\n\n const geometry = new Triangle(gl);\n const program = new Program(gl, {\n vertex: vertexShader,\n fragment: fragmentShader,\n uniforms: {\n uTime: { value: 0 },\n uResolution: {\n value: new Float32Array([gl.canvas.width, gl.canvas.height, gl.canvas.width / gl.canvas.height])\n },\n uBaseColor: { value: new Float32Array(props.baseColor) },\n uAmplitude: { value: props.amplitude },\n uFrequencyX: { value: props.frequencyX },\n uFrequencyY: { value: props.frequencyY },\n uMouse: { value: new Float32Array([0, 0]) }\n }\n });\n const mesh = new Mesh(gl, { geometry, program });\n\n function resize() {\n const scale = 1;\n if (!container) return;\n\n renderer.setSize(container.offsetWidth * scale, container.offsetHeight * scale);\n const resUniform = program.uniforms.uResolution.value as Float32Array;\n resUniform[0] = gl.canvas.width;\n resUniform[1] = gl.canvas.height;\n resUniform[2] = gl.canvas.width / gl.canvas.height;\n }\n window.addEventListener('resize', resize);\n resize();\n\n function handleMouseMove(event: MouseEvent) {\n if (!container) return;\n\n const rect = container.getBoundingClientRect();\n const x = (event.clientX - rect.left) / rect.width;\n const y = 1 - (event.clientY - rect.top) / rect.height;\n const mouseUniform = program.uniforms.uMouse.value as Float32Array;\n mouseUniform[0] = x;\n mouseUniform[1] = y;\n }\n\n function handleTouchMove(event: TouchEvent) {\n if (event.touches.length > 0) {\n const touch = event.touches[0];\n if (!container) return;\n\n const rect = container.getBoundingClientRect();\n const x = (touch.clientX - rect.left) / rect.width;\n const y = 1 - (touch.clientY - rect.top) / rect.height;\n const mouseUniform = program.uniforms.uMouse.value as Float32Array;\n mouseUniform[0] = x;\n mouseUniform[1] = y;\n }\n }\n\n if (props.interactive) {\n container.addEventListener('mousemove', handleMouseMove);\n container.addEventListener('touchmove', handleTouchMove);\n }\n\n let animationId: number;\n function update(t: number) {\n animationId = requestAnimationFrame(update);\n program.uniforms.uTime.value = t * 0.001 * props.speed;\n renderer.render({ scene: mesh });\n }\n animationId = requestAnimationFrame(update);\n\n container.appendChild(gl.canvas);\n\n cleanupAnimation = () => {\n cancelAnimationFrame(animationId);\n window.removeEventListener('resize', resize);\n if (props.interactive) {\n container.removeEventListener('mousemove', handleMouseMove);\n container.removeEventListener('touchmove', handleTouchMove);\n }\n if (gl.canvas.parentElement) {\n gl.canvas.parentElement.removeChild(gl.canvas);\n }\n gl.getExtension('WEBGL_lose_context')?.loseContext();\n };\n};\n\nonMounted(() => {\n setupAnimation();\n});\n\nonUnmounted(() => {\n if (cleanupAnimation) {\n cleanupAnimation();\n cleanupAnimation = null;\n }\n});\n\nwatch(\n () => props,\n () => {\n if (cleanupAnimation) {\n cleanupAnimation();\n setupAnimation();\n }\n },\n { deep: true }\n);\n</script>\n\n<template>\n <div ref=\"containerRef\" class=\"w-full h-full\" v-bind=\"$attrs\" />\n</template>\n","path":"LiquidChrome/LiquidChrome.vue","_imports_":[],"registryDependencies":[],"dependencies":[],"devDependencies":[]}],"registryDependencies":[],"dependencies":[{"ecosystem":"js","name":"ogl","version":"^1.0.11"}],"devDependencies":[],"categories":["Backgrounds"]}