按照国际惯例,先放效果图
1、js动态初始化Dom结构
首先在index.html中添加基本样式
body{background:pink;text-align: center;}
加个移动端Meta头
引入index.js脚本
script src="index.js"></script>
index.js
// 匿名函数自执行 (function(){ canvasLock是全局对象 window.canvasLock=(obj){ this.width=obj.width; this.height=obj.height; } 动态生成DOM canvasLock.prototype.initDom=(){ 创建一个div var div=document.createElement("div"); var h4="<h4 id='title' class='title'>绘制解锁图案</h4>"; div.innerHTML=h4; div.setAttribute("style","position:absolute;top:0;left:0;right:0;bottom:0;"); 创建canvas var canvas=document.createElement("canvas"); canvas.setAttribute("id","canvas"csstext 的本质就是设置 HTML 元素的 style 属性值 canvas.style.csstext="background:pink;display:inine-block;margin-top:15px;"; div.appendChild(canvas); document.body.appendChild(div); 设置canvas默认宽高 var width=this.width||300; var height=this.height||300; canvas.style.width=width+"px"; canvas.style.height=height+"px"; canvas.width=width; canvas.height=height; } init代表初始化,程序的入口 canvasLock.prototype.init=动态生成DOM this.initDom(); 创建画布 this.canvas=document.getElementById("canvas"this.ctx=this.canvas.getContext("2d"); } })();
在index.html中创建实例并初始化
new canvasLock({}).init();
效果图
2、 画圆函数
需要补充一下画布宽度与圆的半径的关系
如果一行3个圆,则有4个间距,间距的宽度与圆的直径相同,相当于7个直径,即14个半径
如果一行4个圆,则有5个间距,间距的宽度与圆的直径相同,相当于9个直径,即18个半径
如果一行n个圆,则有n+1个间距,间距的宽度与圆的直径相同,相当于2n+1个直径,即4n+2个半径
补充两个方法:
以给定坐标点为圆心画出单个圆 canvasLock.prototype.drawCircle=(x,y){ this.ctx.strokeStyle="#abcdef"this.ctx.linewidth=2.ctx.beginPath(); this.ctx.arc(x,y,this.r,2*Math.PI,1)">true.ctx.closePath(); .ctx.stroke(); } 绘制出所有的圆 canvasLock.prototype.createCircle=var n=this.circleNum;一行几个圆 var count=0this.r=this.canvas.width/(4*n+2);//公式计算出每个圆的半径 this.lastPoint=[];储存点击过的圆的信息 this.arr=[];储存所有圆的信息 this.restPoint=[];储存未被点击的圆的信息 var r=.r; for(var i=0;i<n;i++){ var j=0;j<n;j++){ count++; var obj={ x:(4*j+3)*r,y:(4*i+3)*给每个圆标记索引 }; .arr.push(obj); this.restPoint.push(obj);初始化时为所有点 } } 清屏 this.ctx.clearRect(0,1)">this.canvas.width,1)">.canvas.height); 以给定坐标点为圆心画出所有圆 var i=0;i<this.arr.length;i++循环调用画单个圆的方法 this.drawCircle(this.arr[i].x,1)">.arr[i].y); } }
初始化的时候记得调用
canvasLock.prototype.init=绘制出所有的圆 .createCircle(); }
别忘了在index.html中实例化时传入参数(一行定义几个圆)
new canvasLock({circleNum:3}).init();
效果图
3、canvas事件操作——实现画圆和画线
getPosition方法用来得到鼠标触摸点离canvas的距离(左边和上边)
canvasLock.prototype.getPosition=(e){ var rect=e.currentTarget.getBoundingClientRect();获得canvas距离屏幕的上下左右距离 var po={ 鼠标与视口的左距离 - canvas距离视口的左距离 = 鼠标与canvas的左距离 x:(e.touches[0].clientX-rect.left),鼠标与视口的上距离 - canvas距离视口的上距离 = 鼠标距离canvas的上距离 y:(e.touches[0].clientY-rect.top) }; return po; }
给canvas添加 touchstart 事件,判断触摸点是否在圆内
触摸点在圆内则允许拖拽,并将该圆添加到 lastPoint 中,从 restPoint 中剔除
this.canvas.addEventListener("touchstart",1)">(e){ var po=self.getPosition(e);鼠标距离canvas的距离 判断是否在圆内 var i=0;i<self.arr.lenth;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.lastPoint.push(self.arr[i]);点击过的点 self.restPoint.splice(i,1);剩下的点剔除这个被点击的点 break; } } },1)">false);
判断是否在圆内的原理:
圆心的x轴偏移和鼠标点的x轴偏移的距离的绝对值小于半径
并且
圆心的y轴偏移和鼠标点的y轴偏移的距离的绝对值小于半径
则可以判断鼠标位于圆内
给touchmove绑定事件,在触摸点移动时给点击过的圆画上实心圆,并画线
触摸点移动时的动画 canvasLock.prototype.update=(po){ 清屏,canvas动画前必须清空原来的内容 .arr[i].y); } this.drawPoint();点击过的圆画实心圆 this.drawLine(po);画线 } 画实心圆 canvasLock.prototype.drawPoint=this.lastPoint.length;i++this.ctx.fillStyle="#abcdef"; .ctx.beginPath(); this.ctx.arc(this.lastPoint[i].x,1)">this.lastPoint[i].y,1)">this.r/2,true); .ctx.closePath(); .ctx.fill(); } } 画线 canvasLock.prototype.drawLine=this.linewidth=3this.ctx.moveto(this.lastPoint[0].x,1)">this.lastPoint[0].y);线条起点 var i=1;i<this.ctx.lineto(.lastPoint[i].y); } this.ctx.lineto(po.x,po.y);触摸点 .ctx.stroke(); .ctx.closePath(); }
效果图
4、canvas手势链接操作实现
在touchmove中补充当碰到下一个目标圆时的操作
碰到下一个圆时只需要push到lastPoint当中去 this.restPoint.length;i++if((Math.abs(po.x-this.restPoint[i].x)<this.r) && (Math.abs(po.y-this.restPoint[i].y)<.r)){ this.lastPoint.push(this.restPoint[i]);将这个新点击到的点存入lastPoint this.restPoint.splice(i,1)">从restPoint中剔除这个新点击到的点 ; } }
效果图
5、解锁成功与否的判断
设置密码 canvasLock.prototype.storePass=if(.checkPass()){ document.getElementById("title").innerHTML="解锁成功"this.drawStatusPoint("lightgreen"); }else{ document.getElementById("title").innerHTML="解锁失败"this.drawStatusPoint("orange"); } } 判断输入的密码 canvasLock.prototype.checkPass=var p1="123",1)">成功的密码 p2=""){ p2+=.lastPoint[i].index; } return p1===p2; } 绘制判断结束后的状态 canvasLock.prototype.drawStatusPoint=(type){ this.ctx.strokeStyle=type; ); .ctx.stroke(); } } 程序全部结束后重置 canvasLock.prototype.reset=.createCircle(); }
大功告成!!下面晒出所有代码
index.html
<!DOCTYPE html> html lang="en"head> charset="UTF-8"title>手势解锁</<!-- 移动端Meta头 --> style> body{background:pink;text-align center} body> > // circleNum:3 表示一行3个圆 new canvasLock({circleNum:3}).init(); > htmlobj.height; this.circleNum=obj.circleNum; } height; } .arr[i].y); } } 添加事件 canvasLock.prototype.bindEvent=var self=; 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=falsethis.canvas.addEventListener("touchmove",1)">if(self.touchFlag){ 触摸点移动时的动画 self.update(self.getPosition(e)); } },1)">触摸离开时 this.canvas.addEventListener("touchend",1)">(self.touchFlag){ self.storePass(self.lastPoint); setTimeout((){ self.reset(); },300); } },1)">); } .createCircle(); } 获取鼠标点击处离canvas的距离 canvasLock.prototype.getPosition= po; } 鼠标每移动一下都会重绘canvas,update操作相当于每一个move事件都会触发 画线 ; } } } .ctx.closePath(); } 默认不允许拖拽 this.touchFlag=.createCircle(); 添加事件 .bindEvent(); } })();