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

1 line
8.5 KiB
JSON

{"name":"Balatro","title":"Balatro","description":"The balatro shader, fully customizalbe and interactive.","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\" />\n</template>\n\n<script setup lang=\"ts\">\nimport { watch, onMounted, onUnmounted, type CSSProperties, useTemplateRef } from 'vue';\nimport { Renderer, Program, Mesh, Triangle } from 'ogl';\n\ninterface BalatroProps {\n spinRotation?: number;\n spinSpeed?: number;\n offset?: [number, number];\n color1?: string;\n color2?: string;\n color3?: string;\n contrast?: number;\n lighting?: number;\n spinAmount?: number;\n pixelFilter: number;\n spinEase?: number;\n isRotate: boolean;\n mouseInteraction: boolean;\n className?: string;\n style?: CSSProperties;\n}\n\nconst props = withDefaults(defineProps<BalatroProps>(), {\n spinRotation: -2,\n spinSpeed: 7,\n offset: () => [0, 0],\n color1: '#DE443B',\n color2: '#006BB4',\n color3: '#162325',\n contrast: 3.5,\n lighting: 0.4,\n spinAmount: 0.25,\n pixelFilter: 745,\n spinEase: 1,\n isRotate: false,\n mouseInteraction: true,\n className: '',\n style: () => ({})\n});\n\nconst containerRef = useTemplateRef<HTMLDivElement>('containerRef');\n\nconst hexToVec4 = (hex: string): [number, number, number, number] => {\n const hexStr = hex.replace('#', '');\n let r = 0,\n g = 0,\n b = 0,\n a = 1;\n if (hexStr.length === 6) {\n r = parseInt(hexStr.slice(0, 2), 16) / 255;\n g = parseInt(hexStr.slice(2, 4), 16) / 255;\n b = parseInt(hexStr.slice(4, 6), 16) / 255;\n } else if (hexStr.length === 8) {\n r = parseInt(hexStr.slice(0, 2), 16) / 255;\n g = parseInt(hexStr.slice(2, 4), 16) / 255;\n b = parseInt(hexStr.slice(4, 6), 16) / 255;\n a = parseInt(hexStr.slice(6, 8), 16) / 255;\n }\n return [r, g, b, a];\n};\n\nconst vertexShader = `\n attribute vec2 uv;\n attribute vec2 position;\n varying vec2 vUv;\n void main() {\n vUv = uv;\n gl_Position = vec4(position, 0, 1);\n }\n `;\n\nconst fragmentShader = `\n precision highp float;\n \n #define PI 3.14159265359\n \n uniform float iTime;\n uniform vec3 iResolution;\n uniform float uSpinRotation;\n uniform float uSpinSpeed;\n uniform vec2 uOffset;\n uniform vec4 uColor1;\n uniform vec4 uColor2;\n uniform vec4 uColor3;\n uniform float uContrast;\n uniform float uLighting;\n uniform float uSpinAmount;\n uniform float uPixelFilter;\n uniform float uSpinEase;\n uniform bool uIsRotate;\n uniform vec2 uMouse;\n \n varying vec2 vUv;\n \n vec4 effect(vec2 screenSize, vec2 screen_coords) {\n float pixel_size = length(screenSize.xy) / uPixelFilter;\n vec2 uv = (floor(screen_coords.xy * (1.0 / pixel_size)) * pixel_size - 0.5 * screenSize.xy) / length(screenSize.xy) - uOffset;\n float uv_len = length(uv);\n \n float speed = (uSpinRotation * uSpinEase * 0.2);\n if(uIsRotate){\n speed = iTime * speed;\n }\n speed += 302.2;\n \n float mouseInfluence = (uMouse.x * 2.0 - 1.0);\n speed += mouseInfluence * 0.1;\n \n float new_pixel_angle = atan(uv.y, uv.x) + speed - uSpinEase * 20.0 * (uSpinAmount * uv_len + (1.0 - uSpinAmount));\n vec2 mid = (screenSize.xy / length(screenSize.xy)) / 2.0;\n uv = (vec2(uv_len * cos(new_pixel_angle) + mid.x, uv_len * sin(new_pixel_angle) + mid.y) - mid);\n \n uv *= 30.0;\n float baseSpeed = iTime * uSpinSpeed;\n speed = baseSpeed + mouseInfluence * 2.0;\n \n vec2 uv2 = vec2(uv.x + uv.y);\n \n for(int i = 0; i < 5; i++) {\n uv2 += sin(max(uv.x, uv.y)) + uv;\n uv += 0.5 * vec2(\n cos(5.1123314 + 0.353 * uv2.y + speed * 0.131121),\n sin(uv2.x - 0.113 * speed)\n );\n uv -= cos(uv.x + uv.y) - sin(uv.x * 0.711 - uv.y);\n }\n \n float contrast_mod = (0.25 * uContrast + 0.5 * uSpinAmount + 1.2);\n float paint_res = min(2.0, max(0.0, length(uv) * 0.035 * contrast_mod));\n float c1p = max(0.0, 1.0 - contrast_mod * abs(1.0 - paint_res));\n float c2p = max(0.0, 1.0 - contrast_mod * abs(paint_res));\n float c3p = 1.0 - min(1.0, c1p + c2p);\n float light = (uLighting - 0.2) * max(c1p * 5.0 - 4.0, 0.0) + uLighting * max(c2p * 5.0 - 4.0, 0.0);\n \n return (0.3 / uContrast) * uColor1 + (1.0 - 0.3 / uContrast) * (uColor1 * c1p + uColor2 * c2p + vec4(c3p * uColor3.rgb, c3p * uColor1.a)) + light;\n }\n \n void main() {\n vec2 uv = vUv * iResolution.xy;\n gl_FragColor = effect(iResolution.xy, uv);\n }\n `;\n\nlet renderer: Renderer | null = null;\nlet program: Program | null = null;\nlet animateId = 0;\n\nconst initBalatro = () => {\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, 1);\n gl.canvas.style.backgroundColor = 'transparent';\n\n function resize() {\n renderer?.setSize(container!.offsetWidth, container!.offsetHeight);\n if (program) {\n program.uniforms.iResolution.value = [gl.canvas.width, gl.canvas.height, gl.canvas.width / gl.canvas.height];\n }\n }\n\n window.addEventListener('resize', resize);\n resize();\n\n const geometry = new Triangle(gl);\n program = new Program(gl, {\n vertex: vertexShader,\n fragment: fragmentShader,\n uniforms: {\n iTime: { value: 0 },\n iResolution: {\n value: [gl.canvas.width, gl.canvas.height, gl.canvas.width / gl.canvas.height]\n },\n uSpinRotation: { value: props.spinRotation },\n uSpinSpeed: { value: props.spinSpeed },\n uOffset: { value: props.offset },\n uColor1: { value: hexToVec4(props.color1) },\n uColor2: { value: hexToVec4(props.color2) },\n uColor3: { value: hexToVec4(props.color3) },\n uContrast: { value: props.contrast },\n uLighting: { value: props.lighting },\n uSpinAmount: { value: props.spinAmount },\n uPixelFilter: { value: props.pixelFilter },\n uSpinEase: { value: props.spinEase },\n uIsRotate: { value: props.isRotate },\n uMouse: { value: [0.5, 0.5] }\n }\n });\n\n const mesh = new Mesh(gl, { geometry, program });\n\n function update(time: DOMHighResTimeStamp) {\n animateId = requestAnimationFrame(update);\n program!.uniforms.iTime.value = time * 0.001;\n renderer!.render({ scene: mesh });\n }\n\n animateId = requestAnimationFrame(update);\n container.appendChild(gl.canvas);\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 = '100';\n\n function handleMouseMove(e: MouseEvent) {\n if (!props.mouseInteraction) return;\n const rect = container!.getBoundingClientRect();\n const x = (e.clientX - rect.left) / rect.width;\n const y = 1.0 - (e.clientY - rect.top) / rect.height;\n program!.uniforms.uMouse.value = [x, y];\n }\n\n container.addEventListener('mousemove', handleMouseMove);\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 program = null;\n};\n\nonMounted(() => {\n initBalatro();\n});\n\nonUnmounted(() => {\n cleanup();\n});\n\nwatch(\n () => [props.pixelFilter, props.isRotate, props.color1, props.color2, props.color3],\n () => {\n if (!program) return;\n program.uniforms.uColor1.value = hexToVec4(props.color1);\n program.uniforms.uColor2.value = hexToVec4(props.color2);\n program.uniforms.uColor3.value = hexToVec4(props.color3);\n program.uniforms.uPixelFilter.value = props.pixelFilter;\n program.uniforms.uIsRotate.value = props.isRotate;\n }\n);\n</script>\n","path":"Balatro/Balatro.vue","_imports_":[],"registryDependencies":[],"dependencies":[],"devDependencies":[]}],"registryDependencies":[],"dependencies":[{"ecosystem":"js","name":"ogl","version":"^1.0.11"}],"devDependencies":[],"categories":["Backgrounds"]}