微信小程序实现图案绘制

微信小程序实现图案绘制

如图
在这里插入图片描述
直接贴代码

1,wxml

<view class='page'>
  <view class='hd'>
    <text>{{lockType==1?'图案设置' : '图案解锁'}}</text>
    <text class='c-link' wx:if="{{lockType==1}}" bindtap='reset'>重新设置</text>
    <text class='c-link' wx:if="{{lockType==2}}" bindtap='forget'>忘记密码</text>
  </view>
  <view class='bd'>
    <view class='title'>{{lockMsg}}</view>
    <canvas canvas-id="canvas" class="canvas" bindtouchend="onTouchEnd"
      bindtouchstart="onTouchStart" bindtouchmove="onTouchMove"></canvas>
  </view>
  <view class='ft' style="height:{{ftHeight}}px">
    <scroll-view scroll-y="true" style="height:100%;">
      <view class="box">
        <text>使用说明:</text>
        <text>1) 读取图案密码,密码为空,则进入图案设置模式,反之则进入解锁模式;</text>
        <text>2) 设置模式,点击'重新设置',取消第一次图案绘制,重新绘制;</text>
        <text>3) 解锁模式,点击'忘记密码',重置图案密码,重新设置;</text>
      </view>
    </scroll-view>
  </view>
</view>

2.js

const app = getApp()
const Locker = require('../../utils/locker.js');

Page({
  data: {
    lockMsg: '请绘制图案'
  },
  onLoad: function () {
    let pwd = wx.getStorageSync('locker_pwd') || '';
    let res = wx.getSystemInfoSync();
    let ftHeight = res.windowHeight - 410;
    let that = this;
    this.setData({ lockType: pwd == '' ? 1 : 2, ftHeight: ftHeight });

    //
    this.lock = new Locker(this, {
      id: 'canvas',
      lockType: this.data.lockType,
      onTouchEnd: (e) => {
        // console.log("onTouchEnd: ", e);
      },
      onSuccess: (e) => {
        // console.log("onSuccess: ", e);
        if (e.lockType == "1") {
          console.log("密码设置成功,密码为:", e.lockPwd);
          that.setData({ lockType: 2 });
          that.lock.changeLockType(2);
        } else {
          console.log("密码解锁成功!");
        }
      }
    });
  },
  reset(e) {
    this.lock.reset();
  },
  forget(e) {
    let that = this;
    wx.showModal({
      content: '确定要重置密码吗?',
      success: (res) => {
        if (res.confirm) {
          that.setData({ lockType: 1 });
          that.lock.changeLockType(1);
        }
      }
    })
  }
})

3,wxss

.page {
  background: #f4f4f4;
  font-size: 14px;
}

.hd {
  padding: 20px;
  height: 20px;
  line-height: 20px;
  position: relative;
  text-align: center;
  margin-bottom: 20px;
}

.c-link {
  color: #576b95;
  height: 20px;
  position: absolute;
  right: 20px;
  top: 20px;
}

.title {
  font-size: 17px;
  height: 30px;
  line-height: 30px;
  text-align: center
}

.canvas {
  width: 300px;
  height: 300px;
  margin: 0 auto;
  background-color: #f4f4f4;
}

.ft {
  background: #fff;
  height: 30px;
}

.box {
  padding: 15px;
  display: flex;
  flex-direction: column
}

4,untils/locker.js

/**
 * canvasId: 画布ID, 默认'canvas'
 * lockType: 1--setting, 2--unlock
 * width: 图案区域宽度,px
 * successColor: 图案设置或解锁成功时,划过的圆周线颜色
 * errorColor: 图案设置或解锁失败时,划过的圆周线颜色
 * cleColor: 9个圆点的圆周线颜色
 * cleCenterColor: 9个原点的中心小点颜色,及连接线颜色
 * storageKey: 保存密码的key
 * onTouchEnd(e): 触摸结束回调
 * onSuccess(e): 密码设置成功、解锁成功回调
 */
var Locker = class {
  constructor(page,opt){
    var obj = opt || {};
    
    this.page = page;
    this.width = obj.width || 300;
    this.height = this.width;
    this.canvasId = obj.id || 'canvas';
    this.successColor = obj.successColor || '#00c203';
    this.errorColor = obj.errorColor || '#e64340';
    this.cleColor = obj.cleColor || '#3296fa';
    this.cleCenterColor = obj.cleCenterColor || '#3296fa';
    this.lockType = obj.lockType || 1;  //1->setting, 2->unlock
    this.lockStep = 1;
    this.lockMsg = "请绘制图案";
    this.storageKey = obj.storageKey || 'locker_password';
    this.onTouchEnd = obj.onTouchEnd ? obj.onTouchEnd : function (e) {};
    this.onSuccess = obj.onSuccess ? obj.onSuccess: function(e) {};
 
    this.init();
  }
  init(){
    // 创建 canvas 绘图上下文(指定 canvasId)
    this.ctx = wx.createCanvasContext(this.canvasId,this);
    this.touchFlag = false;
    this.lastPoint = [];
    //
    this.createCircles();
    // canvas绑定事件
    this.bindEvent();
    //
    this.page.setData({ lockMsg: this.lockMsg });
    //
    if(this.lockType == 2) {  //读取保存的密码
      this.savedPwd = wx.getStorageSync(this.storageKey) || '';
    }
  }
  changeLockType(lockType=1) {
    this.lockType = lockType;
    if (this.lockType == 2) {  //读取保存的密码
      this.savedPwd = wx.getStorageSync(this.storageKey) || '';
    }
    this.reset();
  }
  // 画圆方法
  drawCircle(x, y, color){
    color = color || this.cleColor;
    // 设置边框颜色。
    this.ctx.setStrokeStyle(color);
    // 设置线条的宽度。
    this.ctx.setLineWidth(1);
    // 开始创建一个路径,需要调用fill或者stroke才会使用路径进行填充或描边。
    this.ctx.beginPath();
    this.ctx.arc(x, y, this.r, 0, Math.PI * 2, true);
    this.ctx.closePath();
    // 画出当前路径的边框。默认颜色色为黑色。
    this.ctx.stroke();
    // 将之前在绘图上下文中的描述(路径、变形、样式)画到 canvas 中。
    this.ctx.draw(true);
  }
 
  // 创建解锁点的坐标,根据canvas的大小(默认300px)来平均分配半径
  createCircles() {
    var n = 3, count = 0;
    // 计算圆半径
    this.r = this.width / ((2 * n + 1) * 2);
    this.arr = [];
    this.restPoint = [];
    var r = this.r;
    // 获取圆心坐标,以及当前圆所代表的数
    for (var i = 0; i < n; i++) {
      for (var j = 0; j < n; j++) {
        count++;
        var obj = {
          x: j * 4 * r + 3 * r,
          y: i * 4 * r + 3 * r,
          index: count
        };
        this.arr.push(obj);
        this.restPoint.push(obj);
      }
    }
    // 清空画布
    this.ctx.clearRect(0, 0, this.width, this.height);
    // 绘制所有的圆
    this.arr.forEach(current => { this.drawCircle(current.x, current.y);});
  }
 
  // 获取touch点相对于canvas的坐标
  getPosition(e) { 
    return {
      x: e.touches[0].x,
      y: e.touches[0].y
    };
  }
 
  // 绘制触摸点
  drawPoint(x, y, color) { 
    color = color || this.cleCenterColor;
    this.ctx.setFillStyle(color); // 注意用set方法
    this.ctx.beginPath();
    this.ctx.arc(x, y, this.r / 2, 0, Math.PI * 2, true);
    this.ctx.closePath();
    this.ctx.fill();
    this.ctx.draw(true);
  }
  drawStatusPoint(color) { // 初始化状态线条
    for (var i = 0; i < this.lastPoint.length; i++) {
      this.drawCircle(this.lastPoint[i].x, this.lastPoint[i].y, color);
    }
  }
  drawLine(po1, po2) {
    this.ctx.beginPath();
    this.ctx.lineWidth = 2;
    this.ctx.moveTo(po1.x, po1.y);
    this.ctx.lineTo(po2.x, po2.y);
    this.ctx.stroke();
    this.ctx.closePath();
    this.ctx.draw(true);  
  }
  // 如果po在剩余可用点的圆内,则画触摸点和轨迹线
  drawTrace(po) {
    for (var i = 0; i < this.restPoint.length; i++) {
      var pt = this.restPoint[i];
      if (Math.abs(po.x - pt.x) < this.r && Math.abs(po.y - pt.y) < this.r) {
        this.drawPoint(pt.x, pt.y);
        this.drawLine(this.lastPoint[this.lastPoint.length - 1], pt);
        this.lastPoint.push(pt);
        this.restPoint.splice(i, 1);
        break;
      }
    }
  }
  reset() {
    this.lockStep = 1, this.lastPoint = [], this.lockMsg = "请绘制图案";
    // 绘制圆
    this.createCircles();
    //
    this.page.setData({lockMsg: this.lockMsg});
  }
  getLockPwd() {
    var str = "";
    for(var i = 0; i<this.lastPoint.length; i++) {
      str += this.lastPoint[i].index + "";
    }
    return str;
  }
  bindEvent(){
    var self = this;
    this.page.onTouchStart = function(e){
      console.log("t: ", self.lastPoint);
      if(self.lastPoint.length > 0) return;   // 上次绘制没有清理干净
      var po = self.getPosition(e);
      for (var i = 0; i < self.arr.length; i++) {
        // 坐标是否在圆内?
        if (Math.abs(po.x - self.arr[i].x) < self.r && Math.abs(po.y - self.arr[i].y) < self.r) {
          self.touchFlag = true;
          self.drawPoint(self.arr[i].x, self.arr[i].y);
          self.lastPoint.push(self.arr[i]);
          self.restPoint.splice(i, 1);
          break;
        }
      }
    }
    this.page.onTouchMove = function(e){
      if (self.touchFlag) {
        self.drawTrace(self.getPosition(e));
      }
    }
    this.page.onTouchEnd = function(e){
      let success = false;
      if (self.touchFlag) {
        self.touchFlag = false;
        e.lockType = self.lockType;
        e.lockStep = self.lockStep;
        e.lockPwd = self.getLockPwd();
        self.onTouchEnd(e);
        if(self.lockType == "1") {  // setting pwd
          if(self.lockStep == 1) {
            if(e.lockPwd.length < 4) {
              self.lockMsg = "至少绘制4个及以上点";
            } else {
              self.lockMsg = "请再次绘制图案";
              self.lockPwd = e.lockPwd, self.lockStep++;
            }
          } else {
            if(e.lockPwd == self.lockPwd) {
              self.lockMsg = "图案绘制成功";
              self.drawStatusPoint(self.successColor);
              wx.setStorageSync(self.storageKey,e.lockPwd);
              success = true;
            } else {
              self.drawStatusPoint(self.errorColor);
              self.lockMsg = "两次不一致,请重新绘制";
            }
          }
        } else {  // unlock pwd
          if(e.lockPwd == self.savedPwd) {
            self.lockMsg = "图案解锁成功";
            self.drawStatusPoint(self.successColor);
            success = true;
          } else {
            self.drawStatusPoint(self.errorColor);
            self.lockMsg = "图案错误, 请重新绘制";
          }
        }
        self.page.setData({lockMsg: self.lockMsg});
        setTimeout(function () {
          self.lastPoint = [], self.createCircles();
          if(success) {
            self.onSuccess(e);
          }
        }, 1000);
      }
    }
  }
}
module.exports = Locker;

希望此文章能帮助到你

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Cheng Lucky

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值