解锁demo"/>
canvas实现一个解锁demo
问题1:
代码如下:
function Foo() {};
Foo.prototype.talk = function () {alert('hello~\n');
};var a = new Foo;
a.talk(); // 输出'hello~\n'
但是如果这样:
Foo.talk() // 报错:Object doesn't support property or method 'talk'
Foo.prototype.talk() // 没有问题
解答:
因为这里a是构造出来的对象,其[[proto]]属性指向Foo.prototype。但Foo不是,Foo是一个函数对象,它的[[proto]]指向的是Function.prototype,因此有的是Foo.call Foo.apply这些函数对象上的方法(来自Function.prototypr)而不会有你定义的那个talk
问题2:
对一个函数对象添加属性,用prototype何不用prototype有何不同
用prototype添加属性的时候,当你实例化以后调用实际上只是调用了原型对象,只调用了一次,不管你实例化几次最多只是内容变了,而在内存中只会出现一次原型对象的函数。
js完成dom结构
tips: setAttribute和.style方法
setAttribute是给html元素属性设置值的。html元素属性就是指的标签中的键值对的前者, 例如<div id="name" class="text"><div>中的id和class就是html元素属性。 .style.property="值"是用来设置css样式的。例如说 document.getElementById("#name").style.background="red"设置文本框的背景颜色为红色。 在标签中我们也会内嵌样式, 例如<div id="name" class="text" style="width:100px;"><div>,说明style也是html元素的一个属性。 在设置的时候可以使用document.getElementById("#name").setAttribute("style","color:red;font:9px;"); 实际上平时用setAtrribute()这个方法就用的不多,也不太支持这种设置样式属性的写法。 如果引用了jQuery的话,在设置CSS样式的时候还可以使用.css("border","1px solid red");这种方式。
H5lock.prototype.initDom = function(){var wrap = document.createElement('div');var str = '<h4 id="title" class="title">绘制解锁图案</h4>'+'<a id="updatePassword" style="position: absolute;right: 5px;top: 5px;color:#fff;font-size: 10px;display:none;">重置密码</a>';wrap.setAttribute('style','position: absolute;top:0;left:0;right:0;bottom:0;');var canvas = document.createElement('canvas');canvas.setAttribute('id','canvas');canvas.style.cssText = 'background-color: #305066;display: inline-block;margin-top: 15px;';wrap.innerHTML = str;wrap.appendChild(canvas);var width = this.width || 300;var height = this.height || 300;document.body.appendChild(wrap);// 高清屏锁放 canvas.style.width = width + "px";canvas.style.height = height + "px";canvas.height = height * this.devicePixelRatio; //修改canvas默认宽高 canvas.width = width * this.devicePixelRatio;}
画外层大圆的函数
关于大圆半径求法
如下图为一行三个圆的时候
this.r = this.ctx.canvas.width / (2 + 4 * n);// 公式计算把大圆的直径和大圆左侧距离看成一个整体 也就是 4个半径的长度 一行就是n个圆 4*n 加上最右剩余的距离两个半径 所以计算半径公式如上
实现存放大圆坐标
H5lock.prototype.createCircle = function() {// 创建解锁点的坐标,根据canvas的大小来平均分配半径 var n = this.chooseType;var count = 0;this.r = this.ctx.canvas.width / (2 + 4 * n);// 公式计算 //确定圆心 this.lastPoint = [];this.arr = [];//9个圆的中心点坐标 this.restPoint = [];//同样是9个圆 var r = this.r;for (var i = 0 ; i < n ; i++) {//3*3 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.ctx.canvas.width, this.ctx.canvas.height);//开始画 for (var i = 0 ; i < this.arr.length ; i++) {this.drawCle(this.arr[i].x, this.arr[i].y);}//return arr; }
绘制圆 drawCle
H5lock.prototype.drawCle = function(x, y) { // 初始化解锁密码面板 this.ctx.strokeStyle = '#CFE6FF';this.ctx.lineWidth = 2;this.ctx.beginPath();this.ctx.arc(x, y, this.r, 0, Math.PI * 2, true);this.ctx.closePath();this.ctx.stroke(); }
绑定触摸事件
触发触摸:判断触摸点是否在大圆内
判断方法 触发点到画布最左的距离 减去 大圆圆心到最左的距离 的绝对值小于半径 那么这个触发点一定在某个圆内 有九个大圆 所以得遍历比较每个圆 遇到满足条件的就终止循环
this.canvas.addEventListener("touchstart", function (e) {//判断触发点是不是再大圆内 e.preventDefault();// 某些android 的 touchmove不宜触发 所以增加此行代码 var po = self.getPosition(e);//判断触摸点位置 console.log(po);//判断条件 触发点与圆心的位置到最左端的绝对值是否小于r 是的话就在圆内 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]);//点在哪个圆内就往lastpoint中push哪个圆的坐标 self.restPoint.splice(i,1);break;//每次都要从第一个圆开始判断直到最后一个圆 如果中间判断出在某个圆内 就要终止循环 }}}, false);
获取触发点坐标的函数getPosition
//判断触摸点位置的函数 H5lock.prototype.getPosition = function(e) {// 获取touch点相对于canvas的坐标 var rect = e.currentTarget.getBoundingClientRect();//返回的是画布距离屏幕的上下左右距离 var po = {x: (e.touches[0].clientX - rect.left)*this.devicePixelRatio,//一个手指操作 所以 下标为0 注意这里是触发点到屏幕左的距离减去画布到屏幕左的距离 y: (e.touches[0].clientY - rect.top)*this.devicePixelRatio };return po; }
补充
getBoundingClientRect用于获取某个元素相对于视窗的位置集合。集合中有top, right, bottom, left等属性。
rectObject.top:元素上边到视窗上边的距离;
rectObject.right:元素右边到视窗左边的距离;
rectObject.bottom:元素下边到视窗上边的距离;
rectObject.left:元素左边到视窗左边的距离;
clientX 、 clientY
clientX 事件属性返回当事件被触发时鼠标指针向对于浏览器页面(或客户区)的水平坐标。
touches
这是应用于移动端触摸事件的,event.x是他在手机上点的X轴位置,event.touches,多点触碰时的位置数组,比如缩放手势必须要用两指的触摸点,就是一个数组
添加touchmove事件
this.canvas.addEventListener("touchmove", function (e) {//画圆和画线 if (self.touchFlag) {self.update(self.getPosition(e));} }, false);
update函数
H5lock.prototype.update = function(po) {// 核心变换方法在touchmove时候调用 this.ctx.clearRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height);//重画九个圆 for (var i = 0 ; i < this.arr.length ; i++) { // 每帧先把面板画出来 this.drawCle(this.arr[i].x, this.arr[i].y);}this.drawPoint(this.lastPoint);// 每帧画圆心 this.drawLine(po , this.lastPoint);// 每帧花轨迹 //1.检测手势移动的位置是否处于下一个圆内 //2.如果处于圆内则画圆 //3.已经划过实心圆的圆 无需重复检测 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.drawPoint(this.restPoint[i].x, this.restPoint[i].y);this.lastPoint.push(this.restPoint[i]);//上面是找到画过内圆的 大圆 然后从restPoint中删除掉这个 this.restPoint.splice(i, 1);break;}}}画内圆的函数 drawPoint画线段的函数 drawLineH5lock.prototype.drawPoint = function() { // 初始化圆心 for (var i = 0 ; i < this.lastPoint.length ; i++) {this.ctx.fillStyle = '#CFE6FF';this.ctx.beginPath();this.ctx.arc(this.lastPoint[i].x, this.lastPoint[i].y, this.r / 2, 0, Math.PI * 2, true);this.ctx.closePath();this.ctx.fill();} }
H5lock.prototype.drawLine = function(po, lastPoint) {// 解锁轨迹 this.ctx.beginPath();this.ctx.lineWidth = 3;this.ctx.moveTo(this.lastPoint[0].x, this.lastPoint[0].y);//起始点为圆心 console.log(this.lastPoint.length);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();}
继续添加touchend事件
this.canvas.addEventListener("touchend", function (e) {if (self.touchFlag) {self.touchFlag = false;self.storePass(self.lastPoint);setTimeout(function(){self.reset();//不管密码输入正确与否 过了一会要将页面重置为开始时的九个无状态的大圆}, 300); }}, false);
tips:
setTimeout(表达式,延时时间)在执行时,是在载入后延迟指定时间后,去执行一次表达式,记住,次数是一次 而setInterval(表达式,交互时间)则不一样,它从载入后,每隔指定的时间就执行一次表达式
存储密码函数 storePass
H5lock.prototype.storePass = function(psw) {// touchend结束之后对密码和状态的处理 if (this.pswObj.step == 1) {if (this.checkPass(this.pswObj.fpassword, psw)) {this.pswObj.step = 2;this.pswObj.spassword = psw;document.getElementById('title').innerHTML = '密码保存成功';this.drawStatusPoint('#2CFF26');window.localStorage.setItem('passwordxx', JSON.stringify(this.pswObj.spassword));window.localStorage.setItem('chooseType', this.chooseType);} else {document.getElementById('title').innerHTML = '两次不一致,重新输入';this.drawStatusPoint('red');delete this.pswObj.step;}} else if (this.pswObj.step == 2) {if (this.checkPass(this.pswObj.spassword, psw)) {document.getElementById('title').innerHTML = '解锁成功';this.drawStatusPoint('#2CFF26');} else {this.drawStatusPoint('red');document.getElementById('title').innerHTML = '解锁失败';}} else {//第一次输入的时候会直接执行这一段 this.pswObj.step = 1;this.pswObj.fpassword = psw;document.getElementById('title').innerHTML = '再次输入';}}
检测密码checkPass
/*检测解锁成功: 1.检测路径是否正确 2.正确就重置,圆圈变绿 3.不对也重置,圆圈变红 4.重置 */H5lock.prototype.checkPass = function(psw1, psw2) {// 检测密码 var p1 = '',//成功解锁的密码 p2 = '';for (var i = 0 ; i < psw1.length ; i++) {//index是九个点 分别为1 ~9 p1 += psw1[i].index + psw1[i].index;}for (var i = 0 ; i < psw2.length ; i++) {p2 += psw2[i].index + psw2[i].index;}return p1 === p2;//返回的是布尔值 } 注意psw都是数组drawStatusPoint是重绘大圆颜色的 正确是绿色 错误是红色H5lock.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, Math.PI * 2, true);this.ctx.closePath();this.ctx.stroke();} }
reset重置
H5lock.prototype.reset = function() {this.makeState();this.createCircle();//重新画出最开的九个圆 }
makeState
H5lock.prototype.makeState = function() {//决定右上角的重置密码这几个字是否显示 if (this.pswObj.step == 2) {//如果在设置密码的时候 第二次输入的密码与第一次不符 根本不会有等于2的情况 document.getElementById('updatePassword').style.display = 'block';//document.getElementById('chooseType').style.display = 'none'; document.getElementById('title').innerHTML = '请解锁';} else if (this.pswObj.step == 1) {//document.getElementById('chooseType').style.display = 'none'; document.getElementById('updatePassword').style.display = 'none';} else {document.getElementById('updatePassword').style.display = 'none'; //点击重置密码的时候触发这里//document.getElementById('chooseType').style.display = 'block'; } }
给重置密码这个div绑定一个点击事件
document.getElementById('updatePassword').addEventListener('click', function(){self.updatePassword();});
updatePassword
H5lock.prototype.updatePassword = function(){window.localStorage.removeItem('passwordxx');window.localStorage.removeItem('chooseType');this.pswObj = {};//step被删除 document.getElementById('title').innerHTML = '绘制解锁图案';this.reset(); }
初始化H5lock.prototype.init = function() {//1.确定半径 //2.确定每个圆的中心坐标 //3. 14个半径 this.initDom();this.pswObj = window.localStorage.getItem('passwordxx') ? {step: 2,spassword: JSON.parse(window.localStorage.getItem('passwordxx'))} : {};this.lastPoint = [];this.makeState();//决定重置密码这几个字是否显示 this.touchFlag = false;//初始化touchFlag this.canvas = document.getElementById('canvas');this.ctx = this.canvas.getContext('2d');this.createCircle();//大圆绘制 this.bindEvent();//绑定事件函数 }
更多推荐
canvas实现一个解锁demo
发布评论