如何在纯前端中通过手势交互来控制星球的转动技术栈MediaPipe Hands - Google 的开源手部追踪库Three.js - 3D 渲染引擎摄像头实时视频流输入项目地址粒子土星手势交互核心实现流程步骤0CDN导入!-- MediaPipe 核心库 --scriptsrchttps://cdn.jsdelivr.net/npm/mediapipe/camera_utils/camera_utils.js/scriptscriptsrchttps://cdn.jsdelivr.net/npm/mediapipe/hands/hands.js/script!-- Three.js (如果需要3D渲染) --scriptsrchttps://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js/script步骤1: 初始化手势检测器consthandsnewHands({locateFile:(file){returnhttps://cdn.jsdelivr.net/npm/mediapipe/hands/${file};}});hands.setOptions({maxNumHands:1,// 只检测一只手modelComplexity:1,// 模型复杂度(0-1)minDetectionConfidence:0.7,// 检测置信度阈值minTrackingConfidence:0.7// 追踪置信度阈值});步骤2: 启动摄像头并持续检测constcameraUtilsnewCamera(videoElement,{onFrame:async(){awaithands.send({image:videoElement});// 每帧都发送给模型分析},width:640,height:480});cameraUtils.start();步骤3: 处理检测结果 - 两个关键手势映射 手势1: 缩放控制 (拇指食指距离)// 获取拇指尖端(landmark 4)和食指尖端(landmark 8)constp1hand[4];// 大拇指指尖constp2hand[8];// 食指指尖// 计算两点间的欧氏距离constdistMath.sqrt((p1.x-p2.x)**2(p1.y-p2.y)**2);// 归一化到 [0, 1] 区间,然后映射到缩放范围 [0.15, 2.5]constnormDistMath.max(0,Math.min(1,(dist-0.02)/0.25));targetScale0.15normDist*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){autoIdleTime0.005;targetScale1.0Math.sin(autoIdleTime)*0.2;// 自动缩放呼吸效果 targetRotX0.4Math.sin(autoIdleTime*0.3)*0.15;// 自动轻微摇摆}Shader中的实际应用手势控制的值最终传递给GPU着色器:uniforms.uScale.valuecurrentScale;// 控制土星大小uniforms.uRotationX.valuecurrentRotX;// 控制视角俯仰在GLSL着色器中应用旋转:// 处理整体视角的 X 轴旋转即手势控制的俯仰角float cxcos(uRotationX);float sxsin(uRotationX);float rypos.y*cx-pos.z*sx;float rzpos.y*sxpos.z*cx;pos.yry;pos.zrz;作者NotSleeply |项目地址粒子土星