mirror of
https://github.com/DavidHDev/vue-bits.git
synced 2026-03-07 22:49:31 -07:00
1 line
7.8 KiB
JSON
1 line
7.8 KiB
JSON
{"name":"Lightning","title":"Lightning","description":"Procedural lightning bolts with branching and glow flicker.","type":"registry:component","add":"when-added","files":[{"type":"registry:component","role":"file","content":"<template>\n <canvas ref=\"canvasRef\" class=\"w-full h-full block mix-blend-screen relative\"></canvas>\n</template>\n\n<script setup lang=\"ts\">\nimport { onMounted, onUnmounted, watch, useTemplateRef } from 'vue';\n\ninterface LightningProps {\n hue?: number;\n xOffset?: number;\n speed?: number;\n intensity?: number;\n size?: number;\n}\n\nconst props = withDefaults(defineProps<LightningProps>(), {\n hue: 230,\n xOffset: 0,\n speed: 1,\n intensity: 1,\n size: 1\n});\n\nconst canvasRef = useTemplateRef<HTMLCanvasElement>('canvasRef');\nlet animationId = 0;\nlet gl: WebGLRenderingContext | null = null;\nlet program: WebGLProgram | null = null;\nlet startTime = 0;\n\nconst vertexShaderSource = `\nattribute vec2 aPosition;\nvoid main() {\n gl_Position = vec4(aPosition, 0.0, 1.0);\n}\n`;\n\nconst fragmentShaderSource = `\nprecision mediump float;\nuniform vec2 iResolution;\nuniform float iTime;\nuniform float uHue;\nuniform float uXOffset;\nuniform float uSpeed;\nuniform float uIntensity;\nuniform float uSize;\n\n#define OCTAVE_COUNT 10\n\nvec3 hsv2rgb(vec3 c) {\n vec3 rgb = clamp(abs(mod(c.x * 6.0 + vec3(0.0,4.0,2.0), 6.0) - 3.0) - 1.0, 0.0, 1.0);\n return c.z * mix(vec3(1.0), rgb, c.y);\n}\n\nfloat hash11(float p) {\n p = fract(p * .1031);\n p *= p + 33.33;\n p *= p + p;\n return fract(p);\n}\n\nfloat hash12(vec2 p) {\n vec3 p3 = fract(vec3(p.xyx) * .1031);\n p3 += dot(p3, p3.yzx + 33.33);\n return fract((p3.x + p3.y) * p3.z);\n}\n\nmat2 rotate2d(float theta) {\n float c = cos(theta);\n float s = sin(theta);\n return mat2(c, -s, s, c);\n}\n\nfloat noise(vec2 p) {\n vec2 ip = floor(p);\n vec2 fp = fract(p);\n float a = hash12(ip);\n float b = hash12(ip + vec2(1.0, 0.0));\n float c = hash12(ip + vec2(0.0, 1.0));\n float d = hash12(ip + vec2(1.0, 1.0));\n \n vec2 t = smoothstep(0.0, 1.0, fp);\n return mix(mix(a, b, t.x), mix(c, d, t.x), t.y);\n}\n\nfloat fbm(vec2 p) {\n float value = 0.0;\n float amplitude = 0.5;\n for (int i = 0; i < OCTAVE_COUNT; ++i) {\n value += amplitude * noise(p);\n p *= rotate2d(0.45);\n p *= 2.0;\n amplitude *= 0.5;\n }\n return value;\n}\n\nvoid mainImage( out vec4 fragColor, in vec2 fragCoord ) {\n vec2 uv = fragCoord / iResolution.xy;\n uv = 2.0 * uv - 1.0;\n uv.x *= iResolution.x / iResolution.y;\n uv.x += uXOffset;\n \n uv += 2.0 * fbm(uv * uSize + 0.8 * iTime * uSpeed) - 1.0;\n \n float dist = abs(uv.x);\n vec3 baseColor = hsv2rgb(vec3(uHue / 360.0, 0.7, 0.8));\n vec3 col = baseColor * pow(mix(0.0, 0.07, hash11(iTime * uSpeed)) / dist, 1.0) * uIntensity;\n col = pow(col, vec3(1.0));\n fragColor = vec4(col, 1.0);\n}\n\nvoid main() {\n mainImage(gl_FragColor, gl_FragCoord.xy);\n}\n`;\n\nconst compileShader = (source: string, type: number): WebGLShader | null => {\n if (!gl) return null;\n const shader = gl.createShader(type);\n if (!shader) return null;\n gl.shaderSource(shader, source);\n gl.compileShader(shader);\n if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {\n console.error('Shader compile error:', gl.getShaderInfoLog(shader));\n gl.deleteShader(shader);\n return null;\n }\n return shader;\n};\n\nconst initWebGL = () => {\n const canvas = canvasRef.value;\n if (!canvas) return;\n\n const resizeCanvas = () => {\n const rect = canvas.getBoundingClientRect();\n const dpr = window.devicePixelRatio || 1;\n\n let width = rect.width;\n let height = rect.height;\n\n let parent = canvas.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 canvas.width = width * dpr;\n canvas.height = height * dpr;\n\n canvas.style.width = '100%';\n canvas.style.height = '100%';\n canvas.style.display = 'block';\n canvas.style.position = 'absolute';\n canvas.style.top = '0';\n canvas.style.left = '0';\n };\n\n resizeCanvas();\n window.addEventListener('resize', resizeCanvas);\n\n gl = canvas.getContext('webgl');\n if (!gl) {\n console.error('WebGL not supported');\n return;\n }\n\n const vertexShader = compileShader(vertexShaderSource, gl.VERTEX_SHADER);\n const fragmentShader = compileShader(fragmentShaderSource, gl.FRAGMENT_SHADER);\n if (!vertexShader || !fragmentShader) return;\n\n program = gl.createProgram();\n if (!program) return;\n gl.attachShader(program, vertexShader);\n gl.attachShader(program, fragmentShader);\n gl.linkProgram(program);\n if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {\n console.error('Program linking error:', gl.getProgramInfoLog(program));\n return;\n }\n gl.useProgram(program);\n\n const vertices = new Float32Array([-1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1]);\n const vertexBuffer = gl.createBuffer();\n gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);\n gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);\n\n const aPosition = gl.getAttribLocation(program, 'aPosition');\n gl.enableVertexAttribArray(aPosition);\n gl.vertexAttribPointer(aPosition, 2, gl.FLOAT, false, 0, 0);\n\n startTime = performance.now();\n render();\n\n return () => {\n window.removeEventListener('resize', resizeCanvas);\n };\n};\n\nconst render = () => {\n if (!gl || !program || !canvasRef.value) return;\n\n const canvas = canvasRef.value;\n\n const rect = canvas.getBoundingClientRect();\n if (canvas.width !== rect.width || canvas.height !== rect.height) {\n canvas.width = rect.width;\n canvas.height = rect.height;\n canvas.style.width = rect.width + 'px';\n canvas.style.height = rect.height + 'px';\n }\n\n gl.viewport(0, 0, canvas.width, canvas.height);\n\n const iResolutionLocation = gl.getUniformLocation(program, 'iResolution');\n const iTimeLocation = gl.getUniformLocation(program, 'iTime');\n const uHueLocation = gl.getUniformLocation(program, 'uHue');\n const uXOffsetLocation = gl.getUniformLocation(program, 'uXOffset');\n const uSpeedLocation = gl.getUniformLocation(program, 'uSpeed');\n const uIntensityLocation = gl.getUniformLocation(program, 'uIntensity');\n const uSizeLocation = gl.getUniformLocation(program, 'uSize');\n\n gl.uniform2f(iResolutionLocation, canvas.width, canvas.height);\n const currentTime = performance.now();\n gl.uniform1f(iTimeLocation, (currentTime - startTime) / 1000.0);\n gl.uniform1f(uHueLocation, props.hue);\n gl.uniform1f(uXOffsetLocation, props.xOffset);\n gl.uniform1f(uSpeedLocation, props.speed);\n gl.uniform1f(uIntensityLocation, props.intensity);\n gl.uniform1f(uSizeLocation, props.size);\n\n gl.drawArrays(gl.TRIANGLES, 0, 6);\n animationId = requestAnimationFrame(render);\n};\n\nonMounted(() => {\n initWebGL();\n});\n\nonUnmounted(() => {\n if (animationId) {\n cancelAnimationFrame(animationId);\n }\n});\n\nwatch(\n () => [props.hue, props.xOffset, props.speed, props.intensity, props.size],\n () => {}\n);\n</script>\n\n<style scoped>\ncanvas {\n width: 100% !important;\n height: 100% !important;\n min-height: 100% !important;\n display: block !important;\n position: absolute !important;\n top: 0 !important;\n left: 0 !important;\n z-index: 1 !important;\n}\n</style>\n","path":"Lightning/Lightning.vue","_imports_":[],"registryDependencies":[],"dependencies":[],"devDependencies":[]}],"registryDependencies":[],"dependencies":[],"devDependencies":[],"categories":["Backgrounds"]} |