文章时效性提示
这是一篇发布于 1905天前 的文章部分信息可能已经过时,请甄别后使用。

前言
最近做的一个app项目使用的 apicloud 来实现跨平台开发,现在需要为这个 app 添加手势(九宫格)解锁的功能,apicloud 已经有一些第三方的原生实现的手势解锁插件,原生插件性能比较好,调用也比较方便,但是不能对它们的样式做定制修改,所以打算花了一点时间使用h5画布自己来实现这个功能。这篇文章将实现过程整理分享出来,希望有需要的可以了解。分享出来的代码只实现了最基本的设置密码功能、解锁功能、比较密码的功能等,一些高级功能例如:不能限制一个点最多经过多少次、限制用户设置密码的长度本文暂未涉及。
demo版解锁效果演示
原生实现还是其它方式实现?
1、使用 android 和 ios 对应的平台通过原生代码来写手势解锁插件。体验好,但是开发周期长,需要处理各平台的兼容性问题,并且需要学习apicloud平台插件编写方法。(放弃)
2、使用 html5 的 canvas 画布来实现。开发周期短,不需要过多的处理兼容性问题,体验好。(选择)
原理分析
手势解锁
通过手指将屏幕上的九个点依次连接起来形成一个图案,所以叫图案解锁。如上图每一个解锁圆圈后面其实都是一个数字,每次比较的并不是是用户画出来的图案,而是每次手指经过图案时串联起来的圆圈下的数字组成的密码字符串,本质上我们比较的还是字符串的密码,只不过站在用户的角度看是绘制出来的图案。图案的记忆远比数字字符串记的牢固。
实现步骤
绘制密码盘
密码盘的绘制比较简单,唯一需要注意需要通过动态计算使九个点围成的正方式始终在屏幕的中间位置,在手机上还需要减去状态栏的高度。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 var width = $(document ).width ();var height = $(document ).height () - 40 ; var lockCicle = { x : 0 , y : 0 , color : "#999999" , state : "1" };var offset = (width - height) / 2 ; var arr = []; for (var i = 1 ; i <= 3 ; i++) { for (var j = 1 ; j <= 3 ; j++) { var lockCicle = {}; if (offset > 0 ) { lockCicle.x = (height / 4 ) * j + Math .abs (offset); lockCicle.y = (height / 4 ) * i; lockCicle.state = 0 ; } else { lockCicle.x = (width / 4 ) * j; lockCicle.y = (width / 4 ) * i + Math .abs (offset); lockCicle.state = 0 ; } arr.push (lockCicle); } }function init ( ) { ctx.clearRect (0 , 0 , width, height); pointerArr = []; for (var i = 0 ; i < arr.length ; i++) { arr[i].state = 0 ; drawPointer (i); } }function drawPointer (i ) { ctx.save (); var radius = 0 ; if (hastouch) { radius = width / 12 ; } else { radius = 24 ; } var _fillStyle = "#dd514c" ; var _strokeStyle = "#dd514c" ; if (arr[i].state == 1 ) { _strokeStyle = "#1bd6c5" ; } ctx.beginPath (); ctx.fillStyle = _fillStyle; ctx.arc (arr[i].x , arr[i].y , 6 , 0 , Math .PI * 2 , false ); ctx.fill (); ctx.closePath (); ctx.beginPath (); ctx.strokeStyle = _strokeStyle; ctx.lineWidth = 0.3 ; ctx.lineCap = "round" ; ctx.lineJoin = "round" ; ctx.arc (arr[i].x , arr[i].y , radius, 0 , Math .PI * 2 , false ); ctx.stroke (); ctx.closePath (); ctx.restore (); }init (); JAVASCRIPT
绘制连线
绘制连线的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 var pointerArr = []; var startX, startY; var puts = []; var currentPointer; var pwd = []; var confirmPwd = []; var unlockFlag = false ; function drawLinePointer (x, y, flag ) { ctx.clearRect (0 , 0 , width, height); ctx.save (); ctx.beginPath (); ctx.strokeStyle = "#1bd6c5" ; ctx.lineWidth = 5 ; ctx.lineCap = "round" ; ctx.lineJoin = "round" ; for (var i = 0 ; i < pointerArr.length ; i++) { if (i == 0 ) { ctx.moveTo (pointerArr[i].x , pointerArr[i].y ); } else { ctx.lineTo (pointerArr[i].x , pointerArr[i].y ); } } ctx.stroke (); ctx.closePath (); ctx.restore (); for (var i = 0 ; i < arr.length ; i++) { drawPointer (i); if (ctx.isPointInPath (x, y) && currentPointer != i) { pointerArr.push ({ x : arr[i].x , y : arr[i].y }); currentPointer = i; puts.push (i + 1 ); startX = arr[i].x ; startY = arr[i].y ; arr[i].state = 1 ; } } if (flag) { ctx.save (); ctx.beginPath (); ctx.globalCompositeOperation = "destination-over" ; ctx.strokeStyle = "#e2e0e0" ; ctx.lineWidth = 5 ; ctx.lineCap = "round" ; ctx.lineJoin = "round" ; ctx.moveTo (startX, startY); ctx.lineTo (x, y); ctx.stroke (); ctx.beginPath (); ctx.restore (); } } JAVASCRIPT
绑定事件
连线的过程就是将 3 个 touch(移动端) 事件组合起来获取当前位置的坐标放入数组中,然后将这些坐标渲染到界面上的过程。
touchstart (mousedown) 当手指(鼠标)按下时设置 isMouseDown=true,同时将该点的坐标保存到线条数组中,并将数组中的点绘制出来。
touchmove (mousemove) 当 isMouseDown=true 时 将手指(鼠标)移动过程中所有的坐标点都保存到萧条数组中,并将数组中的点绘制出来。
mouseup (mouseup) 当手指(鼠标)松开后设置 isMouseDown=fasle.将数组中的所有点绘制出来,清空 pointerArr 数组,然后比较连接的点的数量如果小于 6(自己设置,一般密码 6 位以上)给一个密码长度不够的提示,清空 puts 数组,重新调用 init 方法初始化界面,如果大于等于 6 则密码设置成功。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 var hastouch = "ontouchstart" in window ? true : false , tapstart = hastouch ? "touchstart" : "mousedown" , tapmove = hastouch ? "touchmove" : "mousemove" , tapend = hastouch ? "touchend" : "mouseup" ; lockCnavs.addEventListener (tapstart, function (e ) { isMouseDown = true ; var x1 = hastouch ? e.targetTouches [0 ].pageX : e.clientX - canvas.offsetLeft ; var y1 = hastouch ? e.targetTouches [0 ].pageY : e.clientY - canvas.offsetTop ; drawLinePointer (x1, y1, true ); }); lockCnavs.addEventListener (tapmove, function (e ) { if (isMouseDown) { var x1 = hastouch ? e.targetTouches [0 ].pageX : e.clientX - canvas.offsetLeft ; var y1 = hastouch ? e.targetTouches [0 ].pageY : e.clientY - canvas.offsetTop ; drawLinePointer (x1, y1, true ); } }); lockCnavs.addEventListener (tapend, function (e ) { drawLinePointer (0 , 0 , false ); isMouseDown = false ; pointerArr = []; if (puts.length >= 6 ) { alert ("你的图案密码是: [ " + puts.join (" > " ) + " ]" ); if (unlockFlag) { unlock (); } else { settingUnlockPwd (); } } else { if (puts.length >= 1 ) { alert ("你的图案密码太简单了~~~" ); init (); } } puts = []; }); JAVASCRIPT
实现解锁逻辑
通过上面几步的操作,九宫格解锁每一次绘图之后的数据和显示效果都有了,现在只需要在关键地方添加相应逻辑代码就可以了,这里主要介绍它的实现逻辑就不对代码做封装了。
相关代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 function settingUnlockPwd ( ) { if (pwd.length <= 0 ) { pwd = puts; init (); $("header" ).text ("再次绘制解锁图案" ); } else if (confirmPwd.length <= 0 ) { confirmPwd = puts; } console .log (pwd + " " + confirmPwd); if (pwd.length > 0 && confirmPwd.length > 0 ) { if (compareArr (pwd, confirmPwd)) { $("header" ).text ("解锁图案绘制成功" ); init (); } else { $("header" ).text ("两次绘制的解锁图案不一致" ); init (); confirmPwd = []; } } }function unlock ( ) { console .log ("解锁密码:" + puts + " " + confirmPwd); if (compareArr (puts, confirmPwd)) { $("header" ).text ("解锁成功!页面跳转中......" ); } else { $("header" ).text ("解锁图案不正确!!!" ); init (); } } $("footer" ).click (function ( ) { if ($(this ).text () === "解锁" ) { unlockFlag = true ; init (); $("header" ).text ("绘制解锁图案" ); } });function compareArr (arr1, arr2 ) { return arr1.toString () === arr2.toString (); } JAVASCRIPT
后记
本文完整 demo 在线演示地址
canvas 相关文章