ionic 2+ 手勢解鎖界面

ionic 2+ 手勢解鎖界面

一些對平安請求比較高的app少不了鎖屏頁面,而手勢解鎖關於用戶來講使用輕易,關於順序員來講小有應戰,怎樣有棄之不必的原理。

效果圖
效果圖處置懲罰短,輕易人人瀏覽
《ionic 2+ 手勢解鎖界面》

下面附上源碼,源碼的解釋加上語義化代碼願望協助小夥伴加快明白。有不足的處所迎接小夥伴批評指正。

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…
咯咯噠
覺得這個插件對你有協助請點個贊贊贊贊吧!!
咯咯噠

    原文作者:jsure
    原文地址: https://segmentfault.com/a/1190000014889630
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞