Ballpit
ballpit一坑彩色小球的可交互物理球池 · 重力下落+墙壁回弹+球球弹性碰撞+光标排斥(零依赖 canvas2d·chart token·reduced-motion)
用法
基础用法
默认参数即可,移动光标会推开周围小球。
移动光标推开小球
tsx
<div className="relative h-64 overflow-hidden rounded-xl"
style={{ background: "oklch(0.14 0.02 255)" }}>
<Ballpit />
</div>失重漂浮
gravity=0 让小球不下沉,bounce=1 完全弹性永不停。
tsx
<Ballpit gravity={0} bounce={1} count={60} />大球少量
sizeRange 加大、count 减少,营造壁纸级稀疏大球。
tsx
<Ballpit count={14} sizeRange={[20, 36]} gravity={700} />纯背景(不跟光标)
followCursor=false 关闭交互,纯装饰背景;浅色底亦可用。
tsx
<Ballpit followCursor={false} count={100} sizeRange={[8, 18]} />何时用
需要俏皮、可触碰的物理交互背景时用(落地页 Hero、空状态、品牌玩味区)。要静态规则底纹用 DotPattern / GridPattern;要油彩流场用 Balatro;Ballpit 是 canvas2d 实时物理模拟(O(n²) 碰撞),强调交互而非细腻纹理,球多了会掉帧。
导入
ts
import { Ballpit } from "@hulianui/ui"Props
| 名称 | 类型 | 默认 | 说明 |
|---|---|---|---|
| count | number | 80 | 小球数量上限;实际随容器面积自适应缩减。碰撞 O(n²),建议 ≤ 200 |
| colors | string[] | chart token ×5 | 小球配色,按索引循环分配;可传任意 CSS 颜色字符串,默认吃明暗主题 |
| gravity | number | 900 | 重力强度(px/s²);0=失重漂浮,越大越快下沉 |
| bounce | number | 0.86 | 墙壁/互撞能量保留系数(0–1);1=完全弹性永不停 |
| sizeRange | [number, number] | [10, 26] | 小球半径范围 [最小,最大](px);另受容器短边约束 |
| followCursor | boolean | true | 是否跟随光标形成排斥球推开周围小球;关闭后光标无交互 |
| className | string | — | 透传到根容器;组件自带 absolute inset-0 z-0 |
| style | CSSProperties | — | 透传到根容器的内联样式 |
Slots
| 插槽 | 类型 | 说明 |
|---|---|---|
| fallback | ReactNode | reduced-motion / 无 canvas 时的静态兜底(默认一组静态排布的小球占位) |
禁忌 / 坑
- 球数 vs 容器面积:
count是上限,组件按「球总面积 ≤ 容器约 42%」自适应缩减;窄卡里硬塞大球(如 count=28 / r∈[24,44])会触发超填充抖动 —— 让组件自适应,或自己把 count/sizeRange 调小。 - O(n²) 碰撞:球数过大(>200)实时碰撞检测会卡顿,背景场景建议小半径多球而非大球。
- 客户端渲染:依赖 canvas2d 与
requestAnimationFrame,SSR 阶段渲染fallback静态占位;勿在 server component 直接挂载实时逻辑。 - 父容器须
relative+overflow-hidden。
相关
DotPattern · GridPattern · StripedPattern · Spotlight · RetroGrid · Ripple
Playground
<div className="relative h-64 overflow-hidden rounded-xl"
style={{ background: "oklch(0.14 0.02 255)" }}>
<Ballpit
count={80}
gravity={900}
bounce={0.86}
followCursor={true}
/>
</div>