mirror of
https://github.com/DavidHDev/vue-bits.git
synced 2026-03-07 06:29:30 -07:00
1 line
6.6 KiB
JSON
1 line
6.6 KiB
JSON
{"name":"Plasma","title":"Plasma","description":"Organic plasma gradients swirl + morph with smooth turbulence.","type":"registry:component","add":"when-added","files":[{"type":"registry:component","role":"file","content":"<script setup lang=\"ts\">\nimport { Mesh, Program, Renderer, Triangle } from 'ogl';\nimport { onBeforeUnmount, onMounted, ref, useTemplateRef, watch } from 'vue';\n\ninterface PlasmaProps {\n color?: string;\n speed?: number;\n direction?: 'forward' | 'reverse' | 'pingpong';\n scale?: number;\n opacity?: number;\n mouseInteractive?: boolean;\n}\n\nconst props = withDefaults(defineProps<PlasmaProps>(), {\n color: '#9EF2BE',\n speed: 1,\n direction: 'forward',\n scale: 1,\n opacity: 1,\n mouseInteractive: true\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 if (!result) return [1, 0.5, 0.2];\n return [parseInt(result[1], 16) / 255, parseInt(result[2], 16) / 255, parseInt(result[3], 16) / 255];\n};\n\nconst vertex = `#version 300 es\nprecision highp float;\nin vec2 position;\nin vec2 uv;\nout vec2 vUv;\nvoid main() {\n vUv = uv;\n gl_Position = vec4(position, 0.0, 1.0);\n}\n`;\n\nconst fragment = `#version 300 es\nprecision highp float;\nuniform vec2 iResolution;\nuniform float iTime;\nuniform vec3 uCustomColor;\nuniform float uUseCustomColor;\nuniform float uSpeed;\nuniform float uDirection;\nuniform float uScale;\nuniform float uOpacity;\nuniform vec2 uMouse;\nuniform float uMouseInteractive;\nout vec4 fragColor;\n\nvoid mainImage(out vec4 o, vec2 C) {\n vec2 center = iResolution.xy * 0.5;\n C = (C - center) / uScale + center;\n \n vec2 mouseOffset = (uMouse - center) * 0.0002;\n C += mouseOffset * length(C - center) * step(0.5, uMouseInteractive);\n \n float i, d, z, T = iTime * uSpeed * uDirection;\n vec3 O, p, S;\n\n for (vec2 r = iResolution.xy, Q; ++i < 60.; O += o.w/d*o.xyz) {\n p = z*normalize(vec3(C-.5*r,r.y)); \n p.z -= 4.; \n S = p;\n d = p.y-T;\n \n p.x += .4*(1.+p.y)*sin(d + p.x*0.1)*cos(.34*d + p.x*0.05); \n Q = p.xz *= mat2(cos(p.y+vec4(0,11,33,0)-T)); \n z+= d = abs(sqrt(length(Q*Q)) - .25*(5.+S.y))/3.+8e-4; \n o = 1.+sin(S.y+p.z*.5+S.z-length(S-p)+vec4(2,1,0,8));\n }\n \n o.xyz = tanh(O/1e4);\n}\n\nbool finite1(float x){ return !(isnan(x) || isinf(x)); }\nvec3 sanitize(vec3 c){\n return vec3(\n finite1(c.r) ? c.r : 0.0,\n finite1(c.g) ? c.g : 0.0,\n finite1(c.b) ? c.b : 0.0\n );\n}\n\nvoid main() {\n vec4 o = vec4(0.0);\n mainImage(o, gl_FragCoord.xy);\n vec3 rgb = sanitize(o.rgb);\n \n float intensity = (rgb.r + rgb.g + rgb.b) / 3.0;\n vec3 customColor = intensity * uCustomColor;\n vec3 finalColor = mix(rgb, customColor, step(0.5, uUseCustomColor));\n \n float alpha = length(rgb) * uOpacity;\n fragColor = vec4(finalColor, alpha);\n}`;\n\nconst containerRef = useTemplateRef('containerRef');\nconst mousePos = ref({ x: 0, y: 0 });\n\nlet cleanup: (() => void) | null = null;\n\nconst setup = () => {\n if (!containerRef.value) return;\n\n const useCustomColor = props.color ? 1.0 : 0.0;\n const customColorRgb = props.color ? hexToRgb(props.color) : [1, 1, 1];\n\n const directionMultiplier = props.direction === 'reverse' ? -1.0 : 1.0;\n\n const renderer = new Renderer({\n webgl: 2,\n alpha: true,\n antialias: false,\n dpr: Math.min(window.devicePixelRatio || 1, 2)\n });\n const gl = renderer.gl;\n const canvas = gl.canvas as HTMLCanvasElement;\n canvas.style.display = 'block';\n canvas.style.width = '100%';\n canvas.style.height = '100%';\n containerRef.value.appendChild(canvas);\n\n const geometry = new Triangle(gl);\n\n const program = new Program(gl, {\n vertex: vertex,\n fragment: fragment,\n uniforms: {\n iTime: { value: 0 },\n iResolution: { value: new Float32Array([1, 1]) },\n uCustomColor: { value: new Float32Array(customColorRgb) },\n uUseCustomColor: { value: useCustomColor },\n uSpeed: { value: props.speed * 0.4 },\n uDirection: { value: directionMultiplier },\n uScale: { value: props.scale },\n uOpacity: { value: props.opacity },\n uMouse: { value: new Float32Array([0, 0]) },\n uMouseInteractive: { value: props.mouseInteractive ? 1.0 : 0.0 }\n }\n });\n\n const mesh = new Mesh(gl, { geometry, program });\n\n const handleMouseMove = (e: MouseEvent) => {\n if (!props.mouseInteractive) return;\n const rect = containerRef.value!.getBoundingClientRect();\n mousePos.value.x = e.clientX - rect.left;\n mousePos.value.y = e.clientY - rect.top;\n const mouseUniform = program.uniforms.uMouse.value as Float32Array;\n mouseUniform[0] = mousePos.value.x;\n mouseUniform[1] = mousePos.value.y;\n };\n\n if (props.mouseInteractive) {\n containerRef.value.addEventListener('mousemove', handleMouseMove);\n }\n\n const setSize = () => {\n const rect = containerRef.value!.getBoundingClientRect();\n const width = Math.max(1, Math.floor(rect.width));\n const height = Math.max(1, Math.floor(rect.height));\n renderer.setSize(width, height);\n const res = program.uniforms.iResolution.value as Float32Array;\n res[0] = gl.drawingBufferWidth;\n res[1] = gl.drawingBufferHeight;\n };\n\n const ro = new ResizeObserver(setSize);\n ro.observe(containerRef.value);\n setSize();\n\n let raf = 0;\n const t0 = performance.now();\n const loop = (t: number) => {\n const timeValue = (t - t0) * 0.001;\n\n if (props.direction === 'pingpong') {\n const cycle = Math.sin(timeValue * 0.5) * directionMultiplier;\n (program.uniforms.uDirection as { value: number }).value = cycle;\n }\n\n (program.uniforms.iTime as { value: number }).value = timeValue;\n renderer.render({ scene: mesh });\n raf = requestAnimationFrame(loop);\n };\n raf = requestAnimationFrame(loop);\n\n cleanup = () => {\n cancelAnimationFrame(raf);\n ro.disconnect();\n if (props.mouseInteractive && containerRef.value) {\n containerRef.value.removeEventListener('mousemove', handleMouseMove);\n }\n try {\n containerRef.value?.removeChild(canvas);\n } catch {}\n };\n};\n\nonMounted(() => {\n setup();\n});\n\nonBeforeUnmount(() => {\n cleanup?.();\n});\n\nwatch(\n props,\n () => {\n cleanup?.();\n setup();\n },\n { deep: true }\n);\n</script>\n\n<template>\n <div ref=\"containerRef\" class=\"relative w-full h-full overflow-hidden\" />\n</template>\n","path":"Plasma/Plasma.vue","_imports_":[],"registryDependencies":[],"dependencies":[],"devDependencies":[]}],"registryDependencies":[],"dependencies":[{"ecosystem":"js","name":"ogl","version":"^1.0.11"}],"devDependencies":[],"categories":["Backgrounds"]} |