ionic 2+ 手勢解鎖界面
一些對平安請求比較高的app少不了鎖屏頁面,而手勢解鎖關於用戶來講使用輕易,關於順序員來講小有應戰,怎樣有棄之不必的原理。
效果圖
效果圖處置懲罰短,輕易人人瀏覽
下面附上源碼,源碼的解釋加上語義化代碼願望協助小夥伴加快明白。有不足的處所迎接小夥伴批評指正。
import {Component, ElementRef, ViewChild, Renderer2} from '@angular/core';
import {IonicPage, NavController, NavParams} from 'ionic-angular';
import {Storage} from "@ionic/storage";
//點class
export class Point {
x: number;
y: number;
index?: number;
}
//儲存到當地數據庫的摒擋解鎖對象
export class GestureLockObj {
password: string;
chooseType: number;
step: number;
constructor() {
this.chooseType = 3;
this.step = 0;
}
}
//儲存到當地數據庫的摒擋毛病對象
export class GestureAttemptObj {
lockDate: number;
lastAttemptDate: number;
attemptsNu: number;
constructor() {
this.attemptsNu = 3;
}
}
@IonicPage()
@Component({
selector: 'page-gesture-lock',
templateUrl: 'gesture-lock.html',
})
export class GestureLockPage {
height = 320;
width = 320;
chooseType = 3;
devicePixelRatio; // 裝備密度
titleMes = "手勢暗碼解鎖";
unSelectedColor = '#87888a';
selectedColor = '#1783CE';
successColor = '#7bd56c';
errorColor = '#d54e20';
lockTimeUnit = 50; //嘗試失利后鎖定若干秒
gestureLockObj: GestureLockObj = new GestureLockObj(); //暗碼當地緩存
gestureAttemptObj: GestureAttemptObj = new GestureAttemptObj(); //嘗試日期和次數當地緩存
firstPassword: string;
private canTouch = false;
private radius: number; //小圓點半徑
private allPointArray: Point[] = [];
private unSelectedPointArray: Point[] = [];
private selectedPointArray: Point[] = [];
private ctx;
private lockTime = this.lockTimeUnit;
@ViewChild('canvas') canvas: ElementRef;
textColor = this.selectedColor;
constructor(public navCtrl: NavController,
public navParams: NavParams,
private render: Renderer2,
private storage: Storage) {
}
ngOnInit() {
this.devicePixelRatio = window.devicePixelRatio || 1;
this.radius = this.width * this.devicePixelRatio / (1 + 2 * this.chooseType) / 2; // 半徑盤算
this.canvas.nativeElement.height = this.height * this.devicePixelRatio;
this.canvas.nativeElement.width = this.width * this.devicePixelRatio;
this.ctx = this.canvas.nativeElement.getContext('2d');
this.initPointArray();
this.ctx.clearRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height);
this.drawCircles(this.allPointArray);
this.bindEvent();
this.storage.get('gestureLockObj').then(data => {
if (data) {
this.gestureLockObj = data;
}
});
this.storage.get('gestureAttemptObj').then(data => {
if (data) {
this.gestureAttemptObj = data;
if (this.gestureAttemptObj.attemptsNu === 0) {
const now =Date.now();
const last =this.gestureAttemptObj.lockDate;
const secend = (now - last) / 1000 - this.lockTimeUnit;
if (secend <= 0) {
this.setInteralFun( 1- secend );
} else {
this.gestureAttemptObj = new GestureAttemptObj();
this.storage.set("gestureAttemptObj", this.gestureAttemptObj);
}
}
}
});
if (this.gestureLockObj.step === 0) {
this.titleMes = "請繪製你的手勢暗碼";
}
}
//滑動完畢后處置懲罰暗碼
private dealPassword(selectedArray) {
if (this.gestureLockObj.step === 2) { /** 進入解鎖 **/
if (this.checkPassword(selectedArray, this.gestureLockObj.password)) { // 解鎖勝利
this.textColor = this.successColor;
this.titleMes = '解鎖勝利';
this.drawAll(this.successColor);
this.storage.remove('gestureAttemptObj')
} else { //解鎖失利
this.lockFaile();
}
} else if (this.gestureLockObj.step === 1) { // 設置暗碼確認暗碼
if (this.checkPassword(selectedArray, this.firstPassword)) { //設置暗碼勝利
this.gestureLockObj.step = 2;
this.gestureLockObj.password = this.firstPassword;
this.titleMes = '手勢暗碼設置勝利,再次繪製登錄';
this.storage.set('gestureLockObj', this.gestureLockObj);
this.drawAll(this.successColor);
} else { //設置暗碼失利
this.textColor = this.errorColor;
this.titleMes = '兩次不一致,從新輸入';
this.drawAll(this.errorColor);
this.gestureLockObj = new GestureLockObj();
}
} else if (this.gestureLockObj.step === 0) { //設置暗碼
this.gestureLockObj.step = 1;
this.firstPassword = this.parsePassword(selectedArray);
this.textColor = this.selectedColor;
this.titleMes = '再次輸入';
} else if (this.gestureLockObj.step === 3) {//重置暗碼輸入舊隱秘
if (this.checkPassword(selectedArray, this.gestureLockObj.password)) { // 舊暗碼勝利
this.gestureLockObj.step = 0;
this.textColor = this.successColor;
this.titleMes = '請輸入新手勢暗碼';
this.drawAll(this.successColor);
} else { //舊暗碼失利
this.lockFaile();
}
}
}
//解鎖失利
lockFaile() {
this.drawAll(this.errorColor);
this.textColor = this.errorColor;
this.gestureAttemptObj.attemptsNu = this.gestureAttemptObj.attemptsNu - 1;
if (this.gestureAttemptObj.attemptsNu > 0) {
this.titleMes = `暗碼毛病,您還能夠輸入${this.gestureAttemptObj.attemptsNu}次`;
} else {
this.gestureAttemptObj.lockDate = Date.now();
this.storage.set("gestureAttemptObj", this.gestureAttemptObj);
this.setInteralFun(this.lockTimeUnit);
}
}
setInteralFun(time) { //搜檢是不是凌駕設定時刻
this.lockTime = time;
const interval = setInterval(() => {
this.lockTime = this.lockTime - 1;
this.titleMes = `請在${this.lockTime}秒后嘗試`;
if (this.lockTime === 0) {
this.gestureAttemptObj = new GestureAttemptObj();
this.storage.set("gestureAttemptObj", this.gestureAttemptObj);
this.lockTime = this.lockTimeUnit;
this.titleMes = "手勢暗碼解鎖";
clearInterval(interval);
}
}, 1000);
}
//重置手勢隱秘
resetPasswordFun() {
this.titleMes = '請輸入舊手勢暗碼';
this.gestureLockObj.step = 3;
}
deletPasswordFun() {
this.storage.remove("gestureLockObj");
this.gestureLockObj = new GestureLockObj();
this.titleMes = '請繪製你的手勢暗碼';
this.reset();
}
//設置手勢暗碼矩陣
setChooseType(type) {
this.chooseType = type;
}
//初始化手勢點的坐標數組
private initPointArray() {
const n = this.chooseType;
const radius = this.radius;
this.selectedPointArray = [];
this.allPointArray = [];
this.unSelectedPointArray = [];
for (let i = 0; i < n; i++) {
for (let j = 0; j < n; j++) {
const obj = {
x: (j * 4 + 3) * radius,
y: (i * 4 + 3) * radius,
index: ((i * n + 1 + j) + 2) * 7 - 1
};
this.allPointArray.push(obj);
this.unSelectedPointArray.push(obj);
}
}
}
//滑動手勢的時刻更新畫布
private update(nowPoint: Point) {
this.drawAll(this.selectedColor, nowPoint);
this.dealPoint(this.unSelectedPointArray, nowPoint);
}
private checkPassword(pointArray, password): boolean {
return password === this.parsePassword(pointArray);
}
private parsePassword(pointArray): string {
return pointArray.map(data => {
return data.index;
}).join("");
}
//取得手指滑動點的位置
private getPosition(e): Point {
const rect = e.currentTarget.getBoundingClientRect();
return {
x: (e.touches[0].clientX - rect.left) * this.devicePixelRatio,
y: (e.touches[0].clientY - rect.top) * this.devicePixelRatio
};
}
//重置
reset() {
this.initPointArray();
this.ctx.clearRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height);
this.drawCircles(this.allPointArray);
}
//增加滑動監聽事宜
private bindEvent() {
this.render.listen(this.canvas.nativeElement, "touchstart", (e) => {
e.preventDefault();
if (this.selectedPointArray.length === 0 && this.gestureAttemptObj.attemptsNu !== 0) {
this.dealPoint(this.allPointArray, this.getPosition(e), true);
}
});
this.render.listen(this.canvas.nativeElement, "touchmove", (e) => {
if (this.canTouch) {
this.update(this.getPosition(e));
}
});
const self = this;
this.render.listen(this.canvas.nativeElement, "touchend", () => {
if (this.canTouch) {
this.canTouch = false;
this.dealPassword(this.selectedPointArray);
setTimeout(function () {
self.reset();
}, 1000);
}
});
}
//繪製滑動屏幕後的點
private drawAll(color, nowPoint = null) {
this.ctx.clearRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height);
this.drawCircles(this.allPointArray);
this.drawCircles(this.selectedPointArray, color);
this.drawPoints(this.selectedPointArray, color);
this.drawLine(this.selectedPointArray, color, nowPoint);
}
//滑動點的時刻處置懲罰是不是划中點
private dealPoint(pointArry: Point[], nowPoint: Point, canTouch = false) {
for (let i = 0; i < pointArry.length; i++) {
if (Math.abs(Number(nowPoint.x) - Number(pointArry[i].x)) < this.radius && Math.abs(Number(nowPoint.y) - Number(pointArry[i].y)) < this.radius) {
if (canTouch) {
this.canTouch = true;
}
this.drawPoint(pointArry[i]);
this.selectedPointArray.push(pointArry[i]);
this.unSelectedPointArray.splice(i, 1);
break;
}
}
}
private drawPoints(pointArray: Point[], style = this.selectedColor) {
for (const value of pointArray) {
this.drawPoint(value, style);
}
}
private drawCircles(pointArray: Point[], style = this.unSelectedColor) {
for (const value of pointArray) {
this.drawCircle(value, style);
}
}
//畫圈
private drawCircle(point: Point, style = this.unSelectedColor) {
this.ctx.strokeStyle = style;
this.ctx.lineWidth = 2;
this.ctx.beginPath();
this.ctx.arc(point.x, point.y, this.radius, 0, Math.PI * 2, true);
this.ctx.closePath();
this.ctx.stroke();
}
//畫點
private drawPoint(point: Point, style = this.selectedColor) {
this.ctx.fillStyle = style;
this.ctx.beginPath();
this.ctx.arc(point.x, point.y, this.radius / 2.5, 0, Math.PI * 2, true);
this.ctx.closePath();
this.ctx.fill();
}
//劃線
private drawLine(pointArray: Point[], style, nowPoint: Point = null) {
this.ctx.beginPath();
this.ctx.strokeStyle = style;
this.ctx.lineWidth = 3;
this.ctx.moveTo(pointArray[0].x, pointArray[0].y);
for (let i = 1; i < pointArray.length; i++) {
this.ctx.lineTo(pointArray[i].x, pointArray[i].y);
}
if (nowPoint) {
this.ctx.lineTo(nowPoint.x, nowPoint.y);
}
this.ctx.stroke();
this.ctx.closePath();
}
}
注重 注重
鎖屏界面的完成並不龐雜,龐雜是的是在順序中怎樣準確的挪用,迎接小夥伴交換。
源碼地點,html+sass+ts
https://github.com/sure2darli…
咯咯噠
覺得這個插件對你有協助請點個贊贊贊贊吧!!
咯咯噠