mirror of
https://github.com/DavidHDev/vue-bits.git
synced 2026-03-07 06:29:30 -07:00
1 line
9.4 KiB
JSON
1 line
9.4 KiB
JSON
{"name":"RippleGrid","title":"RippleGrid","description":"A grid that continously animates with a ripple effect.","type":"registry:component","add":"when-added","files":[{"type":"registry:component","role":"file","content":"<script setup lang=\"ts\">\nimport { Renderer, Program, Triangle, Mesh } from 'ogl';\nimport { onMounted, onUnmounted, ref, useTemplateRef, watch } from 'vue';\n\ninterface RippleGridProps {\n enableRainbow?: boolean;\n gridColor?: string;\n rippleIntensity?: number;\n gridSize?: number;\n gridThickness?: number;\n fadeDistance?: number;\n vignetteStrength?: number;\n glowIntensity?: number;\n opacity?: number;\n gridRotation?: number;\n mouseInteraction?: boolean;\n mouseInteractionRadius?: number;\n}\n\nconst props = withDefaults(defineProps<RippleGridProps>(), {\n enableRainbow: false,\n gridColor: '#27FF64',\n rippleIntensity: 0.05,\n gridSize: 10.0,\n gridThickness: 15.0,\n fadeDistance: 1.5,\n vignetteStrength: 2.0,\n glowIntensity: 0.1,\n opacity: 1.0,\n gridRotation: 0,\n mouseInteraction: true,\n mouseInteractionRadius: 1\n});\n\nconst hexToRgb = (hex: string): [number, number, number] => {\n const result = /^#?([a-f\\d]{2})([a-f\\d]{2})([a-f\\d]{2})$/i.exec(hex);\n return result\n ? [parseInt(result[1], 16) / 255, parseInt(result[2], 16) / 255, parseInt(result[3], 16) / 255]\n : [1, 1, 1];\n};\n\nconst uniforms = {\n iTime: { value: 0 },\n iResolution: { value: [1, 1] },\n enableRainbow: { value: props.enableRainbow },\n gridColor: { value: hexToRgb(props.gridColor) },\n rippleIntensity: { value: props.rippleIntensity },\n gridSize: { value: props.gridSize },\n gridThickness: { value: props.gridThickness },\n fadeDistance: { value: props.fadeDistance },\n vignetteStrength: { value: props.vignetteStrength },\n glowIntensity: { value: props.glowIntensity },\n opacity: { value: props.opacity },\n gridRotation: { value: props.gridRotation },\n mouseInteraction: { value: props.mouseInteraction },\n mousePosition: { value: [0.5, 0.5] },\n mouseInfluence: { value: 0 },\n mouseInteractionRadius: { value: props.mouseInteractionRadius }\n};\n\nconst containerRef = useTemplateRef('containerRef');\nconst mousePositionRef = ref({ x: 0.5, y: 0.5 });\nconst targetMouseRef = ref({ x: 0.5, y: 0.5 });\nconst mouseInfluenceRef = ref(0);\nconst uniformsRef = ref<typeof uniforms | null>(null);\n\nonMounted(() => {\n if (!containerRef.value) return;\n\n const renderer = new Renderer({\n dpr: Math.min(window.devicePixelRatio, 2),\n alpha: true\n });\n const gl = renderer.gl;\n gl.enable(gl.BLEND);\n gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);\n gl.canvas.style.width = '100%';\n gl.canvas.style.height = '100%';\n containerRef.value.appendChild(gl.canvas);\n\n const vert = `\nattribute vec2 position;\nvarying vec2 vUv;\nvoid main() {\n vUv = position * 0.5 + 0.5;\n gl_Position = vec4(position, 0.0, 1.0);\n}`;\n\n const frag = `precision highp float;\nuniform float iTime;\nuniform vec2 iResolution;\nuniform bool enableRainbow;\nuniform vec3 gridColor;\nuniform float rippleIntensity;\nuniform float gridSize;\nuniform float gridThickness;\nuniform float fadeDistance;\nuniform float vignetteStrength;\nuniform float glowIntensity;\nuniform float opacity;\nuniform float gridRotation;\nuniform bool mouseInteraction;\nuniform vec2 mousePosition;\nuniform float mouseInfluence;\nuniform float mouseInteractionRadius;\nvarying vec2 vUv;\n\nfloat pi = 3.141592;\n\nmat2 rotate(float angle) {\n float s = sin(angle);\n float c = cos(angle);\n return mat2(c, -s, s, c);\n}\n\nvoid main() {\n vec2 uv = vUv * 2.0 - 1.0;\n uv.x *= iResolution.x / iResolution.y;\n\n if (gridRotation != 0.0) {\n uv = rotate(gridRotation * pi / 180.0) * uv;\n }\n\n float dist = length(uv);\n float func = sin(pi * (iTime - dist));\n vec2 rippleUv = uv + uv * func * rippleIntensity;\n\n if (mouseInteraction && mouseInfluence > 0.0) {\n vec2 mouseUv = (mousePosition * 2.0 - 1.0);\n mouseUv.x *= iResolution.x / iResolution.y;\n float mouseDist = length(uv - mouseUv);\n\n float influence = mouseInfluence * exp(-mouseDist * mouseDist / (mouseInteractionRadius * mouseInteractionRadius));\n\n float mouseWave = sin(pi * (iTime * 2.0 - mouseDist * 3.0)) * influence;\n rippleUv += normalize(uv - mouseUv) * mouseWave * rippleIntensity * 0.3;\n }\n\n vec2 a = sin(gridSize * 0.5 * pi * rippleUv - pi / 2.0);\n vec2 b = abs(a);\n\n float aaWidth = 0.5;\n vec2 smoothB = vec2(\n smoothstep(0.0, aaWidth, b.x),\n smoothstep(0.0, aaWidth, b.y)\n );\n\n vec3 color = vec3(0.0);\n color += exp(-gridThickness * smoothB.x * (0.8 + 0.5 * sin(pi * iTime)));\n color += exp(-gridThickness * smoothB.y);\n color += 0.5 * exp(-(gridThickness / 4.0) * sin(smoothB.x));\n color += 0.5 * exp(-(gridThickness / 3.0) * smoothB.y);\n\n if (glowIntensity > 0.0) {\n color += glowIntensity * exp(-gridThickness * 0.5 * smoothB.x);\n color += glowIntensity * exp(-gridThickness * 0.5 * smoothB.y);\n }\n\n float ddd = exp(-2.0 * clamp(pow(dist, fadeDistance), 0.0, 1.0));\n\n vec2 vignetteCoords = vUv - 0.5;\n float vignetteDistance = length(vignetteCoords);\n float vignette = 1.0 - pow(vignetteDistance * 2.0, vignetteStrength);\n vignette = clamp(vignette, 0.0, 1.0);\n\n vec3 t;\n if (enableRainbow) {\n t = vec3(\n uv.x * 0.5 + 0.5 * sin(iTime),\n uv.y * 0.5 + 0.5 * cos(iTime),\n pow(cos(iTime), 4.0)\n ) + 0.5;\n } else {\n t = gridColor;\n }\n\n float finalFade = ddd * vignette;\n float alpha = length(color) * finalFade * opacity;\n gl_FragColor = vec4(color * t * finalFade * opacity, alpha);\n}`;\n\n uniformsRef.value = uniforms;\n\n const geometry = new Triangle(gl);\n const program = new Program(gl, { vertex: vert, fragment: frag, uniforms });\n const mesh = new Mesh(gl, { geometry, program });\n\n const resize = () => {\n const { clientWidth: w, clientHeight: h } = containerRef.value!;\n renderer.setSize(w, h);\n uniforms.iResolution.value = [w, h];\n };\n\n const handleMouseMove = (e: MouseEvent) => {\n if (!props.mouseInteraction || !containerRef.value) return;\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 targetMouseRef.value = { x, y };\n };\n\n const handleMouseEnter = () => {\n if (!props.mouseInteraction) return;\n mouseInfluenceRef.value = 1.0;\n };\n\n const handleMouseLeave = () => {\n if (!props.mouseInteraction) return;\n mouseInfluenceRef.value = 0.0;\n };\n\n window.addEventListener('resize', resize);\n if (props.mouseInteraction) {\n containerRef.value.addEventListener('mousemove', handleMouseMove);\n containerRef.value.addEventListener('mouseenter', handleMouseEnter);\n containerRef.value.addEventListener('mouseleave', handleMouseLeave);\n }\n resize();\n\n const render = (t: number) => {\n uniforms.iTime.value = t * 0.001;\n\n const lerpFactor = 0.1;\n mousePositionRef.value.x += (targetMouseRef.value.x - mousePositionRef.value.x) * lerpFactor;\n mousePositionRef.value.y += (targetMouseRef.value.y - mousePositionRef.value.y) * lerpFactor;\n\n const currentInfluence = uniforms.mouseInfluence.value;\n const targetInfluence = mouseInfluenceRef.value;\n uniforms.mouseInfluence.value += (targetInfluence - currentInfluence) * 0.05;\n\n uniforms.mousePosition.value = [mousePositionRef.value.x, mousePositionRef.value.y];\n\n renderer.render({ scene: mesh });\n requestAnimationFrame(render);\n };\n\n requestAnimationFrame(render);\n\n onUnmounted(() => {\n window.removeEventListener('resize', resize);\n if (props.mouseInteraction && containerRef.value) {\n containerRef.value.removeEventListener('mousemove', handleMouseMove);\n containerRef.value.removeEventListener('mouseenter', handleMouseEnter);\n containerRef.value.removeEventListener('mouseleave', handleMouseLeave);\n }\n renderer.gl.getExtension('WEBGL_lose_context')?.loseContext();\n containerRef.value?.removeChild(gl.canvas);\n });\n});\n\nwatch(\n () => props,\n () => {\n if (!uniformsRef.value) return;\n\n uniformsRef.value.enableRainbow.value = props.enableRainbow;\n uniformsRef.value.gridColor.value = hexToRgb(props.gridColor);\n uniformsRef.value.rippleIntensity.value = props.rippleIntensity;\n uniformsRef.value.gridSize.value = props.gridSize;\n uniformsRef.value.gridThickness.value = props.gridThickness;\n uniformsRef.value.fadeDistance.value = props.fadeDistance;\n uniformsRef.value.vignetteStrength.value = props.vignetteStrength;\n uniformsRef.value.glowIntensity.value = props.glowIntensity;\n uniformsRef.value.opacity.value = props.opacity;\n uniformsRef.value.gridRotation.value = props.gridRotation;\n uniformsRef.value.mouseInteraction.value = props.mouseInteraction;\n uniformsRef.value.mouseInteractionRadius.value = props.mouseInteractionRadius;\n },\n { deep: true, immediate: true }\n);\n</script>\n\n<template>\n <div ref=\"containerRef\" class=\"[&_canvas]:block relative w-full h-full overflow-hidden\" />\n</template>\n","path":"RippleGrid/RippleGrid.vue","_imports_":[],"registryDependencies":[],"dependencies":[],"devDependencies":[]}],"registryDependencies":[],"dependencies":[{"ecosystem":"js","name":"ogl","version":"^1.0.11"}],"devDependencies":[],"categories":["Backgrounds"]} |