在早期我們使用智能手機(jī)的時(shí)候還是使用密碼使用在之后就是使用手勢(shì)解鎖到現(xiàn)在就是使用人臉解鎖,那么今天我們就來說說有關(guān)于:“html5怎么實(shí)現(xiàn)手勢(shì)解鎖?canvas標(biāo)簽案例分享! ”這個(gè)問題。
前言
最近做的一個(gè)app項(xiàng)目使用的 apicloud 來實(shí)現(xiàn)跨平臺(tái)開發(fā),現(xiàn)在需要為這個(gè) app 添加手勢(shì)(九宮格)解鎖的功能,apicloud 已經(jīng)有一些第三方的原生實(shí)現(xiàn)的手勢(shì)解鎖插件,因?yàn)槭窃男阅芤脖容^好,調(diào)用也比較方便,但是都不能對(duì)它們的樣式做修改,所以就打算自己來實(shí)現(xiàn)這個(gè)功能。這篇文章將實(shí)現(xiàn)過程整理分享出來,希望有需要的可以了解。分享出來的代碼只實(shí)現(xiàn)了最基本的 設(shè)置密碼功能
、 解鎖功能
、 比較密碼
的功能等,一些高級(jí)功能例如:不能限制一個(gè)點(diǎn)最多經(jīng)過多少次、限制用戶設(shè)置密碼的長(zhǎng)度。
原生實(shí)現(xiàn)還是其它方式實(shí)現(xiàn)?
1、使用 android 和 ios 對(duì)應(yīng)的平臺(tái)通過原生代碼來寫手勢(shì)解鎖插件。體驗(yàn)好,但是開發(fā)周期長(zhǎng),需要處理各平臺(tái)的兼容性問題,并且需要學(xué)習(xí)apicloud平臺(tái)插件編寫方法。(放棄)
2、使用 html5 的 canvas 畫布來實(shí)現(xiàn)。開發(fā)周期短,不需要過多的處理兼容性問題,體驗(yàn)好。(選擇)
原理分析
手勢(shì)解鎖
通過手指將屏幕上的九個(gè)點(diǎn)依次連接起來形成一個(gè)圖案,所以叫圖案解鎖。如上圖每一個(gè)解鎖圓圈后面其實(shí)都是一個(gè)數(shù)字,每次比較的并不是是用戶畫出來的圖案,而是每次手指經(jīng)過圖案時(shí)串聯(lián)起來的圓圈下的數(shù)字組成的密碼字符串,本質(zhì)上我們比較的還是字符串的密碼,只不過站在用戶的角度看是繪制出來的圖案。圖案的記憶遠(yuǎn)比數(shù)字字符串記的牢固。
實(shí)現(xiàn)步驟
繪制密碼盤
密碼盤的繪制比較簡(jiǎn)單,唯一需要注意需要通過動(dòng)態(tài)計(jì)算使九個(gè)點(diǎn)圍成的正方式始終在屏幕的中間位置,在手機(jī)上還需要減去狀態(tài)欄的高度。
var width = $(document).width();
var height = $(document).height() - 40; //減去手機(jī)狀態(tài)欄的高度
//九宮格其實(shí)就是九個(gè)點(diǎn),9個(gè)點(diǎn)的坐標(biāo)對(duì)象
var lockCicle = {
x: 0, //x坐標(biāo)
y: 0, //y坐標(biāo)
color: "#999999",
state: "1" //狀態(tài)當(dāng)前點(diǎn)是否已經(jīng)被鏈接過
};
var offset = (width - height) / 2; //計(jì)算偏移量
var arr = []; //九個(gè)點(diǎn)的坐標(biāo)數(shù)組
//計(jì)算九個(gè)點(diǎn)坐標(biāo)的方法
for (var i = 1; i <= 3; i++) {
//每一行
for (var j = 1; j <= 3; j++) {
//每一行的每一個(gè)
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; //清除繪制狀態(tài)
drawPointer(i);
}
}
//繪制九宮格解鎖界面
function drawPointer(i) {
ctx.save();
var radius = 0;
if (hastouch) {
radius = width / 12;
} else {
radius = 24;
}
var _fillStyle = "#dd514c";
var _strokeStyle = "#dd514c";
//不同狀態(tài)顯示不同顏色
if (arr[i].state == 1) {
_strokeStyle = "#1bd6c5";
}
//繪制原點(diǎn)
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 = []; //連接線點(diǎn)的坐標(biāo)數(shù)組
var startX, startY; //線條起始點(diǎn)
var puts = []; //經(jīng)過的九個(gè)點(diǎn)的數(shù)組
var currentPointer; //當(dāng)前點(diǎn)是否已經(jīng)連接
var pwd = []; //密碼
var confirmPwd = []; //確認(rèn)密碼
var unlockFlag = false; //是否解鎖的標(biāo)志
/**
** 繪制鏈接線的方法,將坐標(biāo)數(shù)組中的點(diǎn)繪制在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); //繪制圓圈和原點(diǎn)
if (ctx.isPointInPath(x, y) && currentPointer != i) {
//判斷鼠標(biāo)點(diǎn)擊是否在圓中
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();
}
}
綁定事件
連線的過程就是將 3 個(gè) touch(移動(dòng)端) 事件組合起來獲取當(dāng)前位置的坐標(biāo)放入數(shù)組中,然后將這些坐標(biāo)渲染到界面上的過程。
- touchstart (mousedown) 當(dāng)手指(鼠標(biāo))按下時(shí)設(shè)置 isMouseDown=true,同時(shí)將該點(diǎn)的坐標(biāo)保存到線條數(shù)組中,并將數(shù)組中的點(diǎn)繪制出來。
- touchmove (mousemove) 當(dāng) isMouseDown=true 時(shí) 將手指(鼠標(biāo))移動(dòng)過程中所有的坐標(biāo)點(diǎn)都保存到蕭條數(shù)組中,并將數(shù)組中的點(diǎn)繪制出來。
- mouseup (mouseup) 當(dāng)手指(鼠標(biāo))松開后設(shè)置 isMouseDown=fasle.將數(shù)組中的所有點(diǎn)繪制出來,清空 pointerArr 數(shù)組,然后比較連接的點(diǎn)的數(shù)量如果小于 6(自己設(shè)置,一般密碼 6 位以上)給一個(gè)密碼長(zhǎng)度不夠的提示,清空 puts 數(shù)組,重新調(diào)用 init 方法初始化界面,如果大于等于 6 則密碼設(shè)置成功。
//兼容移動(dòng)觸摸的事件寫法
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);
});
//移動(dòng)時(shí)候,將經(jīng)過的坐標(biāo)點(diǎn)全部保存起來
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 {
//設(shè)置解鎖密碼
settingUnlockPwd();
}
} else {
if (puts.length >= 1) {
alert("你的圖案密碼太簡(jiǎn)單了~~~");
init();
}
}
puts = [];
});
實(shí)現(xiàn)解鎖邏輯
通過上面幾步的操作,九宮格解鎖每一次繪圖之后的數(shù)據(jù)和顯示效果都有了,現(xiàn)在只需要在關(guān)鍵地方添加相應(yīng)邏輯代碼就可以了,這里主要介紹它的實(shí)現(xiàn)邏輯就不對(duì)代碼做封裝了。
相關(guān)代碼
//設(shè)置解鎖密碼和解鎖測(cè)試
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("解鎖成功!頁(yè)面跳轉(zhuǎn)中......");
} else {
$("header").text("解鎖圖案不正確?。?!");
init();
}
}
$("footer").click(function() {
if ($(this).text() === "解鎖") {
unlockFlag = true;
init();
$("header").text("繪制解鎖圖案");
}
});
//比較兩個(gè)數(shù)組(Number)是否相等
function compareArr(arr1, arr2) {
return arr1.toString() === arr2.toString();
}
后記
通過本文中的講解和代碼分享相信大家對(duì)于:“html5怎么實(shí)現(xiàn)手勢(shì)解鎖?canvas標(biāo)簽案例分享! ”這個(gè)問題有所了解。當(dāng)然更多有關(guān)于html5這方面的其他相關(guān)的使用方法我們都也可以在W3Cschool中進(jìn)行學(xué)習(xí)和了解。