挑战新形式,小程序之手势解锁登录功能教程【附完整代码及思路】

简介: 挑战新形式,小程序之手势解锁登录功能教程【附完整代码及思路】

一、应用介绍

十五年前,乔布斯老爷子发布了跨时代的作品:iphone 4。当他演示用手滑动给手机解锁的操作时,整个世界都沸腾了!

的确,实时说明,在滑动解锁手机出来之后,全世界都陷入于对该形式的狂热追求中。

image.gif

那么,现在小程序成为了新的时代宠儿。各家的小程序平台百花齐放,一个个厂商的小程序平台就好比一个个的新的操作系统。


续iphone滑动解锁之后,由安卓手机引领的九宫格密码锁也成为了时代的潮流。


那么这次我们来尝试一下,如何在小程序的系统里面,摆脱普通的密码登录授权,也加入并体验到九宫格密码解锁的功能特效!


二、效果展示

c5863da51f6b497b80ee623f689d87a5.gif


三、技术与难点

3.1 基本开发环境

  • 开发框架:uniapp
  • 代码语法:vue 2htmlcss3


3.2 开发要点

3.2.1 引入九宫格插件

为了更高效的开发出手势登录的效果,我们这里直接引入了第三方的九宫格插件:mpvueGestureLock


3.2.2 自适应手机屏幕比

当用户绘制手势秘密的时候,我们需要保证九宫格每个格点的位置和形状不会有大的畸变,这会严重影响用户的手势输入体验,所以我们在加载界面功能之前需要使得九宫格动态适应屏幕的屏占比。

/* 测量屏幕高度,固定手势锁位置 */
      getScreenHeight() {
        //测量屏幕宽度(得到的是px单位)
        const deviceInfo = uni.getSystemInfoSync();
        console.log("设备信息:" + JSON.stringify(deviceInfo));
        this.screenWidth = deviceInfo.screenWidth;
        console.log("屏幕宽高:(" + this.screenWidth + ";" + this.screenHeight + ")");
      },


3.2.3 手势注册逻辑

  • 手势的注册逻辑一般有两步,第一步用于输入,第二步用于与之前的输入进行校验
/* 注册、修改-第一次绘制手势 */
      firstDraw() {
        if (this.lockType == 1) {
          console.log("注册-绘制第一次");
        } else if (this.lockType == 3) {
          console.log("修改密码-重新设置第一次");
        }
        if (this.password.length < 4) {
          this.text = '至少连接4个点,请重新绘制';
          this.password = [];
        } else {
          this.text = '再次绘制图案进行确认';
          this.showLockConfirm = true;
        }
      },
      /* 注册、修改-第二次绘制手势 */
      secondDraw(pwdAgain) {
        if (this.lockType == 1) {
          console.log("注册-绘制第二次");
        } else if (this.lockType == 3) {
          console.log("修改密码-重新设置第二次");
        }
        if (this.password.join('') === pwdAgain.join('')) {
          this.text = '手势设定完成,点击确认完成设定';
          this.confirmEnable = true;
          console.log("密码:" + JSON.stringify(this.password));
          // this.password = []
        } else {
          this.text = '两次手势设定不一致'
          this.confirmEnable = false;
          this.password = []
        }
      },


3.2.4 手势登录逻辑

  • 绑定插件的@end=onEnd()事件,获取用户输入的密码,然后将输入的密码和服务端的密码进行比对,从而实现登录。
onEnd(data) {
        if (this.password.length) {
          /* ***********二次认证*********** */
          if (this.lockType == 1) {
            //1、登录
            console.log("认证失败次数1:" + this.errorTimes);
            this.overFailedTimes();
          } else if (this.lockType == 2) {
            //2、注册
            this.secondDraw(data);
          } else if (this.lockType == 3) {
            //3、修改-重新设置
            this.secondDraw(data);
          }
        } else {
          /* ************首次绘制************ */
          this.password = data;
          if (this.lockType == 1) {
            //1、登录
            console.log("认证失败次数1:" + this.errorTimes);
            if (!this.overFailedTimes()) {
              //解锁
              console.log("密码:" + this.password.join(''));
              if ((this.password.join('')) == this.serverPwd) {
                this.text = '解锁成功';
                this.needAuthenticate = false;
                setTimeout(function() {
                  uni.switchTab({
                    url: '../index/index'
                  });
                }, 500);
              } else {
                this.certificationFailed();
              }
              this.password = [];
            }
          } else if (this.lockType == 2) {
            //2、注册
            this.firstDraw();
          } else if (this.lockType == 3) {
            //3、修改密码
            if (this.needAuthenticate) {
              console.log("认证失败次数1:" + this.errorTimes);
              if (!this.overFailedTimes()) {
                //修改需要先认证
                console.log("修改-密码认证:" + this.password.join(''));
                if ((this.password.join('')) == this.serverPwd) {
                  uni.showToast({
                    title: "手势认证通过",
                    icon: "success",
                    duration: 500
                  });
                  this.text = '手势认证通过';
                  this.pwdError = false;
                  setTimeout(res => {
                    this.text = '绘制你的手势图案,至少连接4个点';
                    this.needAuthenticate = false;
                  }, 800);
                } else {
                  this.certificationFailed();
                }
                this.password = [];
              }
            } else {
              //修改密码-第一次绘制(认证通过)
              this.firstDraw();
            }
          }
        }
      },


四、完整源码

<template>
  <view style="height: 100vh;overflow: hidden;">
    <view class="lock-tips">
      <view class="big-tips" :style="[needAuthenticate&&!pwdError?{color:'#999999'}:needAuthenticate&&pwdError?{color:'#FF0000'}:{color:''}]">{{text}}</view>
      <view class="small-tips" v-if="lockType == 2 || (lockType == 3 && !needAuthenticate)">请牢记您的密码,忘记后将无法找回</view>
    </view>
    <view class="container-lock">
      <mpvue-gesture-lock ref="mpvueGestureLock" :containerWidth="screenWidth" :cycleRadius="30" @end="onEnd" :password="password"></mpvue-gesture-lock>
    </view>
    <view class="container-confirm" v-if="showLockConfirm">
      <view class="lock-reset" @click="resetLock">重绘</view>
      <view :class="[confirmEnable?'':'disable-confirm']" @click="lockConfirm">确认</view>
    </view>
    <view style="position: fixed;top: 20rpx;left: 35%;z-index: 999;">
      <soure :url="url"></soure>
    </view>
  </view>
</template>
<script>
  import mpvueGestureLock from '@/components/mpvueGestureLock/gestureLocker.vue';
  export default {
    components: {
      mpvueGestureLock
    },
    data() {
      return {
        url:'https://ext.dcloud.net.cn/plugin?id=1623',
        title: "手势图案",
        password: [],
        text: '绘制你的手势图案,至少连接4个点',
        screenWidth: '', //屏幕宽度
        screenHeight: '', //屏幕高度
        showLockConfirm: false, //是否显示确认
        confirmEnable: false, //是否确认可点击
        lockType: '', //手势锁认证类型(1-解锁、2、注册、3-修改)
        serverPwd: '', //用以验证的密码
        needAuthenticate: false, //是否需要认证(解锁、修改需要和设定好的密码做认证)
        pwdError: false, //手势认证是否通过
        errorTimes: 0, //认证失败次数(限制)
        maxErrorTimes: 5, //最多可以失败几次
        backNum: 0,
      }
    },
    onBackPress(e) {
      if (this.lockType == 1) {
        this.backNum++;
        setTimeout(() => {
          this.backNum = 0;
          console.log("重置:" + this.backNum);
        }, 1500);
        console.log("backNum:" + this.backNum);
        if (this.backNum == 1) {
          uni.showToast({
            title: "再按一次退出",
            icon: 'none'
          });
        } else if (this.backNum >= 2) {
          plus.runtime.quit();
        }
        return true; //返回true禁止默认返回
      } else {
        return false;
      }
    },
    onLoad(options) {
      this.getScreenHeight();
      this.lockType = options.type;
      this.serverPwd = options.pwd;
      console.log("type:" + this.lockType + ";pwd:" + this.serverPwd);
      if (this.lockType == 1) {
        this.text = "请绘制您的手势图案";
        this.needAuthenticate = true;
        console.log("need:" + this.needAuthenticate + ";error:" + this.pwdError);
      } else if (this.lockType == 2) {
        this.text = "绘制你的手势图案,至少连接4个点";
      } else if (this.lockType == 3) {
        this.text = "请绘制您的手势图案";
        this.needAuthenticate = true;
      }
    },
    methods: {
      /* 测量屏幕高度,固定手势锁位置 */
      getScreenHeight() {
        //测量屏幕宽度(得到的是px单位)
        const deviceInfo = uni.getSystemInfoSync();
        console.log("设备信息:" + JSON.stringify(deviceInfo));
        this.screenWidth = deviceInfo.screenWidth;
        console.log("屏幕宽高:(" + this.screenWidth + ";" + this.screenHeight + ")");
      },
      onEnd(data) {
        if (this.password.length) {
          /* ***********二次认证*********** */
          if (this.lockType == 1) {
            //1、登录
            console.log("认证失败次数1:" + this.errorTimes);
            this.overFailedTimes();
          } else if (this.lockType == 2) {
            //2、注册
            this.secondDraw(data);
          } else if (this.lockType == 3) {
            //3、修改-重新设置
            this.secondDraw(data);
          }
        } else {
          /* ************首次绘制************ */
          this.password = data;
          if (this.lockType == 1) {
            //1、登录
            console.log("认证失败次数1:" + this.errorTimes);
            if (!this.overFailedTimes()) {
              //解锁
              console.log("密码:" + this.password.join(''));
              if ((this.password.join('')) == this.serverPwd) {
                this.text = '解锁成功';
                this.needAuthenticate = false;
                setTimeout(function() {
                  uni.switchTab({
                    url: '../index/index'
                  });
                }, 500);
              } else {
                this.certificationFailed();
              }
              this.password = [];
            }
          } else if (this.lockType == 2) {
            //2、注册
            this.firstDraw();
          } else if (this.lockType == 3) {
            //3、修改密码
            if (this.needAuthenticate) {
              console.log("认证失败次数1:" + this.errorTimes);
              if (!this.overFailedTimes()) {
                //修改需要先认证
                console.log("修改-密码认证:" + this.password.join(''));
                if ((this.password.join('')) == this.serverPwd) {
                  uni.showToast({
                    title: "手势认证通过",
                    icon: "success",
                    duration: 500
                  });
                  this.text = '手势认证通过';
                  this.pwdError = false;
                  setTimeout(res => {
                    this.text = '绘制你的手势图案,至少连接4个点';
                    this.needAuthenticate = false;
                  }, 800);
                } else {
                  this.certificationFailed();
                }
                this.password = [];
              }
            } else {
              //修改密码-第一次绘制(认证通过)
              this.firstDraw();
            }
          }
        }
      },
      /* 注册、修改-第一次绘制手势 */
      firstDraw() {
        if (this.lockType == 1) {
          console.log("注册-绘制第一次");
        } else if (this.lockType == 3) {
          console.log("修改密码-重新设置第一次");
        }
        if (this.password.length < 4) {
          this.text = '至少连接4个点,请重新绘制';
          this.password = [];
        } else {
          this.text = '再次绘制图案进行确认';
          this.showLockConfirm = true;
        }
      },
      /* 注册、修改-第二次绘制手势 */
      secondDraw(pwdAgain) {
        if (this.lockType == 1) {
          console.log("注册-绘制第二次");
        } else if (this.lockType == 3) {
          console.log("修改密码-重新设置第二次");
        }
        if (this.password.join('') === pwdAgain.join('')) {
          this.text = '手势设定完成,点击确认完成设定';
          this.confirmEnable = true;
          console.log("密码:" + JSON.stringify(this.password));
          // this.password = []
        } else {
          this.text = '两次手势设定不一致'
          this.confirmEnable = false;
          this.password = []
        }
      },
      /* 检查认证错误次数 */
      overFailedTimes() {
        if (this.errorTimes > this.maxErrorTimes - 1) {
          uni.showModal({
            title: "警告",
            content: "今日认证失败超过限制次数,你的设备已被锁定!",
            showCancel: false,
            confirmText: "好哒"
          });
          this.password = [];
          return true;
        }
        return false;
      },
      /* 手势认证失败 */
      certificationFailed() {
        this.text = '手势认证未通过';
        this.pwdError = true;
        this.needAuthenticate = true;
        this.errorTimes++;
        console.log("errorTimes:" + this.errorTimes + ";maxError:" + this.maxErrorTimes + ";error:" + this.pwdError +
          ";need:" + this.needAuthenticate);
      },
      /* 确认手势密码 */
      lockConfirm() {
        if (this.confirmEnable == true) {
          console.log("设定密码:" + this.password.join(''));
          // uni.showToast({
          //  title: "手势密码设置成功" + this.password.join(''),
          //  icon: 'none',
          //  duration: 1000
          // });
          setTimeout(function() {
            uni.navigateBack({
              delta: 1
            });
          }, 500);
        } else {
        }
      },
      /* 重绘密码 */
      resetLock() {
        console.log("重绘");
        this.text = "绘制你的手势图案,至少连接4个点";
        this.password = [];
        //引入子组件文件,然后用ref给子组件一个id标识,然后通过this.$refs.组件名.组件方法;调用子组件方法
        this.$refs.mpvueGestureLock.refesh();
      }
    }
  }
</script>
<style lang="scss">
  page {
    background-color: #FFFFFF;
  }
  .lock-tips {
    height: 25%;
    margin: 0 !important;
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    justify-content: center;
  }
  .big-tips {
    font-size: 32rpx;
    color: #333333;
    text-align: center;
    font-weight: 500;
  }
  .small-tips {
    font-size: 24rpx;
    color: #999999;
    text-align: center;
    font-weight: 500;
    margin-top: 20rpx;
  }
  .container-lock {
    background-color: #FCFCFC;
  }
  .container-confirm {
    display: flex;
    width: 100%;
    height: 16%;
    position: absolute;
    bottom: 0;
    flex-direction: row;
    justify-content: space-around;
    align-items: center;
    text-align: center;
    view {
      color: #666666;
      font-size: 28rpx;
      font-weight: 500;
      text-align: center;
      flex: 1;
      width: 80%;
      margin: 0 5%;
      height: 100rpx;
      line-height: 100rpx;
      border: 1px solid #F0F0F0;
      border-radius: 50rpx;
    }
    .disable-confirm {
      color: #A0A0A0;
      background-color: #F5F5F5;
    }
  }
</style>


相关文章
|
3天前
|
小程序 安全 搜索推荐
陪玩小程序的搭建解析与功能需求
陪玩小程序是为玩家提供专业陪玩服务的应用,嵌入社交或游戏平台,具备智能匹配、实时聊天、预约服务等功能,支持便捷高效的游戏体验。源码交付时需提供详细文档、技术支持及定制开发服务,确保客户能顺利维护和升级。选择陪玩小程序时应关注功能需求、用户体验、安全性和成本效益,以确保最佳使用效果。
23 0
|
2月前
|
XML 小程序 前端开发
小程序制作教程
小程序制作教程
81 3
小程序制作教程
|
1月前
|
小程序 前端开发 算法
|
1月前
|
小程序 数据挖掘 UED
开发1个上门家政小程序APP系统,都有哪些功能?
在快节奏的现代生活中,家政服务已成为许多家庭的必需品。针对传统家政服务存在的问题,如服务质量不稳定、价格不透明等,我们历时两年开发了一套全新的上门家政系统。该系统通过完善信用体系、提供奖励机制、优化复购体验、多渠道推广和多样化盈利模式,解决了私单、复购、推广和盈利四大痛点,全面提升了服务质量和用户体验,旨在成为家政行业的领导者。
|
2月前
|
存储 自然语言处理 小程序
微信小程序多语言切换神器:简繁体切换功能完全指南
随着全球化的发展,支持多种语言的应用程序愈发重要。本文介绍了如何在微信小程序中实现简体与繁体字体之间的切换功能,以满足不同地区用户的需求。通过创建utils文件夹并编写相应的转换函数,开发者可以方便地实现语言切换,从而提升用户体验。文章中还附带了示例代码和效果图,帮助读者更好地理解和应用这一功能。
112 0
微信小程序多语言切换神器:简繁体切换功能完全指南
|
2月前
|
开发框架 小程序 JavaScript
小程序代码丢失!反编译找回
小程序源代码的容易获取问题确实存在一些潜在的安全隐患。然而,现在的小程序开发框架采用像 Babel 这样的打包工具,将 JavaScript 逻辑代码混合在一个文件中并进行转编译,使其变得难以理解。
55 0
小程序代码丢失!反编译找回
|
2月前
|
小程序 Linux Python
查找首字母与Python相关的的英文词汇小程序的续篇---进一步功能完善
查找首字母与Python相关的的英文词汇小程序的续篇---进一步功能完善
25 1
|
2月前
|
小程序 算法 前端开发
微信小程序---授权登录
微信小程序---授权登录
105 0
|
3月前
|
小程序 JavaScript Go
代码总有一个是你想要的分享63个微信小程序源
分享63个微信小程序源代码,包括电商系统、同城拼车、博客等多种应用,涵盖C#、Node.js、Golang等技术栈。每个项目附带源码和示例,适合初学者和开发者参考学习。提取码:8888,代码效果参考:http://www.603393.com/sitemap.xml。
79 2
|
4月前
|
Web App开发 缓存 小程序
【Azure API 管理】从微信小程序访问APIM出现200空响应的问题中发现CORS的属性[terminate-unmatched-request]功能
【Azure API 管理】从微信小程序访问APIM出现200空响应的问题中发现CORS的属性[terminate-unmatched-request]功能