摘要:手勢解鎖界面一些對安全要求比較高的少不了鎖屏頁面,而手勢解鎖對于用戶來說使用方便,對于程序員來說小有挑戰,怎么有棄之不用的道理。
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...
咯咯噠
感覺這個插件對你有幫助請點個贊贊贊贊吧?。?/strong>
咯咯噠
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/95050.html
摘要:有二維碼掃描功能,還做了類似消息可拖拽效果,上拉下拉刷新,輪播圖組件。特別適合用于基于模式的移動應用程序開發。簡介是一個用基于,和的,創建移動跨平臺移動應用程序的快速開發平臺。 這個項目做得比較早,當時是基于ionic1和angular1做的。做了四個tabs的app,首頁模仿攜程首頁,第二頁主要是phonegap調用手機核心功能,第三頁模仿微信和qq聊天頁,第四頁模仿一般手機的表單設...
摘要:畫線畫線需要借助來完成,也就是,當我們的時候,傳入開始時的相對坐標,作為線的一端,當我們的時候,獲得坐標,作為線的另一端,當我們的時候,開始畫線。有兩個函數,獲得當前的相對坐標獲得觸摸點的相對位置相對坐標畫線畫線然后就是監聽的和事件了。 最近參加 360 暑假的前端星計劃,有一個在線作業,截止日期是 3 月 30 號,讓手動實現一個 H5 手勢解鎖,具體的效果就像原生手機的九宮格解鎖那...
閱讀 1877·2021-11-12 10:36
閱讀 2309·2021-09-01 10:29
閱讀 2337·2019-08-30 15:56
閱讀 1015·2019-08-30 12:56
閱讀 2342·2019-08-26 13:58
閱讀 2264·2019-08-23 18:38
閱讀 1486·2019-08-23 18:32
閱讀 2103·2019-08-23 16:53