如何在纯前端中通过手势交互来控制星球的转动
技术栈
- MediaPipe Hands - Google 的开源手部追踪库
- Three.js - 3D 渲染引擎
- 摄像头实时视频流输入
项目地址:粒子土星
手势交互核心实现流程
步骤0:CDN导入
<!-- MediaPipe 核心库 -->
<script src="https://cdn.jsdelivr.net/npm/@mediapipe/camera_utils/camera_utils.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@mediapipe/hands/hands.js"></script>
<!-- Three.js (如果需要3D渲染) -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
步骤1: 初始化手势检测器
const hands = new Hands({
locateFile: (file) => {
return `https://cdn.jsdelivr.net/npm/@mediapipe/hands/${file}`;
}
});
hands.setOptions({
maxNumHands: 1, // 只检测一只手
modelComplexity: 1, // 模型复杂度(0-1)
minDetectionConfidence: 0.7, // 检测置信度阈值
minTrackingConfidence: 0.7 // 追踪置信度阈值
});
步骤2: 启动摄像头并持续检测
const cameraUtils = new Camera(videoElement, {
onFrame: async () => {
await hands.send({ image: videoElement }); // 每帧都发送给模型分析
},
width: 640,
height: 480
});
cameraUtils.start();
步骤3: 处理检测结果 - 两个关键手势映射
🤏 手势1: 缩放控制 (拇指+食指距离)
// 获取拇指尖端(landmark 4)和食指尖端(landmark 8)
const p1 = hand[4]; // 大拇指指尖
const p2 = hand[8]; // 食指指尖
// 计算两点间的欧氏距离
const dist = Math.sqrt((p1.x - p2.x) ** 2 + (p1.y - p2.y) ** 2);
// 归一化到 [0, 1] 区间,然后映射到缩放范围 [0.15, 2.5]
const normDist = Math.max(0, Math.min(1, (dist - 0.02) / 0.25));
targetScale = 0.15 + normDist * 2.35;
效果 : 👌捏合手指 = 缩小 | ✋张开手指 = 放大
✋ 手势2: 俯仰角控制 (手掌Y轴位置)
// 使用手腕位置(landmark 9)的Y坐标
const y = hand[9].y; // 手掌中心点
// 归一化Y坐标到 [0, 1],映射到旋转角度 [-0.6, 1.0] 弧度
const normY = Math.max(0, Math.min(1, (y - 0.1) / 0.8));
targetRotX = -0.6 + normY * 1.6;
效果 : 🖐️手掌上移 = 向上看 | 🖐️手掌下移 = 向下看
项目中其他方面设计
平滑动画系统
为了让动作不卡顿,使用了 线性插值(Lerp) :
const lerpFactor = 0.08; // 插值系数,越小越平滑
currentScale += (targetScale - currentScale) * lerpFactor;
currentRotX += (targetRotX - currentRotX) * lerpFactor;
自动巡航模式
当没有检测到手时,进入自动巡航:
if (!isHandDetected) {
autoIdleTime += 0.005;
targetScale = 1.0 + Math.sin(autoIdleTime) * 0.2; // 自动缩放呼
吸效果
targetRotX = 0.4 + Math.sin(autoIdleTime * 0.3) * 0.15; // 自动轻微摇
摆
}
Shader中的实际应用
手势控制的值最终传递给GPU着色器:
uniforms.uScale.value = currentScale; // 控制土星大小
uniforms.uRotationX.value = currentRotX; // 控制视角俯仰
在GLSL着色器中应用旋转:
// 处理整体视角的 X 轴旋转(即手势控制的俯仰角)
float cx = cos(uRotationX);
float sx = sin(uRotationX);
float ry = pos.y * cx - pos.z * sx;
float rz = pos.y * sx + pos.z * cx;
pos.y = ry;
pos.z = rz;
作者:NotSleeply | 项目地址:粒子土星