App端九宫格手势密码需动态适配canvas尺寸、解耦点位与触摸逻辑:用uni.getSystemInfo和selectQuery获取真实宽高,按pixelRatio缩放计算坐标;预定义3×3点阵做最近点吸附,仅在touchStart/touchMove中识别有效点序,避免轨迹拟合。

App端内用 uni-app 实现九宫格手势密码,不能直接套用 H5 的 canvas 绘制逻辑,也不能依赖小程序的 touch 事件行为——App(尤其是 iOS)对触摸事件的拦截、坐标精度、touchmove 触发频率和 canvas 渲染机制都更敏感。核心问题不是“画不画得出来”,而是“点是否被稳定识别”“路径是否可复现”“校验是否防误触”。下面直奔实操。
canvas 在 App 端必须用 uni.createCanvasContext + 动态尺寸适配
很多开发者直接写死 width="300" 和 height="300",结果在 iPhone 15 Pro 或华为 Mate 60 上 canvas 拉伸、坐标偏移、触摸点错位。App 端 canvas 必须按屏幕物理宽度动态计算尺寸:
-
uni.getSystemInfo拿到screenWidth和pixelRatio,再除以 2(因 canvas 像素密度高,渲染需缩放) - 用
uni.createSelectorQuery().in(this)查容器宽高,比直接写死更可靠 - 绘制时所有坐标(圆心、连线)必须基于实际 canvas 宽高做比例换算,不能硬编码
[50,50]这类值 - App 端
canvas-id必须全局唯一,且不能在多个页面重复使用同一 id,否则 iOS 会复用旧上下文导致绘制异常
九宫格点位生成与触摸捕获必须解耦坐标与逻辑
手势解锁的本质是“点序匹配”,不是“轨迹拟合”。所以不要在 touchMove 中疯狂 push 坐标,而应只在 touchStart 和 touchMove 中做“最近点吸附”:
- 预定义 3×3 点阵,每个点存
{x, y, index},x/y 是 canvas 内绝对像素位置(非 rpx) -
touchStart:遍历 9 个点,用欧氏距离找最近点,若距离 selectedIndices = [index] -
touchMove:持续计算当前 touch 坐标到各未选点的距离,仅当新点距离 selectedIndices 中,才追加 - 禁用
touchEnd后自动触发校验——App 端 touch 结束可能延迟,应加 300ms 防抖再校验
校验逻辑不能只比数组相等,要处理常见误操作
用户滑太快、起点偏移、中途抬手又落回,都会导致 selectedIndices 出现脏数据。简单 JSON.stringify(a) === JSON.stringify(b) 会失败:
- 必须限制最小长度:校验前检查
selectedIndices.length ,直接提示“至少连接 4 个点” - 要过滤重复索引:用户来回划同一格,
[0,1,0,2]应转为[0,1,2]或拒绝(取决于安全策略) - 服务端下发的密码是字符串如
"0-2-5-7",前端校验前先.split('-').map(Number),避免类型不一致 - 本地存储密码建议用
uni.setStorageSync('gesture_pwd', data.join('-')),而不是存数组,避免序列化歧义
App 端手势组件必须手动禁用 WebView 默认滚动
在 iOS App 或安卓 WebView 中,手指长按或快速滑动容易触发页面整体滚动,导致 touch 坐标突变、手势中断。这不是 bug,是 WebView 默认行为:
- 在手势页
onLoad中执行:document.body.style.overflow = 'hidden' - 同时监听
touchmove事件并event.preventDefault(),但注意加{ passive: false }选项,否则安卓无效 - 退出页面前记得恢复
overflow,否则影响其他页滚动体验 - 真机调试时,务必关闭 Chrome DevTools 的 “Emulate touch events”,它会干扰原生 touch 流程
最易被忽略的一点:iOS App 端 canvas 的 draw 调用必须显式加 uni.canvasToTempFilePath 才能触发重绘,单纯 ctx.draw() 在某些系统版本下无反应——这不是 bug,是渲染管线差异。每次路径更新后,别忘了补一句 this.ctx.draw(false, () => {}) 确保同步刷新。











