tiger-vue3-hand-gesture-camera
v1.0.4
Published
Vue 3 hand gesture camera component powered by MediaPipe.
Downloads
683
Readme
tiger-vue3-hand-gesture-camera 使用说明书
tiger-vue3-hand-gesture-camera 是一个基于 Vue 3 的手势识别组件插件,集成了:
- MediaPipe 手势识别(双手)
- 可选二维码识别(浏览器支持
BarcodeDetector时启用) - 可选人脸识别:定时截取视频帧,以 Base64 调用业务后端
/recognize,顶部居中展示员工号 - 可选粒子视觉效果(PIXI + GSAP)
适用于希望在 Vue3 项目中快速接入摄像头手势交互的场景。
1. 安装
npm i tiger-vue3-hand-gesture-camera1.1 样式(重要)
原因说明:早期版本(如 1.0.0)在 Vite 库模式下会把样式打到单独的 dist/*.css 里,而入口 JS 没有自动 import 这份 CSS。你在业务项目里只写了 import ... from 'tiger-vue3-hand-gesture-camera' 时,功能 JS 有、样式 CSS 没进打包链,看起来就像「样式完全失效」。
从 1.0.1 起:ESM 产物会在入口自动带上 import './index.css',一般无需再手动引样式。
若你的构建工具仍不处理该 CSS(极少数场景),可在应用入口手动补一行:
import 'tiger-vue3-hand-gesture-camera/style.css'1.2 作为页面中的一块区域(布局)
- 组件内部摄像头区域使用
position: absolute铺满根容器.hgc-root。 - 请保证外层有高度,或使用组件属性
min-height(对应 prop:minHeight,默认280px)。
示例:
<div style="height: 420px">
<HandGestureCamera min-height="100%" />
</div>2. 快速开始
2.1 全局注册(推荐)
import { createApp } from 'vue'
import App from './App.vue'
import HandGesturePlugin from 'tiger-vue3-hand-gesture-camera'
createApp(App).use(HandGesturePlugin).mount('#app')在任意页面中直接使用:
<template>
<HandGestureCamera
@gesture="onGesture"
@gesture-confirmed="onGestureConfirmed"
@qr-detected="onQr"
@face-recognized="onFaceRecognized"
@face-confirmed="onFaceConfirmed"
@face-error="onFaceError"
@error="onError"
/>
</template>
<script setup>
function onGesture(payload) {
console.log('gesture:', payload)
}
function onQr(text) {
console.log('qr:', text)
}
function onFaceRecognized(employeeNo) {
console.log('员工号(变化即触发):', employeeNo)
}
function onGestureConfirmed(payload) {
console.log('手势(防抖确认):', payload)
}
function onFaceConfirmed(employeeNo) {
console.log('员工号(防抖确认):', employeeNo)
}
function onFaceError(err) {
console.warn('人脸识别请求失败:', err)
}
function onError(err) {
console.error(err)
}
</script>2.2 局部按需引入
import { HandGestureCamera } from 'tiger-vue3-hand-gesture-camera'3. 完整示例(推荐配置)
<template>
<HandGestureCamera
:mediapipe-wasm-base="'https://cdn.jsdelivr.net/npm/@mediapipe/[email protected]/wasm'"
:mediapipe-model-path="'https://storage.googleapis.com/mediapipe-models/gesture_recognizer/gesture_recognizer/float16/1/gesture_recognizer.task'"
:gesture-confirm-ms="1000"
:qr-scan-interval-ms="120"
:qr-label-ttl-ms="3000"
:enable-qr="true"
:video-constraints="{ video: { facingMode: 'user' }, audio: false }"
:displacement-map-url="'/images/displacement_map_repeat.jpg'"
face-api-base-url="https://your-api.example.com"
:face-recognize-interval-ms="1800"
:face-stable-count="2"
:gesture-emit-debounce-ms="400"
:face-emit-debounce-ms="500"
@gesture="onGesture"
@gesture-confirmed="onGestureConfirmed"
@qr-detected="onQrDetected"
@face-recognized="onFaceRecognized"
@face-confirmed="onFaceConfirmed"
@face-error="onFaceError"
@error="onError"
/>
</template>
<script setup>
function onGesture(payload) {
// payload: { left, right, label }
console.log(payload)
}
function onQrDetected(text) {
console.log('二维码内容:', text)
}
function onFaceRecognized(employeeNo) {
console.log('人脸识别员工号(变化即触发):', employeeNo)
}
function onGestureConfirmed(payload) {
console.log('手势防抖确认:', payload)
}
function onFaceConfirmed(employeeNo) {
console.log('员工号防抖确认:', employeeNo)
}
function onFaceError(err) {
console.warn('人脸识别失败:', err)
}
function onError(err) {
console.error('插件运行错误:', err)
}
</script>4. Props 参数说明
| 参数名 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| mediapipeWasmBase | string | https://cdn.jsdelivr.net/npm/@mediapipe/[email protected]/wasm | MediaPipe wasm 资源路径 |
| mediapipeModelPath | string | https://storage.googleapis.com/mediapipe-models/gesture_recognizer/gesture_recognizer/float16/1/gesture_recognizer.task | 手势模型文件地址 |
| gestureConfirmMs | number | 1000 | 手势稳定判定时长(毫秒) |
| qrScanIntervalMs | number | 120 | 二维码识别节流间隔(毫秒) |
| qrLabelTtlMs | number | 3000 | 二维码文字显示保留时长(毫秒) |
| enableQr | boolean | true | 是否启用二维码识别 |
| videoConstraints | MediaStreamConstraints | { video: true } | 摄像头采集参数 |
| displacementMapUrl | string | '' | 视觉特效贴图 URL,留空则关闭位移滤镜 |
| minHeight | string | '280px' | 根容器最小高度,嵌入页面时避免高度为 0 |
| faceApiBaseUrl | string | '' | 人脸识别服务基础地址(不含路径末尾斜杠)。设置后组件会请求 {faceApiBaseUrl}/recognize;留空则不请求人脸识别 |
| faceRecognizeIntervalMs | number | 1800 | 人脸识别请求节流间隔(毫秒),避免每帧打接口 |
| faceStableCount | number | 2 | 连续若干次识别到同一员工号后,才更新顶部展示并触发 face-recognized,用于现场抗抖 |
| gestureEmitDebounceMs | number | 400 | 手势签名(左右手 key + 中文标签)连续不变达到该时长后,触发一次 gesture-confirmed(尾随防抖) |
| faceEmitDebounceMs | number | 500 | 在触发 face-recognized(工号变化)后,再经过该静默时长且界面工号未变,触发一次 face-confirmed(尾随防抖;工号快速切换会重置计时) |
4.1 人脸识别后端约定
- 请求:
POST {faceApiBaseUrl}/recognize - 请求头:
Content-Type: application/json - 请求体:
{ "image": "<JPEG 的纯 Base64,不含 data:image 前缀>" } - 响应:JSON。组件会从返回体中递归查找常见工号字段(如
employeeNo、employee_id、jobNo、code等),取到非空字符串即视为识别成功。 - 界面:识别稳定后,页面顶部居中显示「员工号:xxx」。
- 跨域:浏览器直接请求后端时,后端需配置 CORS;若无法改后端,可在业务侧用同源代理转发到真实识别服务。
5. 事件说明
5.1 gesture
手势在 MediaPipe 稳定判定(gestureConfirmMs)后,仅在签名变化时触发(left / right / label 与上一帧不同才派发),避免原先每帧重复触发。
type GesturePayload = {
left: string // 左手手势 key
right: string // 右手手势 key
label: string // 中文标签(例如:左手OK(确定),右手竖拇指)
}无手势时 label 可能为空字符串;从「有手势」变为「无手势」时也会派发一次变化后的载荷,便于父组件清空状态。
5.2 gesture-confirmed
在 gesture 所用同一套稳定手势数据上再做尾随防抖:手势签名连续不变达到 gestureEmitDebounceMs 后,仅派发一次,载荷类型同 GesturePayload。适合业务侧写库、开门等「只要最终确定结果」的场景。
5.3 qr-detected
识别到二维码文本后触发。
- 回调参数:
string(二维码内容)
5.4 face-recognized
人脸识别稳定通过、且解析到员工号后触发(同一员工号仅在变化时再次触发,避免刷屏)。
- 回调参数:
string(员工号)
5.5 face-confirmed
在 face-recognized 触发(工号相对上一次展示发生变化)后,再经过 faceEmitDebounceMs 且界面当前工号仍与本次一致时派发一次,用于父组件落库等,减轻短抖动。若在该时长内工号再次变化,会重新计时。
- 回调参数:
string(员工号)
5.6 face-error
人脸识别流程异常时触发(网络失败、HTTP 非 2xx、response.json() 解析失败等)。不会走全局 error 事件,避免与手势/摄像头错误混在一起刷屏。
- 回调参数:
Error | unknown
5.7 error
运行异常时触发(如摄像头权限、MediaPipe 模型加载失败等)。不包含人脸识别接口失败(见 face-error)。
- 回调参数:
Error | unknown
6. 手势说明(当前实现)
- 内置 MediaPipe 预置手势(如:
Thumb_Up、Victory等) - 额外支持通过关键点判断
OK手势(LANDMARK_OK) - 组件会做稳定判定,避免每帧抖动导致频繁变化
7. 在业务里推荐的接入方式
- 在页面首次进入时请求摄像头权限,并给出明显提示
- 需要低频、确定结果时,优先监听
@gesture-confirmed与@face-confirmed;@gesture仅在签名变化时触发,适合 HUD 或轻量联动 @error统一接入你的错误上报系统(手势与摄像头相关)@face-error单独处理或降级提示(识别服务不稳定时不宜当作致命错误)@face-recognized绑定门禁、签到等业务;faceApiBaseUrl由环境或父项目配置注入,便于多项目复用同一组件- 生产环境建议固定
mediapipeModelPath到你可控的静态资源地址
8. 常见问题(FAQ)
8.1 摄像头打不开
- 检查浏览器权限是否允许摄像头
- 页面建议在
https或localhost下运行 - 查看
@error回调日志定位具体问题
8.2 二维码识别不工作
- 浏览器需支持
BarcodeDetector enableQr需要为true- 可通过控制台查看是否有识别输出
8.3 识别延迟或卡顿
- 提高
gestureConfirmMs会更稳但更慢 - 适当增大
qrScanIntervalMs降低二维码识别频率 - 适当增大
faceRecognizeIntervalMs降低人脸识别接口频率 - 减少页面其他高负载动画/渲染任务
8.4 人脸识别无反应或收不到 face-recognized
- 确认已设置
face-api-base-url(或faceApiBaseUrl),且实际请求地址为{base}/recognize - 打开开发者工具 Network,查看请求是否发出、状态码与响应体结构;若字段名不在组件内置列表中,需调整后端返回上述常见键名之一,或扩展组件内解析逻辑
- 若连续识别结果不一致,可提高
face-stable-count再试 - 跨域被拦截时,控制台会出现 CORS 相关错误,请配置后端 CORS 或走前端代理
9. TypeScript 支持
包内已包含类型声明文件,可直接在 TS 项目中使用:
import HandGesturePlugin, { HandGestureCamera } from 'tiger-vue3-hand-gesture-camera'10. 本地构建(维护者)
npm run build:lib11. 发布到 npm(维护者)
11.1 首次发布前
npm login
npm view tiger-vue3-hand-gesture-camera11.2 发布
npm publish --access public如果账号开启了 2FA,请带 OTP:
npm publish --access public --otp=12345611.3 版本升级发布
npm version patch
npm publish --access public11.4 使用 npm token 发布(维护者)
勿将 token 写入仓库或 README。在本地使用环境变量或 npm login 完成鉴权后执行 npm publish 即可。
