如何在纯前端中通过手势交互来控制星球的转动

1 阅读2分钟

如何在纯前端中通过手势交互来控制星球的转动

技术栈

  • 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({
  maxNumHands1,           // 只检测一只手
  modelComplexity1,       // 模型复杂度(0-1)
  minDetectionConfidence0.7,  // 检测置信度阈值
  minTrackingConfidence0.7    // 追踪置信度阈值
});

步骤2: 启动摄像头并持续检测

const cameraUtils = new Camera(videoElement, {
  onFrameasync () => {
    await hands.send({ image: videoElement });  // 每帧都发送给模型分析
  },
  width640,
  height480
});
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(0Math.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 | 项目地址粒子土星