Skip to content

Commit 1c34f27

Browse files
committed
fix: plasma pingpong animation bug
1 parent e84a9b5 commit 1c34f27

8 files changed

Lines changed: 50 additions & 30 deletions

File tree

public/r/Plasma-JS-CSS.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
"files": [
1111
{
1212
"path": "public/default/src/content/Backgrounds/Plasma/Plasma.jsx",
13-
"content": "import { useEffect, useRef } from 'react';\nimport { Renderer, Program, Mesh, Triangle } from 'ogl';\nimport './Plasma.css';\n\nconst hexToRgb = hex => {\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\nexport const Plasma = ({\n color = '#ffffff',\n speed = 1,\n direction = 'forward',\n scale = 1,\n opacity = 1,\n mouseInteractive = true\n}) => {\n const containerRef = useRef(null);\n const mousePos = useRef({ x: 0, y: 0 });\n\n useEffect(() => {\n if (!containerRef.current) return;\n\n const useCustomColor = color ? 1.0 : 0.0;\n const customColorRgb = color ? hexToRgb(color) : [1, 1, 1];\n\n const directionMultiplier = 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;\n canvas.style.display = 'block';\n canvas.style.width = '100%';\n canvas.style.height = '100%';\n containerRef.current.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: speed * 0.4 },\n uDirection: { value: directionMultiplier },\n uScale: { value: scale },\n uOpacity: { value: opacity },\n uMouse: { value: new Float32Array([0, 0]) },\n uMouseInteractive: { value: mouseInteractive ? 1.0 : 0.0 }\n }\n });\n\n const mesh = new Mesh(gl, { geometry, program });\n\n const handleMouseMove = e => {\n if (!mouseInteractive) return;\n const rect = containerRef.current.getBoundingClientRect();\n mousePos.current.x = e.clientX - rect.left;\n mousePos.current.y = e.clientY - rect.top;\n const mouseUniform = program.uniforms.uMouse.value;\n mouseUniform[0] = mousePos.current.x;\n mouseUniform[1] = mousePos.current.y;\n };\n\n if (mouseInteractive) {\n containerRef.current.addEventListener('mousemove', handleMouseMove);\n }\n\n const setSize = () => {\n const rect = containerRef.current.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;\n res[0] = gl.drawingBufferWidth;\n res[1] = gl.drawingBufferHeight;\n };\n\n const ro = new ResizeObserver(setSize);\n ro.observe(containerRef.current);\n setSize();\n\n let raf = 0;\n const t0 = performance.now();\n const loop = t => {\n let timeValue = (t - t0) * 0.001;\n\n if (direction === 'pingpong') {\n const cycle = Math.sin(timeValue * 0.5) * directionMultiplier;\n program.uniforms.uDirection.value = cycle;\n }\n\n program.uniforms.iTime.value = timeValue;\n renderer.render({ scene: mesh });\n raf = requestAnimationFrame(loop);\n };\n raf = requestAnimationFrame(loop);\n\n return () => {\n cancelAnimationFrame(raf);\n ro.disconnect();\n if (mouseInteractive && containerRef.current) {\n containerRef.current.removeEventListener('mousemove', handleMouseMove);\n }\n try {\n // eslint-disable-next-line react-hooks/exhaustive-deps\n containerRef.current?.removeChild(canvas);\n } catch {\n console.warn('Canvas already removed from container');\n }\n };\n }, [color, speed, direction, scale, opacity, mouseInteractive]);\n\n return <div ref={containerRef} className=\"plasma-container\" />;\n};\n\nexport default Plasma;\n",
13+
"content": "import { useEffect, useRef } from 'react';\nimport { Renderer, Program, Mesh, Triangle } from 'ogl';\nimport './Plasma.css';\n\nconst hexToRgb = hex => {\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\nexport const Plasma = ({\n color = '#ffffff',\n speed = 1,\n direction = 'forward',\n scale = 1,\n opacity = 1,\n mouseInteractive = true\n}) => {\n const containerRef = useRef(null);\n const mousePos = useRef({ x: 0, y: 0 });\n\n useEffect(() => {\n if (!containerRef.current) return;\n const containerEl = containerRef.current;\n\n const useCustomColor = color ? 1.0 : 0.0;\n const customColorRgb = color ? hexToRgb(color) : [1, 1, 1];\n\n const directionMultiplier = 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;\n canvas.style.display = 'block';\n canvas.style.width = '100%';\n canvas.style.height = '100%';\n containerRef.current.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: speed * 0.4 },\n uDirection: { value: directionMultiplier },\n uScale: { value: scale },\n uOpacity: { value: opacity },\n uMouse: { value: new Float32Array([0, 0]) },\n uMouseInteractive: { value: mouseInteractive ? 1.0 : 0.0 }\n }\n });\n\n const mesh = new Mesh(gl, { geometry, program });\n\n const handleMouseMove = e => {\n if (!mouseInteractive) return;\n const rect = containerRef.current.getBoundingClientRect();\n mousePos.current.x = e.clientX - rect.left;\n mousePos.current.y = e.clientY - rect.top;\n const mouseUniform = program.uniforms.uMouse.value;\n mouseUniform[0] = mousePos.current.x;\n mouseUniform[1] = mousePos.current.y;\n };\n\n if (mouseInteractive) {\n containerEl.addEventListener('mousemove', handleMouseMove);\n }\n\n const setSize = () => {\n const rect = containerRef.current.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;\n res[0] = gl.drawingBufferWidth;\n res[1] = gl.drawingBufferHeight;\n };\n\n const ro = new ResizeObserver(setSize);\n ro.observe(containerEl);\n setSize();\n\n let raf = 0;\n const t0 = performance.now();\n const loop = t => {\n let timeValue = (t - t0) * 0.001;\n if (direction === 'pingpong') {\n const pingpongDuration = 10;\n const segmentTime = timeValue % pingpongDuration;\n const isForward = Math.floor(timeValue / pingpongDuration) % 2 === 0;\n const u = segmentTime / pingpongDuration;\n const smooth = u * u * (3 - 2 * u);\n const pingpongTime = isForward ? smooth * pingpongDuration : (1 - smooth) * pingpongDuration;\n program.uniforms.uDirection.value = 1.0;\n program.uniforms.iTime.value = pingpongTime;\n } else {\n program.uniforms.iTime.value = timeValue;\n }\n renderer.render({ scene: mesh });\n raf = requestAnimationFrame(loop);\n };\n raf = requestAnimationFrame(loop);\n\n return () => {\n cancelAnimationFrame(raf);\n ro.disconnect();\n if (mouseInteractive && containerEl) {\n containerEl.removeEventListener('mousemove', handleMouseMove);\n }\n try {\n containerEl?.removeChild(canvas);\n } catch {\n console.warn('Canvas already removed from container');\n }\n };\n }, [color, speed, direction, scale, opacity, mouseInteractive]);\n\n return <div ref={containerRef} className=\"plasma-container\" />;\n};\n\nexport default Plasma;\n",
1414
"type": "registry:component"
1515
},
1616
{

0 commit comments

Comments
 (0)