• canvas实现手机的手势解锁(步骤详细)


    按照国际惯例,先放效果图

    1、js动态初始化Dom结构

    首先在index.html中添加基本样式

    body{background:pink;text-align: center;}

    加个移动端meta头

    <meta name="viewport" content="width=device-width,initial-scale=1.0,user-scalable=no">

    引入index.js脚本

    <script src="index.js"></script>

    index.js

    // 匿名函数自执行
    (function(){
        // canvasLock是全局对象
        window.canvasLock=function(obj){
            this.width=obj.width;
            this.height=obj.height;
        }
        //动态生成DOM
        canvasLock.prototype.initDom=function(){
            //创建一个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=function(){
            //动态生成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=function(x,y){
            this.ctx.strokeStyle="#abcdef";
            this.ctx.lineWidth=2;
            this.ctx.beginPath();
            this.ctx.arc(x,y,this.r,0,2*Math.PI,true);
            this.ctx.closePath();
            this.ctx.stroke();
        }
        
        //绘制出所有的圆
        canvasLock.prototype.createCircle=function(){
            var n=this.circleNum;//一行几个圆
            var count=0;
            this.r=this.canvas.width/(4*n+2);//公式计算出每个圆的半径
            this.lastPoint=[];//储存点击过的圆的信息
            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:(4*j+3)*r,
                        y:(4*i+3)*r,
                        index:count//给每个圆标记索引
                    };
                    this.arr.push(obj);
                    this.restPoint.push(obj);//初始化时为所有点
                }
            }
    
            //清屏
            this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height);
    
            //以给定坐标点为圆心画出所有圆
            for(var i=0;i<this.arr.length;i++){
                //循环调用画单个圆的方法
                this.drawCircle(this.arr[i].x,this.arr[i].y);
            }
    
        }

    初始化的时候记得调用

    canvasLock.prototype.init=function(){
            //动态生成DOM
            this.initDom();
    
            //创建画布
            this.canvas=document.getElementById("canvas");
            this.ctx=this.canvas.getContext("2d");
    
            //绘制出所有的圆
            this.createCircle();
        }

    别忘了在index.html中实例化时传入参数(一行定义几个圆)

    new canvasLock({circleNum:3}).init();

    效果图

    3、canvas事件操作——实现画圆和画线

    getPosition方法用来得到鼠标触摸点离canvas的距离(左边和上边)

    canvasLock.prototype.getPosition=function(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",function(e){
                var po=self.getPosition(e);//鼠标距离canvas的距离
    
                //判断是否在圆内
                for(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;
                    }
                }
            },false);

    判断是否在圆内的原理:

     圆心的x轴偏移和鼠标点的x轴偏移的距离的绝对值小于半径

    并且

     圆心的y轴偏移和鼠标点的y轴偏移的距离的绝对值小于半径

    则可以判断鼠标位于圆内

    给touchmove绑定事件,在触摸点移动时给点击过的圆画上实心圆,并画线

    //触摸点移动时的动画
        canvasLock.prototype.update=function(po){
            //清屏,canvas动画前必须清空原来的内容
            this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height);
    
            //以给定坐标点为圆心画出所有圆
            for(var i=0;i<this.arr.length;i++){
                this.drawCircle(this.arr[i].x,this.arr[i].y);
            }
    
            this.drawPoint();//点击过的圆画实心圆
            this.drawLine(po);//画线
    
        }
    
        //画实心圆
        canvasLock.prototype.drawPoint=function(){
            for(var i=0;i<this.lastPoint.length;i++){
                this.ctx.fillStyle="#abcdef";
                this.ctx.beginPath();
                this.ctx.arc(this.lastPoint[i].x,this.lastPoint[i].y,this.r/2,0,2*Math.PI,true);
                this.ctx.closePath();
                this.ctx.fill();
            }
        }
    
        //画线
        canvasLock.prototype.drawLine=function(po){
            this.ctx.beginPath();
            this.lineWidth=3;
            this.ctx.moveTo(this.lastPoint[0].x,this.lastPoint[0].y);//线条起点
            for(var i=1;i<this.lastPoint.length;i++){
                this.ctx.lineTo(this.lastPoint[i].x,this.lastPoint[i].y);
            }
            this.ctx.lineTo(po.x,po.y);//触摸点
            this.ctx.stroke();
            this.ctx.closePath();
        }

    效果图

    4、canvas手势链接操作实现

    在touchmove中补充当碰到下一个目标圆时的操作

    //碰到下一个圆时只需要push到lastPoint当中去
            for(var i=0;i<this.restPoint.length;i++){
                if((Math.abs(po.x-this.restPoint[i].x)<this.r) && (Math.abs(po.y-this.restPoint[i].y)<this.r)){
                    this.lastPoint.push(this.restPoint[i]);//将这个新点击到的点存入lastPoint
                    this.restPoint.splice(i,1);//从restPoint中剔除这个新点击到的点
                    break;
                }
            }

    效果图

    5、解锁成功与否的判断

    //设置密码
        canvasLock.prototype.storePass=function(){
            if(this.checkPass()){
                document.getElementById("title").innerHTML="解锁成功";
                this.drawStatusPoint("lightgreen");
            }else{
                document.getElementById("title").innerHTML="解锁失败";
                this.drawStatusPoint("orange");
            }
        }
    
        //判断输入的密码
        canvasLock.prototype.checkPass=function(){
            var p1="123",//成功的密码
                p2="";
            for(var i=0;i<this.lastPoint.length;i++){
                p2+=this.lastPoint[i].index;
            }
            return p1===p2;
        }
    
        //绘制判断结束后的状态
        canvasLock.prototype.drawStatusPoint=function(type){
            for(var i=0;i<this.lastPoint.length;i++){
                this.ctx.strokeStyle=type;
                this.ctx.beginPath();
                this.ctx.arc(this.lastPoint[i].x,this.lastPoint[i].y,this.r,0,2*Math.PI,true);
                this.ctx.closePath();
                this.ctx.stroke();
            }
        }
    
        //程序全部结束后重置
        canvasLock.prototype.reset=function(){
            this.createCircle();
        }

    大功告成!!下面晒出所有代码

    index.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>手势解锁</title>
        <!-- 移动端meta头 -->
        <meta name="viewport" content="width=device-width,initial-scale=1.0,user-scalable=no">
        <style>
               body{background:pink;text-align: center;}
        </style>
    </head>
    <body>
    
        <script src="index.js"></script>
        <script>
            // circleNum:3 表示一行3个圆
            new canvasLock({circleNum:3}).init();
        </script>
      
    </body>
    </html>

    index.js

    // 匿名函数自执行
    (function(){
        // canvasLock是全局对象
        window.canvasLock=function(obj){
            this.width=obj.width;
            this.height=obj.height;
            this.circleNum=obj.circleNum;
        }
        //动态生成DOM
        canvasLock.prototype.initDom=function(){
            //创建一个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;
    
        }
    
        //以给定坐标点为圆心画出单个圆
        canvasLock.prototype.drawCircle=function(x,y){
            this.ctx.strokeStyle="#abcdef";
            this.ctx.lineWidth=2;
            this.ctx.beginPath();
            this.ctx.arc(x,y,this.r,0,2*Math.PI,true);
            this.ctx.closePath();
            this.ctx.stroke();
        }
        
        //绘制出所有的圆
        canvasLock.prototype.createCircle=function(){
            var n=this.circleNum;//一行几个圆
            var count=0;
            this.r=this.canvas.width/(4*n+2);//公式计算出每个圆的半径
            this.lastPoint=[];//储存点击过的圆的信息
            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:(4*j+3)*r,
                        y:(4*i+3)*r,
                        index:count//给每个圆标记索引
                    };
                    this.arr.push(obj);
                    this.restPoint.push(obj);//初始化时为所有点
                }
            }
    
            //清屏
            this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height);
    
            //以给定坐标点为圆心画出所有圆
            for(var i=0;i<this.arr.length;i++){
                //循环调用画单个圆的方法
                this.drawCircle(this.arr[i].x,this.arr[i].y);
            }
    
        }
    
        //添加事件
        canvasLock.prototype.bindEvent=function(){
            var self=this;
    
            this.canvas.addEventListener("touchstart",function(e){
                var po=self.getPosition(e);//鼠标距离canvas的距离
    
                //判断是否在圆内
                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.lastPoint.push(self.arr[i]);//点击过的点
                        self.restPoint.splice(i,1);//剩下的点剔除这个被点击的点
                        break;
                    }
                }
            },false);
    
            this.canvas.addEventListener("touchmove",function(e){
                if(self.touchFlag){
                    //触摸点移动时的动画
                    self.update(self.getPosition(e));
                }
            },false);
    
            //触摸离开时
            this.canvas.addEventListener("touchend",function(e){
                if(self.touchFlag){
                    self.storePass(self.lastPoint);
                    setTimeout(function(){
                        self.reset();
                    },300);
                }
            },false);
    
        }
    
        //设置密码
        canvasLock.prototype.storePass=function(){
            if(this.checkPass()){
                document.getElementById("title").innerHTML="解锁成功";
                this.drawStatusPoint("lightgreen");
            }else{
                document.getElementById("title").innerHTML="解锁失败";
                this.drawStatusPoint("orange");
            }
        }
    
        //判断输入的密码
        canvasLock.prototype.checkPass=function(){
            var p1="123",//成功的密码
                p2="";
            for(var i=0;i<this.lastPoint.length;i++){
                p2+=this.lastPoint[i].index;
            }
            return p1===p2;
        }
    
        //绘制判断结束后的状态
        canvasLock.prototype.drawStatusPoint=function(type){
            for(var i=0;i<this.lastPoint.length;i++){
                this.ctx.strokeStyle=type;
                this.ctx.beginPath();
                this.ctx.arc(this.lastPoint[i].x,this.lastPoint[i].y,this.r,0,2*Math.PI,true);
                this.ctx.closePath();
                this.ctx.stroke();
            }
        }
    
        //程序全部结束后重置
        canvasLock.prototype.reset=function(){
            this.createCircle();
        }
        
        //获取鼠标点击处离canvas的距离
        canvasLock.prototype.getPosition=function(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;
        }
    
        //触摸点移动时的动画
        canvasLock.prototype.update=function(po){
            //清屏,canvas动画前必须清空原来的内容
            this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height);
    
            //以给定坐标点为圆心画出所有圆
            for(var i=0;i<this.arr.length;i++){
                this.drawCircle(this.arr[i].x,this.arr[i].y);
            }
    
            // 鼠标每移动一下都会重绘canvas,update操作相当于每一个move事件都会触发
            this.drawPoint();//点击过的圆画实心圆
            this.drawLine(po);//画线
    
            //碰到下一个圆时只需要push到lastPoint当中去
            for(var i=0;i<this.restPoint.length;i++){
                if((Math.abs(po.x-this.restPoint[i].x)<this.r) && (Math.abs(po.y-this.restPoint[i].y)<this.r)){
                    this.lastPoint.push(this.restPoint[i]);//将这个新点击到的点存入lastPoint
                    this.restPoint.splice(i,1);//从restPoint中剔除这个新点击到的点
                    break;
                }
            }
        }
    
        //画实心圆
        canvasLock.prototype.drawPoint=function(){
            for(var i=0;i<this.lastPoint.length;i++){
                this.ctx.fillStyle="#abcdef";
                this.ctx.beginPath();
                this.ctx.arc(this.lastPoint[i].x,this.lastPoint[i].y,this.r/2,0,2*Math.PI,true);
                this.ctx.closePath();
                this.ctx.fill();
            }
        }
    
        //画线
        canvasLock.prototype.drawLine=function(po){
            this.ctx.beginPath();
            this.lineWidth=3;
            this.ctx.moveTo(this.lastPoint[0].x,this.lastPoint[0].y);//线条起点
            for(var i=1;i<this.lastPoint.length;i++){
                this.ctx.lineTo(this.lastPoint[i].x,this.lastPoint[i].y);
            }
            this.ctx.lineTo(po.x,po.y);//触摸点
            this.ctx.stroke();
            this.ctx.closePath();
        }
    
        //init代表初始化,程序的入口
        canvasLock.prototype.init=function(){
            //动态生成DOM
            this.initDom();
    
            //创建画布
            this.canvas=document.getElementById("canvas");
            this.ctx=this.canvas.getContext("2d");
    
            //默认不允许拖拽
            this.touchFlag=false;
    
            //绘制出所有的圆
            this.createCircle();
    
            //添加事件
            this.bindEvent();
        }
    })();
  • 相关阅读:
    深入浅出TCP之listen
    indexing and hashing
    c++四种强制类型转化
    c++实现web服务框架
    MVC、MVP、MVVM概念解析
    常用设计模式
    [React]虚拟DOM
    防抖和节流
    函数柯里化
    apply, bind, call方法剖析
  • 原文地址:https://www.cnblogs.com/chenyingying0/p/12466325.html
Copyright © 2020-2023  润新知