{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "Ballpit-JS-TW", "type": "registry:block", "title": "Ballpit", "description": "Physics ball pit simulation with bouncing colorful spheres.", "dependencies": [ "three" ], "files": [ { "path": "public/tailwind/src/tailwind/Backgrounds/Ballpit/Ballpit.jsx", "content": "import { useRef, useEffect } from 'react';\r\nimport {\r\n Clock as e,\r\n PerspectiveCamera as t,\r\n Scene as i,\r\n WebGLRenderer as s,\r\n SRGBColorSpace as n,\r\n MathUtils as o,\r\n Vector2 as r,\r\n Vector3 as a,\r\n MeshPhysicalMaterial as c,\r\n ShaderChunk as h,\r\n Color as l,\r\n Object3D as m,\r\n InstancedMesh as d,\r\n PMREMGenerator as p,\r\n SphereGeometry as g,\r\n AmbientLight as f,\r\n PointLight as u,\r\n ACESFilmicToneMapping as v,\r\n Raycaster as y,\r\n Plane as w\r\n} from 'three';\r\nimport { RoomEnvironment as z } from 'three/examples/jsm/environments/RoomEnvironment.js';\r\n\r\nclass x {\r\n #e;\r\n canvas;\r\n camera;\r\n cameraMinAspect;\r\n cameraMaxAspect;\r\n cameraFov;\r\n maxPixelRatio;\r\n minPixelRatio;\r\n scene;\r\n renderer;\r\n #t;\r\n size = { width: 0, height: 0, wWidth: 0, wHeight: 0, ratio: 0, pixelRatio: 0 };\r\n render = this.#i;\r\n onBeforeRender = () => {};\r\n onAfterRender = () => {};\r\n onAfterResize = () => {};\r\n #s = false;\r\n #n = false;\r\n isDisposed = false;\r\n #o;\r\n #r;\r\n #a;\r\n #c = new e();\r\n #h = { elapsed: 0, delta: 0 };\r\n #l;\r\n constructor(e) {\r\n this.#e = { ...e };\r\n this.#m();\r\n this.#d();\r\n this.#p();\r\n this.resize();\r\n this.#g();\r\n }\r\n #m() {\r\n this.camera = new t();\r\n this.cameraFov = this.camera.fov;\r\n }\r\n #d() {\r\n this.scene = new i();\r\n }\r\n #p() {\r\n if (this.#e.canvas) {\r\n this.canvas = this.#e.canvas;\r\n } else if (this.#e.id) {\r\n this.canvas = document.getElementById(this.#e.id);\r\n } else {\r\n console.error('Three: Missing canvas or id parameter');\r\n }\r\n this.canvas.style.display = 'block';\r\n const e = {\r\n canvas: this.canvas,\r\n powerPreference: 'high-performance',\r\n ...(this.#e.rendererOptions ?? {})\r\n };\r\n this.renderer = new s(e);\r\n this.renderer.outputColorSpace = n;\r\n }\r\n #g() {\r\n if (!(this.#e.size instanceof Object)) {\r\n window.addEventListener('resize', this.#f.bind(this));\r\n if (this.#e.size === 'parent' && this.canvas.parentNode) {\r\n this.#r = new ResizeObserver(this.#f.bind(this));\r\n this.#r.observe(this.canvas.parentNode);\r\n }\r\n }\r\n this.#o = new IntersectionObserver(this.#u.bind(this), {\r\n root: null,\r\n rootMargin: '0px',\r\n threshold: 0\r\n });\r\n this.#o.observe(this.canvas);\r\n document.addEventListener('visibilitychange', this.#v.bind(this));\r\n }\r\n #y() {\r\n window.removeEventListener('resize', this.#f.bind(this));\r\n this.#r?.disconnect();\r\n this.#o?.disconnect();\r\n document.removeEventListener('visibilitychange', this.#v.bind(this));\r\n }\r\n #u(e) {\r\n this.#s = e[0].isIntersecting;\r\n this.#s ? this.#w() : this.#z();\r\n }\r\n #v() {\r\n if (this.#s) {\r\n document.hidden ? this.#z() : this.#w();\r\n }\r\n }\r\n #f() {\r\n if (this.#a) clearTimeout(this.#a);\r\n this.#a = setTimeout(this.resize.bind(this), 100);\r\n }\r\n resize() {\r\n let e, t;\r\n if (this.#e.size instanceof Object) {\r\n e = this.#e.size.width;\r\n t = this.#e.size.height;\r\n } else if (this.#e.size === 'parent' && this.canvas.parentNode) {\r\n e = this.canvas.parentNode.offsetWidth;\r\n t = this.canvas.parentNode.offsetHeight;\r\n } else {\r\n e = window.innerWidth;\r\n t = window.innerHeight;\r\n }\r\n this.size.width = e;\r\n this.size.height = t;\r\n this.size.ratio = e / t;\r\n this.#x();\r\n this.#b();\r\n this.onAfterResize(this.size);\r\n }\r\n #x() {\r\n this.camera.aspect = this.size.width / this.size.height;\r\n if (this.camera.isPerspectiveCamera && this.cameraFov) {\r\n if (this.cameraMinAspect && this.camera.aspect < this.cameraMinAspect) {\r\n this.#A(this.cameraMinAspect);\r\n } else if (this.cameraMaxAspect && this.camera.aspect > this.cameraMaxAspect) {\r\n this.#A(this.cameraMaxAspect);\r\n } else {\r\n this.camera.fov = this.cameraFov;\r\n }\r\n }\r\n this.camera.updateProjectionMatrix();\r\n this.updateWorldSize();\r\n }\r\n #A(e) {\r\n const t = Math.tan(o.degToRad(this.cameraFov / 2)) / (this.camera.aspect / e);\r\n this.camera.fov = 2 * o.radToDeg(Math.atan(t));\r\n }\r\n updateWorldSize() {\r\n if (this.camera.isPerspectiveCamera) {\r\n const e = (this.camera.fov * Math.PI) / 180;\r\n this.size.wHeight = 2 * Math.tan(e / 2) * this.camera.position.length();\r\n this.size.wWidth = this.size.wHeight * this.camera.aspect;\r\n } else if (this.camera.isOrthographicCamera) {\r\n this.size.wHeight = this.camera.top - this.camera.bottom;\r\n this.size.wWidth = this.camera.right - this.camera.left;\r\n }\r\n }\r\n #b() {\r\n this.renderer.setSize(this.size.width, this.size.height);\r\n this.#t?.setSize(this.size.width, this.size.height);\r\n let e = window.devicePixelRatio;\r\n if (this.maxPixelRatio && e > this.maxPixelRatio) {\r\n e = this.maxPixelRatio;\r\n } else if (this.minPixelRatio && e < this.minPixelRatio) {\r\n e = this.minPixelRatio;\r\n }\r\n this.renderer.setPixelRatio(e);\r\n this.size.pixelRatio = e;\r\n }\r\n get postprocessing() {\r\n return this.#t;\r\n }\r\n set postprocessing(e) {\r\n this.#t = e;\r\n this.render = e.render.bind(e);\r\n }\r\n #w() {\r\n if (this.#n) return;\r\n const animate = () => {\r\n this.#l = requestAnimationFrame(animate);\r\n this.#h.delta = this.#c.getDelta();\r\n this.#h.elapsed += this.#h.delta;\r\n this.onBeforeRender(this.#h);\r\n this.render();\r\n this.onAfterRender(this.#h);\r\n };\r\n this.#n = true;\r\n this.#c.start();\r\n animate();\r\n }\r\n #z() {\r\n if (this.#n) {\r\n cancelAnimationFrame(this.#l);\r\n this.#n = false;\r\n this.#c.stop();\r\n }\r\n }\r\n #i() {\r\n this.renderer.render(this.scene, this.camera);\r\n }\r\n clear() {\r\n this.scene.traverse(e => {\r\n if (e.isMesh && typeof e.material === 'object' && e.material !== null) {\r\n Object.keys(e.material).forEach(t => {\r\n const i = e.material[t];\r\n if (i !== null && typeof i === 'object' && typeof i.dispose === 'function') {\r\n i.dispose();\r\n }\r\n });\r\n e.material.dispose();\r\n e.geometry.dispose();\r\n }\r\n });\r\n this.scene.clear();\r\n }\r\n dispose() {\r\n this.#y();\r\n this.#z();\r\n this.clear();\r\n this.#t?.dispose();\r\n this.renderer.dispose();\r\n this.isDisposed = true;\r\n }\r\n}\r\n\r\nconst b = new Map(),\r\n A = new r();\r\nlet R = false;\r\nfunction S(e) {\r\n const t = {\r\n position: new r(),\r\n nPosition: new r(),\r\n hover: false,\r\n touching: false,\r\n onEnter() {},\r\n onMove() {},\r\n onClick() {},\r\n onLeave() {},\r\n ...e\r\n };\r\n (function (e, t) {\r\n if (!b.has(e)) {\r\n b.set(e, t);\r\n if (!R) {\r\n document.body.addEventListener('pointermove', M);\r\n document.body.addEventListener('pointerleave', L);\r\n document.body.addEventListener('click', C);\r\n\r\n document.body.addEventListener('touchstart', TouchStart, { passive: false });\r\n document.body.addEventListener('touchmove', TouchMove, { passive: false });\r\n document.body.addEventListener('touchend', TouchEnd, { passive: false });\r\n document.body.addEventListener('touchcancel', TouchEnd, { passive: false });\r\n\r\n R = true;\r\n }\r\n }\r\n })(e.domElement, t);\r\n t.dispose = () => {\r\n const t = e.domElement;\r\n b.delete(t);\r\n if (b.size === 0) {\r\n document.body.removeEventListener('pointermove', M);\r\n document.body.removeEventListener('pointerleave', L);\r\n document.body.removeEventListener('click', C);\r\n\r\n document.body.removeEventListener('touchstart', TouchStart);\r\n document.body.removeEventListener('touchmove', TouchMove);\r\n document.body.removeEventListener('touchend', TouchEnd);\r\n document.body.removeEventListener('touchcancel', TouchEnd);\r\n\r\n R = false;\r\n }\r\n };\r\n return t;\r\n}\r\n\r\nfunction M(e) {\r\n A.x = e.clientX;\r\n A.y = e.clientY;\r\n processInteraction();\r\n}\r\n\r\nfunction processInteraction() {\r\n for (const [elem, t] of b) {\r\n const i = elem.getBoundingClientRect();\r\n if (D(i)) {\r\n P(t, i);\r\n if (!t.hover) {\r\n t.hover = true;\r\n t.onEnter(t);\r\n }\r\n t.onMove(t);\r\n } else if (t.hover && !t.touching) {\r\n t.hover = false;\r\n t.onLeave(t);\r\n }\r\n }\r\n}\r\n\r\nfunction C(e) {\r\n A.x = e.clientX;\r\n A.y = e.clientY;\r\n for (const [elem, t] of b) {\r\n const i = elem.getBoundingClientRect();\r\n P(t, i);\r\n if (D(i)) t.onClick(t);\r\n }\r\n}\r\n\r\nfunction L() {\r\n for (const t of b.values()) {\r\n if (t.hover) {\r\n t.hover = false;\r\n t.onLeave(t);\r\n }\r\n }\r\n}\r\n\r\nfunction TouchStart(e) {\r\n if (e.touches.length > 0) {\r\n e.preventDefault();\r\n A.x = e.touches[0].clientX;\r\n A.y = e.touches[0].clientY;\r\n\r\n for (const [elem, t] of b) {\r\n const rect = elem.getBoundingClientRect();\r\n if (D(rect)) {\r\n t.touching = true;\r\n P(t, rect);\r\n if (!t.hover) {\r\n t.hover = true;\r\n t.onEnter(t);\r\n }\r\n t.onMove(t);\r\n }\r\n }\r\n }\r\n}\r\n\r\nfunction TouchMove(e) {\r\n if (e.touches.length > 0) {\r\n e.preventDefault();\r\n A.x = e.touches[0].clientX;\r\n A.y = e.touches[0].clientY;\r\n\r\n for (const [elem, t] of b) {\r\n const rect = elem.getBoundingClientRect();\r\n P(t, rect);\r\n\r\n if (D(rect)) {\r\n if (!t.hover) {\r\n t.hover = true;\r\n t.touching = true;\r\n t.onEnter(t);\r\n }\r\n t.onMove(t);\r\n } else if (t.hover && t.touching) {\r\n t.onMove(t);\r\n }\r\n }\r\n }\r\n}\r\n\r\nfunction TouchEnd() {\r\n for (const [, t] of b) {\r\n if (t.touching) {\r\n t.touching = false;\r\n if (t.hover) {\r\n t.hover = false;\r\n t.onLeave(t);\r\n }\r\n }\r\n }\r\n}\r\n\r\nfunction P(e, t) {\r\n const { position: i, nPosition: s } = e;\r\n i.x = A.x - t.left;\r\n i.y = A.y - t.top;\r\n s.x = (i.x / t.width) * 2 - 1;\r\n s.y = (-i.y / t.height) * 2 + 1;\r\n}\r\nfunction D(e) {\r\n const { x: t, y: i } = A;\r\n const { left: s, top: n, width: o, height: r } = e;\r\n return t >= s && t <= s + o && i >= n && i <= n + r;\r\n}\r\n\r\nconst { randFloat: k, randFloatSpread: E } = o;\r\nconst F = new a();\r\nconst I = new a();\r\nconst O = new a();\r\nconst V = new a();\r\nconst B = new a();\r\nconst N = new a();\r\nconst _ = new a();\r\nconst j = new a();\r\nconst H = new a();\r\nconst T = new a();\r\n\r\nclass W {\r\n constructor(e) {\r\n this.config = e;\r\n this.positionData = new Float32Array(3 * e.count).fill(0);\r\n this.velocityData = new Float32Array(3 * e.count).fill(0);\r\n this.sizeData = new Float32Array(e.count).fill(1);\r\n this.center = new a();\r\n this.#R();\r\n this.setSizes();\r\n }\r\n #R() {\r\n const { config: e, positionData: t } = this;\r\n this.center.toArray(t, 0);\r\n for (let i = 1; i < e.count; i++) {\r\n const s = 3 * i;\r\n t[s] = E(2 * e.maxX);\r\n t[s + 1] = E(2 * e.maxY);\r\n t[s + 2] = E(2 * e.maxZ);\r\n }\r\n }\r\n setSizes() {\r\n const { config: e, sizeData: t } = this;\r\n t[0] = e.size0;\r\n for (let i = 1; i < e.count; i++) {\r\n t[i] = k(e.minSize, e.maxSize);\r\n }\r\n }\r\n update(e) {\r\n const { config: t, center: i, positionData: s, sizeData: n, velocityData: o } = this;\r\n let r = 0;\r\n if (t.controlSphere0) {\r\n r = 1;\r\n F.fromArray(s, 0);\r\n F.lerp(i, 0.1).toArray(s, 0);\r\n V.set(0, 0, 0).toArray(o, 0);\r\n }\r\n for (let idx = r; idx < t.count; idx++) {\r\n const base = 3 * idx;\r\n I.fromArray(s, base);\r\n B.fromArray(o, base);\r\n B.y -= e.delta * t.gravity * n[idx];\r\n B.multiplyScalar(t.friction);\r\n B.clampLength(0, t.maxVelocity);\r\n I.add(B);\r\n I.toArray(s, base);\r\n B.toArray(o, base);\r\n }\r\n for (let idx = r; idx < t.count; idx++) {\r\n const base = 3 * idx;\r\n I.fromArray(s, base);\r\n B.fromArray(o, base);\r\n const radius = n[idx];\r\n for (let jdx = idx + 1; jdx < t.count; jdx++) {\r\n const otherBase = 3 * jdx;\r\n O.fromArray(s, otherBase);\r\n N.fromArray(o, otherBase);\r\n const otherRadius = n[jdx];\r\n _.copy(O).sub(I);\r\n const dist = _.length();\r\n const sumRadius = radius + otherRadius;\r\n if (dist < sumRadius) {\r\n const overlap = sumRadius - dist;\r\n j.copy(_)\r\n .normalize()\r\n .multiplyScalar(0.5 * overlap);\r\n H.copy(j).multiplyScalar(Math.max(B.length(), 1));\r\n T.copy(j).multiplyScalar(Math.max(N.length(), 1));\r\n I.sub(j);\r\n B.sub(H);\r\n I.toArray(s, base);\r\n B.toArray(o, base);\r\n O.add(j);\r\n N.add(T);\r\n O.toArray(s, otherBase);\r\n N.toArray(o, otherBase);\r\n }\r\n }\r\n if (t.controlSphere0) {\r\n _.copy(F).sub(I);\r\n const dist = _.length();\r\n const sumRadius0 = radius + n[0];\r\n if (dist < sumRadius0) {\r\n const diff = sumRadius0 - dist;\r\n j.copy(_.normalize()).multiplyScalar(diff);\r\n H.copy(j).multiplyScalar(Math.max(B.length(), 2));\r\n I.sub(j);\r\n B.sub(H);\r\n }\r\n }\r\n if (Math.abs(I.x) + radius > t.maxX) {\r\n I.x = Math.sign(I.x) * (t.maxX - radius);\r\n B.x = -B.x * t.wallBounce;\r\n }\r\n if (t.gravity === 0) {\r\n if (Math.abs(I.y) + radius > t.maxY) {\r\n I.y = Math.sign(I.y) * (t.maxY - radius);\r\n B.y = -B.y * t.wallBounce;\r\n }\r\n } else if (I.y - radius < -t.maxY) {\r\n I.y = -t.maxY + radius;\r\n B.y = -B.y * t.wallBounce;\r\n }\r\n const maxBoundary = Math.max(t.maxZ, t.maxSize);\r\n if (Math.abs(I.z) + radius > maxBoundary) {\r\n I.z = Math.sign(I.z) * (t.maxZ - radius);\r\n B.z = -B.z * t.wallBounce;\r\n }\r\n I.toArray(s, base);\r\n B.toArray(o, base);\r\n }\r\n }\r\n}\r\n\r\nclass Y extends c {\r\n constructor(e) {\r\n super(e);\r\n this.uniforms = {\r\n thicknessDistortion: { value: 0.1 },\r\n thicknessAmbient: { value: 0 },\r\n thicknessAttenuation: { value: 0.1 },\r\n thicknessPower: { value: 2 },\r\n thicknessScale: { value: 10 }\r\n };\r\n this.defines.USE_UV = '';\r\n this.onBeforeCompile = e => {\r\n Object.assign(e.uniforms, this.uniforms);\r\n e.fragmentShader =\r\n '\\n uniform float thicknessPower;\\n uniform float thicknessScale;\\n uniform float thicknessDistortion;\\n uniform float thicknessAmbient;\\n uniform float thicknessAttenuation;\\n ' +\r\n e.fragmentShader;\r\n e.fragmentShader = e.fragmentShader.replace(\r\n 'void main() {',\r\n '\\n void RE_Direct_Scattering(const in IncidentLight directLight, const in vec2 uv, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, inout ReflectedLight reflectedLight) {\\n vec3 scatteringHalf = normalize(directLight.direction + (geometryNormal * thicknessDistortion));\\n float scatteringDot = pow(saturate(dot(geometryViewDir, -scatteringHalf)), thicknessPower) * thicknessScale;\\n #ifdef USE_COLOR\\n vec3 scatteringIllu = (scatteringDot + thicknessAmbient) * vColor;\\n #else\\n vec3 scatteringIllu = (scatteringDot + thicknessAmbient) * diffuse;\\n #endif\\n reflectedLight.directDiffuse += scatteringIllu * thicknessAttenuation * directLight.color;\\n }\\n\\n void main() {\\n '\r\n );\r\n const t = h.lights_fragment_begin.replaceAll(\r\n 'RE_Direct( directLight, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight );',\r\n '\\n RE_Direct( directLight, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight );\\n RE_Direct_Scattering(directLight, vUv, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, reflectedLight);\\n '\r\n );\r\n e.fragmentShader = e.fragmentShader.replace('#include ', t);\r\n if (this.onBeforeCompile2) this.onBeforeCompile2(e);\r\n };\r\n }\r\n}\r\n\r\nconst X = {\r\n count: 200,\r\n colors: [0, 0, 0],\r\n ambientColor: 16777215,\r\n ambientIntensity: 1,\r\n lightIntensity: 200,\r\n materialParams: {\r\n metalness: 0.5,\r\n roughness: 0.5,\r\n clearcoat: 1,\r\n clearcoatRoughness: 0.15\r\n },\r\n minSize: 0.5,\r\n maxSize: 1,\r\n size0: 1,\r\n gravity: 0.5,\r\n friction: 0.9975,\r\n wallBounce: 0.95,\r\n maxVelocity: 0.15,\r\n maxX: 5,\r\n maxY: 5,\r\n maxZ: 2,\r\n controlSphere0: false,\r\n followCursor: true\r\n};\r\n\r\nconst U = new m();\r\n\r\nclass Z extends d {\r\n constructor(e, t = {}) {\r\n const i = { ...X, ...t };\r\n const s = new z();\r\n const n = new p(e, 0.04).fromScene(s).texture;\r\n const o = new g();\r\n const r = new Y({ envMap: n, ...i.materialParams });\r\n r.envMapRotation.x = -Math.PI / 2;\r\n super(o, r, i.count);\r\n this.config = i;\r\n this.physics = new W(i);\r\n this.#S();\r\n this.setColors(i.colors);\r\n }\r\n #S() {\r\n this.ambientLight = new f(this.config.ambientColor, this.config.ambientIntensity);\r\n this.add(this.ambientLight);\r\n this.light = new u(this.config.colors[0], this.config.lightIntensity);\r\n this.add(this.light);\r\n }\r\n setColors(e) {\r\n if (Array.isArray(e) && e.length > 1) {\r\n const t = (function (e) {\r\n let t, i;\r\n function setColors(e) {\r\n t = e;\r\n i = [];\r\n t.forEach(col => {\r\n i.push(new l(col));\r\n });\r\n }\r\n setColors(e);\r\n return {\r\n setColors,\r\n getColorAt: function (ratio, out = new l()) {\r\n const scaled = Math.max(0, Math.min(1, ratio)) * (t.length - 1);\r\n const idx = Math.floor(scaled);\r\n const start = i[idx];\r\n if (idx >= t.length - 1) return start.clone();\r\n const alpha = scaled - idx;\r\n const end = i[idx + 1];\r\n out.r = start.r + alpha * (end.r - start.r);\r\n out.g = start.g + alpha * (end.g - start.g);\r\n out.b = start.b + alpha * (end.b - start.b);\r\n return out;\r\n }\r\n };\r\n })(e);\r\n for (let idx = 0; idx < this.count; idx++) {\r\n this.setColorAt(idx, t.getColorAt(idx / this.count));\r\n if (idx === 0) {\r\n this.light.color.copy(t.getColorAt(idx / this.count));\r\n }\r\n }\r\n this.instanceColor.needsUpdate = true;\r\n }\r\n }\r\n update(e) {\r\n this.physics.update(e);\r\n for (let idx = 0; idx < this.count; idx++) {\r\n U.position.fromArray(this.physics.positionData, 3 * idx);\r\n if (idx === 0 && this.config.followCursor === false) {\r\n U.scale.setScalar(0);\r\n } else {\r\n U.scale.setScalar(this.physics.sizeData[idx]);\r\n }\r\n U.updateMatrix();\r\n this.setMatrixAt(idx, U.matrix);\r\n if (idx === 0) this.light.position.copy(U.position);\r\n }\r\n this.instanceMatrix.needsUpdate = true;\r\n }\r\n}\r\n\r\nfunction createBallpit(e, t = {}) {\r\n const i = new x({\r\n canvas: e,\r\n size: 'parent',\r\n rendererOptions: { antialias: true, alpha: true }\r\n });\r\n let s;\r\n i.renderer.toneMapping = v;\r\n i.camera.position.set(0, 0, 20);\r\n i.camera.lookAt(0, 0, 0);\r\n i.cameraMaxAspect = 1.5;\r\n i.resize();\r\n initialize(t);\r\n const n = new y();\r\n const o = new w(new a(0, 0, 1), 0);\r\n const r = new a();\r\n let c = false;\r\n\r\n e.style.touchAction = 'none';\r\n e.style.userSelect = 'none';\r\n e.style.webkitUserSelect = 'none';\r\n\r\n const h = S({\r\n domElement: e,\r\n onMove() {\r\n n.setFromCamera(h.nPosition, i.camera);\r\n i.camera.getWorldDirection(o.normal);\r\n n.ray.intersectPlane(o, r);\r\n s.physics.center.copy(r);\r\n s.config.controlSphere0 = true;\r\n },\r\n onLeave() {\r\n s.config.controlSphere0 = false;\r\n }\r\n });\r\n function initialize(e) {\r\n if (s) {\r\n i.clear();\r\n i.scene.remove(s);\r\n }\r\n s = new Z(i.renderer, e);\r\n i.scene.add(s);\r\n }\r\n i.onBeforeRender = e => {\r\n if (!c) s.update(e);\r\n };\r\n i.onAfterResize = e => {\r\n s.config.maxX = e.wWidth / 2;\r\n s.config.maxY = e.wHeight / 2;\r\n };\r\n return {\r\n three: i,\r\n get spheres() {\r\n return s;\r\n },\r\n setCount(e) {\r\n initialize({ ...s.config, count: e });\r\n },\r\n togglePause() {\r\n c = !c;\r\n },\r\n dispose() {\r\n h.dispose();\r\n i.dispose();\r\n }\r\n };\r\n}\r\n\r\nconst Ballpit = ({ className = '', followCursor = true, ...props }) => {\r\n const canvasRef = useRef(null);\r\n const spheresInstanceRef = useRef(null);\r\n\r\n useEffect(() => {\r\n const canvas = canvasRef.current;\r\n if (!canvas) return;\r\n\r\n spheresInstanceRef.current = createBallpit(canvas, { followCursor, ...props });\r\n\r\n return () => {\r\n if (spheresInstanceRef.current) {\r\n spheresInstanceRef.current.dispose();\r\n }\r\n };\r\n // eslint-disable-next-line react-hooks/exhaustive-deps\r\n }, []);\r\n\r\n return ;\r\n};\r\n\r\nexport default Ballpit;\r\n", "type": "registry:component" } ] }