uni-app app端手势解锁必须用canvas实现,不能仅靠v-for模拟;需监听touchstart/move/end三阶段并阻止默认行为,动态计算九宫格坐标,校验完整触摸序列而非简单索引比对,且ios下todataurl可能被禁用。

uni-app App端手势解锁必须用 canvas,不能只靠 v-for 点击模拟
App端(尤其是 iOS)对触摸事件的连续性、坐标精度、防误触要求极高,纯 DOM 点击九宫格(比如用 v-for 渲染 9 个 view)会严重丢点、响应迟钝、无法判断滑动轨迹,导致密码识别率极低甚至完全失效。真实场景下必须用 canvas 捕获原始触摸流。
关键点:
-
canvas需手动计算九宫格坐标点(3×3),不能依赖 CSS 布局定位 - 必须监听
@touchstart、@touchmove、@touchend三阶段,且加.prevent阻止默认滚动行为 - Android 和 iOS 的
touches[0]坐标单位不一致,需统一用getBoundingClientRect()+event.touches[0].clientX转换为 canvas 坐标系 - 不要用
offsetX/offsetY—— App 端 H5 渲染器不支持或返回0
九宫格点坐标的初始化必须动态适配屏幕宽高比
硬编码 cellSize = 100 或写死 canvas 宽高会导致不同机型点位偏移,用户划到 A 点却识别成 B 点。正确做法是:在 onMounted 中读取设备信息,按安全区域缩放计算。
实操建议:
- 调用
uni.getSystemInfoSync()获取screenWidth和screenHeight - canvas 宽高设为
Math.min(screenWidth, screenHeight) * 0.8(留出上下状态栏空间) - 每个格子边长 =
canvasWidth / 3,中心点坐标按(col + 0.5) * cellSize计算,避免整数截断误差 - 保存点位数组时,用对象而非索引:例如
{ index: 4, x: 150, y: 150 },后续校验更可靠
手势路径校验逻辑不能只比对「点序号数组」
直接比对 [0,1,2,5,8] 这种索引序列看似简单,但实际会漏判大量合法操作:比如用户从点 0 开始,中途经过点 1 但未停留(只是划过),系统仍可能错误计入;或两点间距离太短被过滤,导致序列错位。
推荐校验方式:
- 记录完整触摸点序列(
points: [{x,y,time},...]),剔除密集冗余点(距离 - 将每段有效移动映射到最近的格点,用「最小欧氏距离 + 时间邻近性」双重判定是否命中某点
- 最终生成的密码序列必须满足:长度 ≥ 4、无重复点、相邻点间存在有效连线(排除“跳点”如 0→2 不经 1)
- 验证时用深比较(
JSON.stringify(pwd1) === JSON.stringify(pwd2)),别用==或includes
App端存储密码必须用 uni.setStorageSync,且明文存数组有风险
很多示例直接把 [0,1,2,5] 存进 uni.setStorageSync('unlockPwd', pwd),这在 App 端等于把密码裸奔。iOS 审核可能因此拒审,Android 也易被 root 后提取。
最低限度防护:
- 存前做一次简单混淆:比如
pwd.map(i => (i * 7 + 3) % 9),虽非加密但增加静态分析成本 - 绝不存原始字符串如
"0,1,2,5"—— 这类格式一眼可读 - 敏感场景建议结合
uni.getStorageInfoSync().keys检查是否存在异常 key,防篡改 - 首次设置密码后,立即清空内存中原始数组引用:
initPwd.value = [];
最麻烦但必要的点:iOS WKWebView 下 canvas 的 toDataURL() 可能被禁用,所有绘制逻辑必须纯前端完成,不能依赖服务端回传校验。这点容易被忽略,直到真机测试才发现手势无效。











