mirror of
https://github.com/DavidHDev/vue-bits.git
synced 2026-03-07 06:29:30 -07:00
1 line
7.2 KiB
JSON
1 line
7.2 KiB
JSON
{"name":"Silk","title":"Silk","description":"Smooth waves background with soft lighting.","type":"registry:component","add":"when-added","files":[{"type":"registry:component","role":"file","content":"<template>\n <div ref=\"containerRef\" :class=\"className\" :style=\"style\" class=\"w-full h-full\"></div>\n</template>\n\n<script setup lang=\"ts\">\nimport { onMounted, onUnmounted, watch, type CSSProperties, useTemplateRef } from 'vue';\nimport { Renderer, Program, Mesh, Plane, Camera } from 'ogl';\n\ninterface SilkProps {\n speed?: number;\n scale?: number;\n color?: string;\n noiseIntensity?: number;\n rotation?: number;\n className?: string;\n style?: CSSProperties;\n}\n\nconst props = withDefaults(defineProps<SilkProps>(), {\n speed: 5,\n scale: 1,\n color: '#7B7481',\n noiseIntensity: 1.5,\n rotation: 0,\n className: '',\n style: () => ({})\n});\n\nconst containerRef = useTemplateRef<HTMLDivElement>('containerRef');\n\nconst hexToNormalizedRGB = (hex: string): [number, number, number] => {\n const clean = hex.replace('#', '');\n const r = parseInt(clean.slice(0, 2), 16) / 255;\n const g = parseInt(clean.slice(2, 4), 16) / 255;\n const b = parseInt(clean.slice(4, 6), 16) / 255;\n return [r, g, b];\n};\n\nconst vertexShader = `\nattribute vec2 uv;\nattribute vec3 position;\n\nuniform mat4 modelViewMatrix;\nuniform mat4 projectionMatrix;\n\nvarying vec2 vUv;\nvarying vec3 vPosition;\n\nvoid main() {\n vPosition = position;\n vUv = uv;\n gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);\n}\n`;\n\nconst fragmentShader = `\nprecision highp float;\n\nvarying vec2 vUv;\nvarying vec3 vPosition;\n\nuniform float uTime;\nuniform vec3 uColor;\nuniform float uSpeed;\nuniform float uScale;\nuniform float uRotation;\nuniform float uNoiseIntensity;\n\nconst float e = 2.71828182845904523536;\n\nfloat noise(vec2 texCoord) {\n float G = e;\n vec2 r = (G * sin(G * texCoord));\n return fract(r.x * r.y * (1.0 + texCoord.x));\n}\n\nvec2 rotateUvs(vec2 uv, float angle) {\n float c = cos(angle);\n float s = sin(angle);\n mat2 rot = mat2(c, -s, s, c);\n return rot * uv;\n}\n\nvoid main() {\n float rnd = noise(gl_FragCoord.xy);\n vec2 uv = rotateUvs(vUv * uScale, uRotation);\n vec2 tex = uv * uScale;\n float tOffset = uSpeed * uTime;\n\n tex.y += 0.03 * sin(8.0 * tex.x - tOffset);\n\n float pattern = 0.6 +\n 0.4 * sin(5.0 * (tex.x + tex.y +\n cos(3.0 * tex.x + 5.0 * tex.y) +\n 0.02 * tOffset) +\n sin(20.0 * (tex.x + tex.y - 0.1 * tOffset)));\n\n vec4 col = vec4(uColor, 1.0) * vec4(pattern) - rnd / 15.0 * uNoiseIntensity;\n col.a = 1.0;\n gl_FragColor = col;\n}\n`;\n\nlet renderer: Renderer | null = null;\nlet mesh: Mesh | null = null;\nlet program: Program | null = null;\nlet camera: Camera | null = null;\nlet animateId = 0;\n\nconst initSilk = () => {\n const container = containerRef.value;\n if (!container) return;\n\n renderer = new Renderer({\n alpha: true,\n antialias: true\n });\n\n const gl = renderer.gl;\n gl.clearColor(0, 0, 0, 0);\n gl.canvas.style.backgroundColor = 'transparent';\n\n camera = new Camera(gl, { fov: 75 });\n camera.position.z = 1;\n\n const resize = () => {\n if (!container || !camera) return;\n\n let width = container.offsetWidth;\n let height = container.offsetHeight;\n\n let parent = container.parentElement;\n while (parent && (!width || !height)) {\n if (parent.offsetWidth && parent.offsetHeight) {\n width = parent.offsetWidth;\n height = parent.offsetHeight;\n break;\n }\n parent = parent.parentElement;\n }\n\n if (!width || !height) {\n width = window.innerWidth;\n height = window.innerHeight;\n }\n\n width = Math.max(width, 300);\n height = Math.max(height, 300);\n\n renderer!.setSize(width, height);\n camera.perspective({ aspect: width / height });\n\n if (mesh) {\n const distance = camera.position.z;\n const fov = camera.fov * (Math.PI / 180);\n const height2 = 2 * Math.tan(fov / 2) * distance;\n const width2 = height2 * (width / height);\n\n mesh.scale.set(width2, height2, 1);\n }\n };\n\n window.addEventListener('resize', resize);\n\n const geometry = new Plane(gl, {\n width: 1,\n height: 1\n });\n\n const colorRGB = hexToNormalizedRGB(props.color);\n\n program = new Program(gl, {\n vertex: vertexShader,\n fragment: fragmentShader,\n uniforms: {\n uSpeed: { value: props.speed },\n uScale: { value: props.scale },\n uNoiseIntensity: { value: props.noiseIntensity },\n uColor: { value: colorRGB },\n uRotation: { value: props.rotation },\n uTime: { value: 0 }\n }\n });\n\n mesh = new Mesh(gl, { geometry, program });\n container.appendChild(gl.canvas);\n\n gl.canvas.style.width = '100%';\n gl.canvas.style.height = '100%';\n gl.canvas.style.display = 'block';\n gl.canvas.style.position = 'absolute';\n gl.canvas.style.top = '0';\n gl.canvas.style.left = '0';\n gl.canvas.style.zIndex = '1';\n\n let lastTime = 0;\n const update = (t: number) => {\n animateId = requestAnimationFrame(update);\n const deltaTime = (t - lastTime) / 1000;\n lastTime = t;\n\n if (program && mesh && camera) {\n program.uniforms.uTime.value += 0.1 * deltaTime;\n program.uniforms.uSpeed.value = props.speed;\n program.uniforms.uScale.value = props.scale;\n program.uniforms.uNoiseIntensity.value = props.noiseIntensity;\n program.uniforms.uColor.value = hexToNormalizedRGB(props.color);\n program.uniforms.uRotation.value = props.rotation;\n renderer!.render({ scene: mesh, camera });\n }\n };\n animateId = requestAnimationFrame(update);\n\n resize();\n\n return () => {\n cancelAnimationFrame(animateId);\n window.removeEventListener('resize', resize);\n if (container && gl.canvas.parentNode === container) {\n container.removeChild(gl.canvas);\n }\n gl.getExtension('WEBGL_lose_context')?.loseContext();\n };\n};\n\nconst cleanup = () => {\n if (animateId) {\n cancelAnimationFrame(animateId);\n }\n if (renderer) {\n const gl = renderer.gl;\n const container = containerRef.value;\n if (container && gl.canvas.parentNode === container) {\n container.removeChild(gl.canvas);\n }\n gl.getExtension('WEBGL_lose_context')?.loseContext();\n }\n renderer = null;\n mesh = null;\n camera = null;\n program = null;\n};\n\nonMounted(() => {\n initSilk();\n});\n\nonUnmounted(() => {\n cleanup();\n});\n\nwatch(\n () => [props.speed, props.scale, props.color, props.noiseIntensity, props.rotation],\n () => {}\n);\n</script>\n\n<style scoped>\ndiv {\n width: 100% !important;\n height: 100% !important;\n min-height: 100% !important;\n display: block !important;\n}\n\n:deep(canvas) {\n width: 100% !important;\n height: 100% !important;\n min-height: 100% !important;\n display: block !important;\n object-fit: cover !important;\n}\n</style>\n","path":"Silk/Silk.vue","_imports_":[],"registryDependencies":[],"dependencies":[],"devDependencies":[]}],"registryDependencies":[],"dependencies":[{"ecosystem":"js","name":"ogl","version":"^1.0.11"}],"devDependencies":[],"categories":["Backgrounds"]} |