今年圣诞节来点不一样的。抛弃鼠标点击,用 MediaPipe 和 Three.js 实现一个手势控制的 3D 粒子圣诞树。本文记录了实现思路和核心代码。 🚀

🎅 项目简介
Gesture Controlled Christmas Tree 是一个 Web 端的互动 Demo。利用计算机视觉技术实时捕捉手势,控制 3D 场景中的粒子变化。粒子可以汇聚成树,或者炸裂成星云,还可以支持照片悬浮展示。
✨ 核心功能
- 🌌 粒子系统:基于 Three.js 构建,高性能渲染大量粒子。
- 🙌 手势交互:利用 MediaPipe 实现无接触控制,无需任何额外硬件。
- 📸 照片展示:支持自定义上传照片,悬浮在 3D 场景中。
- 🎶 音频可视化:Web Audio API 解析音乐频率,让视觉元素随音乐律动。
- 🌏 多语言:支持中英切换。
🎮 交互说明
项目集成了 MediaPipe Tasks Vision 进行手部追踪,预设了三种控制手势:
| 手势 | 图标 | 功能 |
|---|---|---|
| 握拳 (Fist) | ✊ | 聚合:将所有粒子和照片汇聚成旋转的圣诞树形态。 |
| 张开手掌 (Open) | 🖐 | 爆发:圣诞树炸裂,切换为星云环绕模式,照片悬浮。 |
| 捏合 (Pinch) | 👌 | 选中:计算空间距离,选中并放大离视角最近的照片。 |
🤖 识别逻辑
手势识别主要依靠计算关键点之间的欧几里得距离:
// ✊ 握拳检测:判断指尖到掌心(MCP关节)的距离
const isFingerCurled = (tip, mcp) => {
const distance = Math.sqrt(Math.pow(tip.x - mcp.x, 2) + Math.pow(tip.y - mcp.y, 2))
return distance < 0.08 // 阈值,根据实际体验调整
}
// 👌 捏合检测:判断拇指于食指尖距离
const isPinching = (thumb, index) => {
const distance = Math.sqrt(Math.pow(thumb.x - index.x, 2) + Math.pow(thumb.y - index.y, 2))
return distance < 0.06
}为了解决视觉识别抖动的问题(每帧识别结果可能跳变),实现了一个简单的 手势防抖器 (GestureStabilizer),基于滑动窗口算法:
class GestureStabilizer {
constructor(windowSize = 8) {
this.history = [] // 历史帧缓存
this.windowSize = windowSize
}
getStabilizedGesture() {
// 简单投票机制:窗口内超过 50% 的结果一致才判定为有效切换
// ... (投票逻辑)
return maxCount > this.windowSize * 0.5 ? stabilized : lastGesture
}
}🛠️ 技术栈
| 技术 | 用途 |
|---|---|
| Three.js | 核心 3D 渲染引擎,负责场景、粒子和几何体。 |
| MediaPipe | Google 的开源机器学习方案,负责前端即时手部追踪。 |
| EffectComposer | Three.js 的后期处理库,实现 Bloom 辉光效果。 |
| Web Audio API | 原生音频 API,用于获取音频频域数据实现可视化。 |
| Vite | 构建工具,提供极速的开发体验。 |
🚀 快速开始
环境准备
- Node.js 18+
- pnpm / npm
- 必要硬件:摄像头
运行步骤
# 1. 克隆仓库
cd gesture-christmas-main
# 2. 安装依赖
pnpm install
# 3. 启动开发服
pnpm dev注意:由于浏览器安全策略,摄像头访问必须在 HTTPS 或 localhost 环境下进行。请勿直接双击打开 html 文件。
⚙️ 配置参数
项目中的 CONFIG 对象集中管理了主要参数,可以在 index.html 中直接调整:
const CONFIG = {
// 🌟 粒子设置
primaryParticleCount: 1200, // 主粒子数
secondaryParticleCount: 2000, // 背景氛围粒子数
// 🎄 树形态
treeHeight: 30,
treeRadius: 14,
// ✨ 后期特效
bloomStrength: 1.2, // 辉光强度
bloomRadius: 0.5,
// 🎨 颜色
colors: [0x2f4f4f, 0x556b2f, 0xb22222, 0xffd700, 0xffffff],
// 🖼️ 照片展示
photoTreeScale: 0.15, // 树模式下的照片缩放
photoHoverScale: 2.0, // 悬停缩放
}🖼️ 自定义照片
除了默认资源,支持自定义上传:
- 界面操作:点击右下角「上传照片」按钮。
- 配置文件:将图片放入
images/目录,并更新images/manifest.json:
{
"images": ["my-cat.jpg", "party.png", "love.jpg"]
}🎨 实现细节
1. 圆锥螺旋分布
粒子树的构建算法使用了圆锥螺旋方程,确保粒子分布均匀且有层次感:
const angle = (i / count) * Math.PI * 20 // 螺旋角
const y = (i / count) * height - height / 2 // 高度
const r = radius * (1 - (y + height / 2) / height) // 半径随高度收缩2. Bloom 辉光
使用 Three.js 的 UnrealBloomPass 实现辉光。这是提升 3D 场景质感的关键,能让简单的几何体看起来更有“光感”。
3. 音频可视化
利用 AnalyserNode 获取 FFT 数据,将低频分量映射到树的整体 Scale,高频分量映射到背景粒子的闪烁强度。
📦 部署
pnpm build构建产物在 dist/ 目录。推荐部署到 Vercel 或 GitHub Pages。
❓ FAQ
- Q: 无法调用摄像头?
- A: 检查浏览器权限设置。确保是在 HTTPS 或 localhost 下访问。
- Q: 手势识别不准?
- A: 尽量在光线充足的环境下使用。背景不要过于杂乱。
- Q: 帧率低?
- A: 计算机视觉+3D渲染对性能有一定要求。如果卡顿,降低
CONFIG.primaryParticleCount即可。
- A: 计算机视觉+3D渲染对性能有一定要求。如果卡顿,降低
🙏 致谢
Merry Christmas & Happy Coding! 🎄