最近做的一個app項目使用的apicloud 來實現跨平台開發,現在需要為這個app 添加手勢(九宮格)解鎖的功能,apicloud 已經有一些第三方的原生實現的手勢解鎖插件,因為是原生的性能也比較好,呼叫也比較方便,但是都不能對它們的樣式做修改,所以就打算自己來實現這個功能。這篇文章將實現過程整理分享出來,希望有需要的可以了解。分享出來的代碼只實現了最基本的设置密码功能
、 解锁功能
、 比较密码
的功能等,一些高級功能例如:不能限制一個點最多經過多少次、限制用戶設定密碼的長度。
原生實作還是其它方式實作?
1.使用android 和ios 對應的平台透過原生代碼來寫手勢解鎖插件。體驗好,但是開發週期長,需要處理各平台的相容性問題,並且需要學習apicloud平台插件編寫方法。 (放棄)
2.使用html5 的canvas 畫布來實現。開發週期短,不需要過多的處理相容性問題,體驗好。 (選擇)
原理分析手勢解鎖透過手指將螢幕上的九點依序連接起來形成一個圖案,所以叫圖案解鎖。如上圖每一個解鎖圓圈後面其實都是一個數字,每次比較的並不是是用戶畫出來的圖案,而是每次手指經過圖案時串聯起來的圓圈下的數字組成的密碼字符串,本質上我們比較的還是字串的密碼,只不過站在使用者的角度看是繪製出來的圖案。圖案的記憶遠比數字字串記的牢固。
實現步驟繪製密碼盤密碼盤的繪製比較簡單,唯一需要注意需要透過動態計算使九個點圍成的正方式始終在螢幕的中間位置,在手機上還需要減去狀態列的高度。
var width = $(document).width();var height = $(document).height() - 40; //減去手機狀態欄的高度//九宮格其實就是九個點,9個點的座標對象var lockCicle = { x: 0, //x座標y: 0, //y座標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();繪製連線繪製連線的方法
var pointerArr = []; //連接線點的座標數組var startX, startY; //線條起始點var puts = []; //經過的九個點的數組var currentPointer; //當前點是否已經連接var pwd = []; //密碼var confirmPwd = []; //確認密碼var unlockFlag = false; //是否解鎖的標誌/** **繪製連結線的方法,將座標數組中的點繪製在canvas畫布中**/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.mstartToXlineCap = round; ctx.lineJoin = round; ctx.mstart. lineTo(x, y); ctx.stroke(); ctx.beginPath(); ctx.restore(); }}綁定事件
連線的過程就是將3 個touch(移動端) 事件組合起來取得目前位置的座標放入數組中,然後將這些座標渲染到介面上的過程。
//相容於移動觸摸的事件寫法var hastouch = ontouchstart in window ? true : false, tapstart = hastouch ? touchstart : mousedown, tapmove = hastouch ? touchmove : mousemove, tapend = hastouch ? touchend : mouseupend : mouseupend : mouseupend : mouseupend : mouseupend : mouseupend : mouseupend : mouseupend : mouseupend : mouseupend : mouseupend : mouseupend : mouseupend : mouseupend : mouseupend : mouseupend : mouseupend : mouseupend : mouseupend)按下綁定事件。 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 - canvasouch ? e.targetTouches[0].pageY : e.clientY - canvas.LoffsetTop; 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 pointerAownal =; []; if (puts.length >= 6) { alert(你的圖案密碼是: [ + puts.join( > ) + ]); if (unlockFlag) { //解鎖unlock(); } else { //設定解鎖密碼settingUnlockPwd(); } } else { if ( puts.length >= 1) { alert(你的圖案密碼太簡單了~~~); init(); } } puts = [];});實現解鎖邏輯
透過上面幾步的操作,九宮格解鎖每一次繪圖之後的數據和顯示效果都有了,現在只需要在關鍵地方添加相應邏輯代碼就可以了,這裡主要介紹它的實現邏輯就不對代碼做封裝了。
相關程式碼
//設定解鎖密碼與解鎖測試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(繪製解鎖圖案); }});//比較兩個陣列(Number)是否相等function compareArr(arr1, arr2) { return arr1.toString() === arr2.toString();}
後記
本文完整demo 線上演示地址
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持VeVb武林網。