>

根据canvas的大小来平均分配半径sbf282.com:,就是

- 编辑:澳门博发娱乐官网 -

根据canvas的大小来平均分配半径sbf282.com:,就是

居安思危及布局设置

自笔者这里用了多少个相比较平常的做法:

(function(w){ var handLock = function(option){} handLock.prototype = { init : function(){}, ... } w.handLock = handLock; })(window) // 使用 new handLock({ el: document.getElementById('id'), ... }).init();

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
(function(w){
  var handLock = function(option){}
 
  handLock.prototype = {
    init : function(){},
    ...
  }
 
  w.handLock = handLock;
})(window)
 
// 使用
new handLock({
  el: document.getElementById('id'),
  ...
}).init();

常规办法,相比易懂和操作,缺陷就是,能够被随意的退换。

传扬的参数中要含有二个 dom 对象,会在那么些 dom 对象內创设贰个canvas。当然还应该有局地别的的 dom 参数,举例 message,info 等。

至于 css 的话,懒得去新建文件了,就直接內联了。

HTML5落到实处荧屏手势解锁

2015/07/18 · HTML5 · 1 评论 · 手势解锁

最先的作品出处: AlloyTeam   

效果与利益呈现

sbf282.com 1

达成原理 利用HTML5的canvas,将解锁的框框划出,利用touch事件解锁那么些层面,直接看代码。

JavaScript

function createCircle() {// 创立解锁点的坐标,依照canvas的尺寸来平均分配半径 var n = chooseType;// 画出n*n的矩阵 lastPoint = []; arr = []; restPoint = []; r = ctx.canvas.width / (2 + 4 * n);// 公式总结 半径和canvas的轻重缓急有关 for (var i = 0 ; i < n ; i++) { for (var j = 0 ; j < n ; j++) { arr.push({ x: j * 4 * r + 3 * r, y: i * 4 * r + 3 * r }); restPoint.push({ x: j * 4 * r + 3 * r, y: i * 4 * r + 3 * r }); } } //return arr; }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function createCircle() {// 创建解锁点的坐标,根据canvas的大小来平均分配半径
 
        var n = chooseType;// 画出n*n的矩阵
        lastPoint = [];
        arr = [];
        restPoint = [];
        r = ctx.canvas.width / (2 + 4 * n);// 公式计算 半径和canvas的大小有关
        for (var i = 0 ; i < n ; i++) {
            for (var j = 0 ; j < n ; j++) {
                arr.push({
                    x: j * 4 * r + 3 * r,
                    y: i * 4 * r + 3 * r
                });
                restPoint.push({
                    x: j * 4 * r + 3 * r,
                    y: i * 4 * r + 3 * r
                });
            }
        }
        //return arr;
    }

canvas里的圈子画好之后方可扩充事件绑定

JavaScript

function bindEvent() { can.addEventListener("touchstart", function (e) { var po = getPosition(e); console.log(po); for (var i = 0 ; i < arr.length ; i++) { if (Math.abs(po.x - arr[i].x) < r && Math.abs(po.y - arr[i].y) < r) { // 用来判别最早点是或不是在规模内部 touchFlag = true; drawPoint(arr[i].x,arr[i].y); lastPoint.push(arr[i]); restPoint.splice(i,1); break; } } }, false); can.addEventListener("touchmove", function (e) { if (touchFlag) { update(getPosition(e)); } }, false); can.addEventListener("touchend", function (e) { if (touchFlag) { touchFlag = false; storePass(lastPoint); setTimeout(function(){ init(); }, 300); } }, false); }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
function bindEvent() {
        can.addEventListener("touchstart", function (e) {
             var po = getPosition(e);
             console.log(po);
             for (var i = 0 ; i < arr.length ; i++) {
                if (Math.abs(po.x - arr[i].x) < r && Math.abs(po.y - arr[i].y) < r) { // 用来判断起始点是否在圈圈内部
 
                    touchFlag = true;
                    drawPoint(arr[i].x,arr[i].y);
                    lastPoint.push(arr[i]);
                    restPoint.splice(i,1);
                    break;
                }
             }
         }, false);
         can.addEventListener("touchmove", function (e) {
            if (touchFlag) {
                update(getPosition(e));
            }
         }, false);
         can.addEventListener("touchend", function (e) {
             if (touchFlag) {
                 touchFlag = false;
                 storePass(lastPoint);
                 setTimeout(function(){
 
                    init();
                }, 300);
             }
 
         }, false);
    }

紧接着到了最入眼的步骤绘制解锁路线逻辑,通过touchmove事件的趋之若鹜触发,调用canvas的moveTo方法和lineTo方法来画出折现,同期决断是不是达标大家所画的层面里面,当中lastPoint保存不易的范围路线,restPoint保存全体范畴去除准确路径之后剩余的。 Update方法:

JavaScript

function update(po) {// 核心转移格局在touchmove时候调用 ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); for (var i = 0 ; i < arr.length ; i++) { // 每帧先把面板画出来 drawCle(arr[i].x, arr[i].y); } drawPoint(lastPoint);// 每帧花轨迹 drawLine(po , lastPoint);// 每帧画圆心 for (var i = 0 ; i < restPoint.length ; i++) { if (Math.abs(po.x - restPoint[i].x) < r && Math.abs(po.y - restPoint[i].y) < r) { drawPoint(restPoint[i].x, restPoint[i].y); lastPoint.push(restPoint[i]); restPoint.splice(i, 1); break; } } }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function update(po) {// 核心变换方法在touchmove时候调用
        ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
 
        for (var i = 0 ; i < arr.length ; i++) { // 每帧先把面板画出来
            drawCle(arr[i].x, arr[i].y);
        }
 
        drawPoint(lastPoint);// 每帧花轨迹
        drawLine(po , lastPoint);// 每帧画圆心
 
        for (var i = 0 ; i < restPoint.length ; i++) {
            if (Math.abs(po.x - restPoint[i].x) < r && Math.abs(po.y - restPoint[i].y) < r) {
                drawPoint(restPoint[i].x, restPoint[i].y);
                lastPoint.push(restPoint[i]);
                restPoint.splice(i, 1);
                break;
            }
        }
 
    }

最后正是截至职业,把渠道里面包车型客车lastPoint保存的数组产生密码存在localstorage里面,之后就用来拍卖解锁验证逻辑了

JavaScript

function storePass(psw) {// touchend结束之后对密码和情景的管理 if (pswObj.step == 1) { if (checkPass(pswObj.fpassword, psw)) { pswObj.step = 2; pswObj.spassword = psw; document.getElementById('title').innerHTML = '密码保存成功'; drawStatusPoint('#2CFF26'); window.localStorage.setItem('passwordx', JSON.stringify(pswObj.spassword)); window.localStorage.setItem('chooseType', chooseType); } else { document.getElementById('title').innerHTML = '四回不平等,重新输入'; drawStatusPoint('red'); delete pswObj.step; } } else if (pswObj.step == 2) { if (checkPass(pswObj.spassword, psw)) { document.getElementById('title').innerHTML = '解锁成功'; drawStatusPoint('#2CFF26'); } else { drawStatusPoint('red'); document.getElementById('title').innerHTML = '解锁退步'; } } else { pswObj.step = 1; pswObj.fpassword = psw; document.getElementById('title').innerHTML = '再一次输入'; } }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
function storePass(psw) {// touchend结束之后对密码和状态的处理
        if (pswObj.step == 1) {
            if (checkPass(pswObj.fpassword, psw)) {
                pswObj.step = 2;
                pswObj.spassword = psw;
                document.getElementById('title').innerHTML = '密码保存成功';
                drawStatusPoint('#2CFF26');
                window.localStorage.setItem('passwordx', JSON.stringify(pswObj.spassword));
                window.localStorage.setItem('chooseType', chooseType);
            } else {
                document.getElementById('title').innerHTML = '两次不一致,重新输入';
                drawStatusPoint('red');
                delete pswObj.step;
            }
        } else if (pswObj.step == 2) {
            if (checkPass(pswObj.spassword, psw)) {
                document.getElementById('title').innerHTML = '解锁成功';
                drawStatusPoint('#2CFF26');
            } else {
                drawStatusPoint('red');
                document.getElementById('title').innerHTML = '解锁失败';
            }
        } else {
            pswObj.step = 1;
            pswObj.fpassword = psw;
            document.getElementById('title').innerHTML = '再次输入';
        }
 
    }

解锁组件

将这一个HTML5解锁写成了一个组件,放在

二维码体验: sbf282.com 2

 

参谋资料:

1 赞 4 收藏 1 评论

sbf282.com 3

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Happy Valentine's Day everyone !</title>
</head>
<style>
body{
overflow: hidden;
margin: 0;
}
h1{
position: fixed;
top: 50%;
left: 0;
width: 100%;
text-align: center;
transform:translateY(-50%);
font-family: 'Love Ya Like A Sister', cursive;
font-size: 40px;
color: #c70012;
padding: 0 20px;
}
@media (min-width:1200px){
h1{
font-size: 60px;
}
}
</style>
<body>
<canvas></canvas>
<h1>canvas heart</h1>

下一场小编将对info实行遍历 成立图片对象. 完成图片预加载.

});

优化 canvas 部分

对此 touchmove 函数,原理都以一致的,手指一划,就推行了 n 数次,那么些标题背后在消除,先来看另三个标题。

touchmove 是三个一再函数,看见这里,如若您并不曾稳重看自己的代码,那你对本人动用的 canvas 画图格局大概不太领悟,上边那几个是 touchmove 函数干了何等事:

  1. 先推断,假设当前处于未入选八个密码状态,则继续监视当前的地点,直到选中第二个密码,进入第二步;
  2. 跻身 update 函数,update 函数根本干四件事,重绘圆(密码)、决断当前地点、重绘点、重绘线;

第二步是贰个很顾虑的动作,为啥历次都要重绘圆,点和线呢?

sbf282.com 4

上边这一个图可以很好的证实难点,因为在装置或证实密码的历程中,咱们需求用一条线来延续触点到当下的尾声四个密码,况兼当 touchmove 的时候,能看出它们在转移。那么些意义很棒,能够形容出 touchmove 的轨迹。

然而,这就应当要时时刷新 canvas,质量大大地减少,刷新的那然则整整 canvas。

因为 canvas 唯有二个,既要画背景圆(密码),又要画已选密码的点,和折线。这里面不菲手续,一如既往只需求三遍就好了,比方背景圆,只需在运营的时候画一次,已选密码,只要当 touchCircles 新澳成分的时候才会用二回,还不用重绘,只要画就足以了。折线分成两某些,一部分是已选密码之间的连线,还会有正是最后一个密码点到当下触点之间的连线。

举个例子有八个 canvas 就好了,二个积攒静态的,三个专程用来重绘

缘何不得以有吗!

小编的消除思路是,以往有多个canvas,多少个在底层,作为描绘静态的圆、点和折线,另八个在上层,一方面监听 touchmove 事件,另一方面不停地重绘最终三个密码点的圆心到最近触点之间的线。假设如此能够的话,touchmove 函数实行二遍的功用大大升高。

安排第3个 canvas:

var canvas2 = canvas.cloneNode(canvas, true); canvas2.style.position = 'absolute';//让上层 canvas 覆盖底层 canvas canvas2.style.top = '0'; canvas2.style.left = '0'; this.el.appendChild(canvas2); this.ctx2 = canvas2.getContext('2d');

1
2
3
4
5
6
var canvas2 = canvas.cloneNode(canvas, true);
canvas2.style.position = 'absolute';//让上层 canvas 覆盖底层 canvas
canvas2.style.top = '0';
canvas2.style.left = '0';
this.el.appendChild(canvas2);
this.ctx2 = canvas2.getContext('2d');

要调换对第三个 ctx2 实行 touch 监听,并设置贰个 this.reDraw 参数,表示有新的密码增加进去,须求对点和折线增多新内容, update 函数要改成这样:

update: function(p){ // 更新 touchmove this.judgePos(p); // 每一次都要认清 this.drawLine2TouchPos(p); // 新加函数,用于绘最终一个密码点点圆心到触点之间的线 if(this.reDraw){ // 有新的密码加步向 this.reDraw = false; this.drawPoints(); // 增加新点 this.drawLine();// 增添新线 } },

1
2
3
4
5
6
7
8
9
update: function(p){ // 更新 touchmove
  this.judgePos(p); // 每次都要判断
  this.drawLine2TouchPos(p); // 新加函数,用于绘最后一个密码点点圆心到触点之间的线
  if(this.reDraw){ // 有新的密码加进来
    this.reDraw = false;
    this.drawPoints(); // 添加新点
    this.drawLine();// 添加新线
  }
},

drawLine2TouchPos: function(p){ var len = this.touchCircles.length; if(len >= 1){ this.ctx2.clearRect(0, 0, this.width, this.width); // 先清空 this.ctx2.beginPath(); this.ctx2.lineWidth = 3; this.ctx2.moveTo(this.touchCircles[len - 1].x, this.touchCircles[len

  • 1].y); this.ctx2.lineTo(p.x, p.y); this.ctx2.stroke(); this.ctx2.closePath(); } },
1
2
3
4
5
6
7
8
9
10
11
12
drawLine2TouchPos: function(p){
  var len = this.touchCircles.length;
  if(len >= 1){
    this.ctx2.clearRect(0, 0, this.width, this.width); // 先清空
    this.ctx2.beginPath();
    this.ctx2.lineWidth = 3;
    this.ctx2.moveTo(this.touchCircles[len - 1].x, this.touchCircles[len - 1].y);
    this.ctx2.lineTo(p.x, p.y);
    this.ctx2.stroke();
    this.ctx2.closePath();
  }
},

对应的 drawPoints 和 drawLine 函数也要对应修改,由原理画全部的,到未来只必要画新加的。

功效怎么着:

sbf282.com 5

move 函数试行数次,而其余函数独有当新密码加进来的时候才实施三回。

function onResize(){
ww = canvas.width = window.innerWidth;
wh = canvas.height = window.innerHeight;
}
ctx.strokeStyle = "red";
ctx.shadowBlur = 25;
ctx.shadowColor = "hsla(0, 100%, 60%,0.5)";
var precision = 100;
var hearts = [];
var mouseMoved = false;
function onMove(e){
mouseMoved = true;
if(e.type === "touchmove"){
hearts.push(new Heart(e.touches[0].clientX, e.touches[0].clientY));
hearts.push(new Heart(e.touches[0].clientX, e.touches[0].clientY));
}
else{
hearts.push(new Heart(e.clientX, e.clientY));
hearts.push(new Heart(e.clientX, e.clientY));
}
}
var Heart = function(x,y){
this.x = x || Math.random()*ww;
this.y = y || Math.random()*wh;
this.size = Math.random()*2 + 1;
this.shadowBlur = Math.random() * 10;
this.speedX = (Math.random()+0.2-0.6) * 8;
this.speedY = (Math.random()+0.2-0.6) * 8;
this.speedSize = Math.random()*0.05 + 0.01;
this.opacity = 1;
this.vertices = [];
for (var i = 0; i < precision; i++) {
var step = (i / precision - 0.5) * (Math.PI * 2);
var vector = {
x : (15 * Math.pow(Math.sin(step), 3)),
y : -(13 * Math.cos(step) - 5 * Math.cos(2 * step) - 2 * Math.cos(3 * step) - Math.cos(4 * step))
}
this.vertices.push(vector);
}
}
Heart.prototype.draw = function(){
this.size -= this.speedSize;
this.x += this.speedX;
this.y += this.speedY;
ctx.save();
ctx.translate(-1000,this.y);
ctx.scale(this.size, this.size);
ctx.beginPath();
for (var i = 0; i < precision; i++) {
var vector = this.vertices[i];
ctx.lineTo(vector.x, vector.y);
}
ctx.globalAlpha = this.size;
ctx.shadowBlur = Math.round((3 - this.size) * 10);
ctx.shadowColor = "hsla(0, 100%, 60%,0.5)";
ctx.shadowOffsetX = this.x + 1000;
ctx.globalCompositeOperation = "screen"
ctx.closePath();
ctx.fill()
ctx.restore();
};

接下去吗,正是js原生代码格局去书写了,并且带上闭包方式张开书写.

x: j *4* r +3* r,

一些 bugs

多少 bugs 在做的时候就开掘了,一些 bug 后来用手提式无线电话机测量检验的时候才意识,举例,小编用 chrome 的时候,未有察觉那么些bug,当自家用 android 手机 chrome 浏览器测验的时候,开采当本人 touchmove 向下的时候,会接触浏览器的下拉刷新,化解办法:加了一个 preventDefault,没悟出居然成功了。

this.canvas.addEventListener('touchmove', function(e){ e.preventDefault ? e.preventDefault() : null; var p = self.getTouchPos(e); if(self.touchFlag){ self.update(p); }else{ self.judgePos(p); } }, false)

1
2
3
4
5
6
7
8
9
this.canvas.addEventListener('touchmove', function(e){
  e.preventDefault ? e.preventDefault() : null;
  var p = self.getTouchPos(e);
  if(self.touchFlag){
    self.update(p);
  }else{
    self.judgePos(p);
  }
}, false)

var ww,wh;

//创制键值对图纸路线

var info = {

"background":"img/background.png", //背景图片

"plane": "img/plane.png", //飞机图片

};

//加载图片能源

loading(info,{

done:main

});

restPoint.push({

3. 画折线

所谓的画折线,便是,将曾经触动到的点连起来,可以把它作为是画折线。

首先,要用三个数组,三个数组用于已经 touch 过的点,另一个数组用于存储未 touch 的点,然后在 move 监听时候,对 touch 的对立地点打开判别,假若触到点,就把该点从未 touch 移到 touch 中,然后,画折线,思路也不会细小略。

JavaScript

drawLine: function(p){ // 画折线 this.ctx.beginPath(); this.ctx.lineWidth = 3; this.ctx.moveTo(this.touchCircles[0].x, this.touchCircles[0].y); for (var i = 1 ; i < this.touchCircles.length ; i++) { this.ctx.lineTo(this.touchCircles[i].x, this.touchCircles[i].y); } this.ctx.lineTo(p.x, p.y); this.ctx.stroke(); this.ctx.closePath(); },

1
2
3
4
5
6
7
8
9
10
11
drawLine: function(p){ // 画折线
  this.ctx.beginPath();
  this.ctx.lineWidth = 3;
  this.ctx.moveTo(this.touchCircles[0].x, this.touchCircles[0].y);
  for (var i = 1 ; i < this.touchCircles.length ; i++) {
    this.ctx.lineTo(this.touchCircles[i].x, this.touchCircles[i].y);
  }
  this.ctx.lineTo(p.x, p.y);
  this.ctx.stroke();
  this.ctx.closePath();
},

JavaScript

judgePos: function(p){ // 判断 触点 是否在 circle 內 for(var i = 0; i < this.restCircles.length; i++){ temp = this.restCircles[i]; if(Math.abs(p.x - temp.x) < r && Math.abs(p.y - temp.y) < r){ this.touchCircles.push(temp); this.restCircles.splice(i, 1); this.touchFlag = true; break; } } }

1
2
3
4
5
6
7
8
9
10
11
judgePos: function(p){ // 判断 触点 是否在 circle 內
  for(var i = 0; i < this.restCircles.length; i++){
    temp = this.restCircles[i];
    if(Math.abs(p.x - temp.x) < r && Math.abs(p.y - temp.y) < r){
      this.touchCircles.push(temp);
      this.restCircles.splice(i, 1);
      this.touchFlag = true;
      break;
    }
  }
}

<script>
var canvas = document.querySelector("canvas"),
ctx = canvas.getContext("2d");

*{

margin: 0;

padding: 0;

}

html,body{

width: 100%;

height: 100%;

}

#canvas{

box-shadow: 0 0 50px gray;

display: block;

margin: 0 auto;

}

落到实处原理利用HTML5的canvas,将解锁的局面划出,利用touch事件解锁这个层面,直接看代码。

4. 手动重新初始化密码

思路也很简短,就是增多点击事件,点击之后,更改 model 就能够,点击事件如下:

this.dom.setPass.add伊夫ntListener('click', function(e){ self.lsPass.model = 2; // 改动 model 为设置密码 self.updateMessage(); // 更新 message self.showInfo('请设置密码', 1000); }) this.dom.checkPass.add伊夫ntListener('click', function(e){ if(self.lsPass.pass){ self.lsPass.model = 1; self.updateMessage(); self.showInfo('请验证密码', 一千) }else{ self.showInfo('请先设置密码', 一千); self.updateMessage(); } })

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
this.dom.setPass.addEventListener('click', function(e){
  self.lsPass.model = 2; // 改变 model 为设置密码
  self.updateMessage(); // 更新 message
  self.showInfo('请设置密码', 1000);
})
this.dom.checkPass.addEventListener('click', function(e){
  if(self.lsPass.pass){
    self.lsPass.model = 1;
    self.updateMessage();
    self.showInfo('请验证密码', 1000)
  }else{
    self.showInfo('请先设置密码', 1000);
    self.updateMessage();
  }
})

ps:那其间还应该有多少个小的 bug,因为 model 唯有 3 个,所以设置的时候,当点击重新初始化密码的时候,未有安装密码成功,又切成验证密码状态,此时无法升迁沿用旧密码,原因是 model 只有八个

hearts.push(new Heart())
ctx.clearRect(0,0,ww,wh);
for (var i = 0; i < hearts.length; i++) {
hearts[i].draw();
if(hearts[i].size <= 0){
hearts.splice(i,1);
i--;
}
}
}
onResize();
window.addEventListener("mousemove", onMove);
window.addEventListener("touchmove", onMove);
window.addEventListener("resize", onResize);
requestAnimationFrame(render);
</script>
</body>
</html>

在这里自个儿又声称了加载后的图样键值对和加载成功后的图样个数.

}

canvas

function render(a){
requestAnimationFrame(render);

<canvas id = "canvas" width = "375" height = "627"></canvas>

if(Math.abs(po.x - restPoint[i].x) < r &&Math.abs(po.y - restPoint[i].y) < r) {

1. 就学 canvas 并消除画圆

MDN 上面有个大致的课程,差不离浏览了一下,认为还行。Canvas教程。

先创设三个 canvas,然后设置其大小,并通过 getContext 方法得到摄影的上下文:

var canvas = document.createElement('canvas'); canvas.width = canvas.height = width; this.el.appendChild(canvas); this.ctx = canvas.getContext('2d');

1
2
3
4
5
var canvas = document.createElement('canvas');
canvas.width = canvas.height = width;
this.el.appendChild(canvas);
 
this.ctx = canvas.getContext('2d');

然后呢,先画 n*n 个圆出来:

JavaScript

createCircles: function(){ var ctx = this.ctx, drawCircle = this.drawCircle, n = this.n; this.r = ctx.canvas.width / (2 + 4 * n) // 这里是仿效的,认为这种画圆的不二秘籍挺合理的,方方圆圆 r = this.r; this.circles = []; // 用来囤积圆心的任务 for(var i = 0; i < n; i++){ for(var j = 0; j < n; j++){ var p = { x: j * 4 * r + 3 * r, y: i * 4 * r + 3 * r, id: i * 3 + j } this.circles.push(p); } } ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); // 为了防卫重复画 this.circles.forEach(function(v){ drawCircle(ctx, v.x, v.y); // 画每一个圆 }) }, drawCircle: function(ctx, x, y){ // 画圆函数 ctx.strokeStyle = '#FFFFFF'; ctx.lineWidth = 2; ctx.beginPath(); ctx.arc(x, y, this.r, 0, Math.PI * 2, true); ctx.closePath(); ctx.stroke(); }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
createCircles: function(){
  var ctx = this.ctx,
    drawCircle = this.drawCircle,
    n = this.n;
  this.r = ctx.canvas.width / (2 + 4 * n) // 这里是参考的,感觉这种画圆的方式挺合理的,方方圆圆
  r = this.r;
  this.circles = []; // 用来存储圆心的位置
  for(var i = 0; i < n; i++){
    for(var j = 0; j < n; j++){
      var p = {
        x: j * 4 * r + 3 * r,
        y: i * 4 * r + 3 * r,
        id: i * 3 + j
      }
      this.circles.push(p);
    }
  }
  ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); // 为了防止重复画
  this.circles.forEach(function(v){
    drawCircle(ctx, v.x, v.y); // 画每个圆
  })
},
 
drawCircle: function(ctx, x, y){ // 画圆函数
  ctx.strokeStyle = '#FFFFFF';
  ctx.lineWidth = 2;
  ctx.beginPath();
  ctx.arc(x, y, this.r, 0, Math.PI * 2, true);
  ctx.closePath();
  ctx.stroke();
}

画圆函数,供给潜心:如何分明圆的半径和各样圆的圆心坐标(这么些本人是参照他事他说加以考察的),假使以圆心为主旨,各类圆上下左右各扩大一个半径的离开,同有时间为了防范四边太挤,四周在填充二个半径的相距。那么得到的半径即是 width / ( 4 * n + 2),对应也足以算出各种圆所在的圆心坐标,也可能有一套公式,GET

而在此间,小编注脚了多个函数loading 并用键值对的方法去传递图片路线和本身的主调用main函数, 他将作为本身的镜头中对飞机和背景的管理主函数. 请往下看 loading.js文件.  小编声Bellamy(Bellamy)个loading.js文件导入项目中.

can.addEventListener("touchmove",function(e){

1. 添加 message 和 单选框

为了尽量的使分界面轻便(越丑越好),直接在 body 后边增加了:

XHTML

<div id="select"> <div class="message">请输动手势密码</div> <div class="radio"> <label><input type="radio" name="pass">设置手势密码</label> <label><input type="radio" name="pass">验证手势密码</label> </div> </div>

1
2
3
4
5
6
7
<div id="select">
  <div class="message">请输入手势密码</div>
  <div class="radio">
    <label><input type="radio" name="pass">设置手势密码</label>
    <label><input type="radio" name="pass">验证手势密码</label>
  </div>
</div>

将拉长到 dom 已 option 的方式传给 handLock:

var el = document.getElementById('handlock'), info = el.getElementsByClassName('info')[0], select = document.getElementById('select'), message = select.getElementsByClassName('message')[0], radio = select.getElementsByClassName('radio')[0], setPass = radio.children[0].children[0], checkPass = radio.children[1].children[0]; new handLock({ el: el, info: info, message: message, setPass: setPass, checkPass: checkPass, n: 3 }).init();

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var el = document.getElementById('handlock'),
  info = el.getElementsByClassName('info')[0],
  select = document.getElementById('select'),
  message = select.getElementsByClassName('message')[0],
  radio = select.getElementsByClassName('radio')[0],
  setPass = radio.children[0].children[0],
  checkPass = radio.children[1].children[0];
new handLock({
  el: el,
  info: info,
  message: message,
  setPass: setPass,
  checkPass: checkPass,
  n: 3
}).init();

//保存已经加载成功的图纸 用键值对章程保存

var loadingimg = {};

// 图片已经加载成功的个数

var loadingimgnumber = 0;

document.getElementById('title').innerHTML ='两遍不雷同,重新输入';

5. 增加 touchend 颜色变化

兑现这些差不离就马到成功了,这一个效应最重要的是给客商一个唤起,若客商划出的密码契合规范,展现血红,若不适合标准或错误,显示宝蓝警戒。

因为从前已经安装了八个 succ 变量,专门用于重绘。

JavaScript

drawEndCircles: function(color){ // end 时重绘已经 touch 的圆 for(var i = 0; i < this.touchCircles.length; i++){ this.drawCircle(this.touchCircles[i].x, this.touchCircles[i].y, color); } }, // 调用 if(succ){ this.drawEndCircles('#2CFF26'); // 绿色 }else{ this.drawEndCircles('red'); // 红色 }

1
2
3
4
5
6
7
8
9
10
11
12
drawEndCircles: function(color){ // end 时重绘已经 touch 的圆
  for(var i = 0; i < this.touchCircles.length; i++){
    this.drawCircle(this.touchCircles[i].x, this.touchCircles[i].y, color);
  }
},
 
// 调用
if(succ){
  this.drawEndCircles('#2CFF26'); // 绿色
}else{
  this.drawEndCircles('red'); // 红色
}

那么,三个足以演示的版本就生成了,固然还留存有的 bug,随后会来缓慢解决。(实际情况分支 password)

     好啊,各位客官老爷,给我们一个代码运维的一个体裁吧.

break;

4. 标志已画

前方早就说了,大家把已经 touch 的点(圆)放到数组中,那年要求将这一个曾经 touch 的点给标志一下,在圆心处画多少个小实心圆:

JavaScript

drawPoints: function(){ for (var i = 0 ; i < this.touchCircles.length ; i++) { this.ctx.fillStyle = '#FFFFFF'; this.ctx.beginPath(); this.ctx.arc(this.touchCircles[i].x, this.touchCircles[i].y, this.r / 2, 0, Math.PI * 2, true); this.ctx.closePath(); this.ctx.fill(); } }

1
2
3
4
5
6
7
8
9
drawPoints: function(){
  for (var i = 0 ; i < this.touchCircles.length ; i++) {
    this.ctx.fillStyle = '#FFFFFF';
    this.ctx.beginPath();
    this.ctx.arc(this.touchCircles[i].x, this.touchCircles[i].y, this.r / 2, 0, Math.PI * 2, true);
    this.ctx.closePath();
    this.ctx.fill();
  }
}

还要增多三个 reset 函数,当 touchend 的时候调用,400ms 调用 reset 重新恢复设置canvas。

到今日结束,一个 H5 手势解锁的简易版已经主导完成。

function loading(info,callbock){

//图片总个数

var allimg = 0;

for(key in info){

    allimg++;

  }

}

if(Math.abs(po.x - arr[i].x) < r &&Math.abs(po.y - arr[i].y) < r) {// 用来判别初阶点是或不是在规模内部

password

为了要兑现记住和重新载入参数密码的职能,把 password 保存在 localStorage 中,但第一要加上要求的 html 和体制。

各位观者老爷我们好,招待收看三角裤总动员之程序猿的IT程序大讲堂,明日给我们分享多个经文游戏.大家都应该玩过微信中的手淫吧.今天就轻松的用canvas画布来制作叁个回顾的娱乐画面. 

drawPoint(restPoint[i].x, restPoint[i].y);

参考

H5lock
Canvas教程
js获取单选框里面包车型客车值
前端高品质滚动 scroll 及页面渲染优化

3 赞 5 收藏 评论

sbf282.com 6

sbf282.com 7

functionstorePass(psw){// touchend甘休之后对密码和情形的拍卖

用 canvas 完成 Web 手势解锁

2017/04/04 · HTML5 · Canvas

原来的文章出处: songjz   

前段时间在座 360 暑假的前端星陈设,有贰个在线作业,结束日期是 3 月 30 号,让手动完毕八个 H5 手势解锁,具体的成效就像是原菜鸟机的九宫格解锁那样。

sbf282.com 8

达成的终极效果就好像下边那张图这样:

sbf282.com 9

主干须求是这么的:将密码保存到 localStorage 里,最早的时候会从地点读取密码,若无就让客户设置密码,密码起码为陆位数,少于五个人要唤醒错误。须要对第贰遍输入的密码进行表达,四回一样工夫维系,然后是印证密码,能够对顾客输入的密码进行认证。

今天因为太晚了,实在扛不住了,代码有个别地点获得未有解释到位, 小编先分享给大家. 后续作者会更新的. 谢谢各位观众老爷的翻阅,如极度请赐教, 四角裤多谢不尽. 多谢大家啦~~~~

drawPoint(arr[i].x,arr[i].y);

缓慢解决小尾巴

所谓的小尾巴,如下:

sbf282.com 10

化解办法也很简单,在 touchend 的时候,先进行 clearRect 就 ok 了。

运营后的飞机的样式.

document.getElementById('title').innerHTML ='再一次输入';

2. info 音讯展现

关于 info 消息呈现,本身写了贰个悬浮窗,然后默以为 display: none,然后写了三个 showInfo 函数用来突显提醒音信,直接调用:

showInfo: function(message, timer){ // 特地用来展现 info var info = this.dom.info; info.innerHTML = message; info.style.display = 'block'; setTimeout(function(){ info.style.display = ''; }, 一千) }

1
2
3
4
5
6
7
8
showInfo: function(message, timer){ // 专门用来显示 info
  var info = this.dom.info;
  info.innerHTML = message;
  info.style.display = 'block';
  setTimeout(function(){
    info.style.display = '';
  }, 1000)
}

关于 info 的样式,在 html 中呢。

体制布局

storePass(lastPoint);

总结

大致花了10日左右的大运,将以此 H5 的手势解锁给到位,本身大概比较满足的,就算恐怕达不到裁判老师的认可,不过自个儿在做的长河中,学习到了累累新知识。

for(key in info){

    var obj = new Image();

   obj.src = info[key];

//闭包情势return函数再次回到当前的图纸加载成功的键值对.

   obj.onload = (function(key2){

      return function(){

               loadingimg[key2] = this;

               loadingimgnumber++;

              if(loadingimgnumber == allimg){

                 if(callbock.done){

                  callbock.done(loadingimg);

               }

            }

       }

   })(key);

}

剩下的就是主函数代码了.

//main主函数

function main(loadingimg){

//最早绘制画布的飞机

var canvas = document.getElementById("canvas");

var ctx = canvas.getContext('2d');

//获取canvas的宽高

canvaswidth = canvas.width;

canvasheight = canvas.height;

//飞机的苗头地点

var loadX = canvaswidth/2 - 33;

var loadY = canvasheight - 82;

//飞机对象

var plane = {

w:66,

h:82,

x:canvaswidth/2 - 33,

y:canvasheight - 82,

startX:0,

startY:0,

endX:0,

endY:0,

flag:true,

draw:function(){

ctx.drawImage(loadingimg.plane,0,0,this.w,this.h,this.x,this.y,this.w,this.h);

},

move:function(){

canvas.addEventListener('touchstart',function(e){

var ev = e || window.event;

var touchs = ev.touches[0];

plane.startX = touchs.pageX;

plane.startY = touchs.pageY;

},false);

canvas.addEventListener('touchmove',function(e){

var ev = e || window.event;

var touchs = ev.touches[0];

if(plane.flag == true){

var x = touchs.pageX - plane.startX+ loadX + plane.endX;

var y = touchs.pageY - plane.startY+ loadY + plane.endY;

plane.x = x ;

plane.y = y ;

plane.draw();

}else{

var x = touchs.pageX - plane.startX + plane.endX;

var y = touchs.pageY - plane.startY + plane.endY;

plane.x = x ;

plane.y = y ;

plane.draw();

}

},false);

canvas.addEventListener('touchend',function(e){

// var ev = e || window.event;

// var touchs = ev.changedTouches[0];

plane.endX = plane.x;

plane.endY = plane.y;

plane.flag = false;

},false);

}

};

plane.draw();

plane.move();

//背景对象

var backgrounds = {

x:0,

y:0,

w:canvaswidth,

h:canvasheight,

draw:function(){

ctx.drawImage(loadingimg.background,this.x,this.y,this.w,this.h);

ctx.drawImage(loadingimg.background,this.x,this.y-this.h,this.w,this.h);

},

move:function(){

this.y+=5;

if(this.y >= this.h){

this.y=0;

}

backgrounds.draw();

}

};

backgrounds.draw();

//动画

function gameloop(){

backgrounds.move();

plane.draw();

window.requestAnimationFrame(gameloop);

}

window.requestAnimationFrame(gameloop);

}

if(checkPass(pswObj.spassword, psw)) {

H5 手势解锁

扫码在线查看:

sbf282.com 11

如故点击查阅手机版。

项目 GitHub 地址,H5HandLock。

首先,小编要证实一下,对于那么些种类,我是参照别人的,H5lock。

自己感觉三个相比较合理的解法应该是采取 canvas 来促成,不知情有没有大神用 css 来完成。假如纯用 css 的话,能够将连线先安装 display: none,当手指划过的时候,展现出来。光设置这一个本该就丰裕辛劳呢。

事先驾驭过 canvas,但并未有真正的写过,上面就来介绍小编近来学习 canvas 并促成 H5 手势解锁的进度。

率先画面布局

}elseif(pswObj.step ==2) {

关于 showInfo

出于showInfo 中有 setTimeout 函数,能够见到函数里的演艺为 1s,导致若是我们操作的速度相当慢,在 1s 内连接 show 了众七个info,前边的 info 会被第一个 info 的 setTimeout 弄乱,显示的岁月低于 1s,或更加短。例如,当再一次点击设置手势密码和申明手势密码,会产生那个 bug。

消除办法有三个,三个是充实贰个特意用来呈现的数组,每一回从数组中取值然后彰显。另一种解题思路和防抖动的思绪很像,便是当有一个新的 show 到来时,把此前的非常 setTimeout 清除掉。

此地运用第三种思路:

showInfo: function(message, timer){ // 特地用来展现 info clearTimeout(this.showInfo.timer); var info = this.dom.info; info.innerHTML = message; info.style.display = 'block'; this.showInfo.timer = setTimeout(function(){ info.style.display = ''; }, timer || 一千) },

1
2
3
4
5
6
7
8
9
showInfo: function(message, timer){ // 专门用来显示 info
  clearTimeout(this.showInfo.timer);
  var info = this.dom.info;
  info.innerHTML = message;
  info.style.display = 'block';
  this.showInfo.timer = setTimeout(function(){
    info.style.display = '';
  }, timer || 1000)
},

});

关于优化

品质优化一向都以贰个大主题材料,不要认为前端无需考虑内存,就足以随意写代码。

前边在布署和谐网页的时候,用到了滚动,鼠标滑轮轻轻一碰,滚动函数就实行了几十多则几百次,在此之前也虚构过化解办法。

自己透过info传送过来的图样路线进行遍历.

varpo = getPosition(e);

3. 有关密码

先不思量从 localStorage 读取到情状,新加八个 lsPass 对象,特意用于存款和储蓄密码,由于密码情形非常多,比方设置密码,贰次承认密码,验证密码,为了方便管理,权且设置了密码的三种形式,分别是:

model:1 认证密码形式

model:2 安装密码形式

model:3 安装密码三次证实

具体看下边那么些图:

sbf282.com 12

这两种 model ,只要管理好它们之间什么跳转就 ok 了,即状态的更动。

就此就有了 initPass:

initPass: function(){ // 将密码开端化 this.lsPass = w.localStorage.getItem('HandLockPass') ? { model: 1, pass: w.localStorage.getItem('HandLockPass').split('-') } : { model: 2 }; this.updateMessage(); }, updateMessage: function(){ // 依照当下格局,更新 dom if(this.lsPass.model == 2){ this.dom.setPass.checked = true; this.dom.message.innerHTML = '请设置手势密码'; }else if(this.lsPass.model == 1){ this.dom.checkPass.checked = true; this.dom.message.innerHTML = '请表达手势密码'; }else if(this.lsPass.model = 3){ this.dom.setPass.checked = true; this.dom.message.innerHTML = '请再度输入密码'; } },

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
initPass: function(){ // 将密码初始化
  this.lsPass = w.localStorage.getItem('HandLockPass') ? {
    model: 1,
    pass: w.localStorage.getItem('HandLockPass').split('-')
  } : { model: 2 };
  this.updateMessage();
},
 
updateMessage: function(){ // 根据当前模式,更新 dom
  if(this.lsPass.model == 2){
    this.dom.setPass.checked = true;
    this.dom.message.innerHTML = '请设置手势密码';
  }else if(this.lsPass.model == 1){
    this.dom.checkPass.checked = true;
    this.dom.message.innerHTML = '请验证手势密码';
  }else if(this.lsPass.model = 3){
    this.dom.setPass.checked = true;
    this.dom.message.innerHTML = '请再次输入密码';
  }
},

有不可或缺再来介绍一下 lsPass 的格式:

this.lsPass = { model:1, // 表示近年来的方式 pass: [0, 1, 2, 4, 5] // 代表方今的密码,可能不设有 }

1
2
3
4
this.lsPass = {
  model:1, // 表示当前的模式
  pass: [0, 1, 2, 4, 5] // 表示当前的密码,可能不存在
}

因为前面已经有了四其中坚的完结框架,今后只须要在 touchend 之后,写贰个函数,功用正是先对近日的 model 举行剖断,完结对应的效应,这里要用到 touchCircles 数组,表示密码的相继:

JavaScript

checkPass: function(){ var succ, model = this.lsPass.model; //succ 未来会用到 if(model == 2){ // 设置密码 if(this.touchCircles.length < 5){ // 验证密码长度 succ = false; this.showInfo('密码长度最少为 5!', 一千); }else{ succ = true; this.lsPass.temp = []; // 将密码放到不时区存款和储蓄 for(var i = 0; i < this.touchCircles.length; i++){ this.lsPass.temp.push(this.touchCircles[i].id); } this.lsPass.model = 3; this.showInfo('请再一次输入密码', 一千); this.updateMessage(); } }else if(model == 3){// 确认密码 var flag = true; // 先要验证密码是还是不是准确 if(this.touchCircles.length == this.lsPass.temp.length){ var tc = this.touchCircles, lt = this.lsPass.temp; for(var i = 0; i < tc.length; i++){ if(tc[i].id != lt[i]){ flag = false; } } }else{ flag = false; } if(!flag){ succ = false; this.showInfo('两遍密码不同,请重新输入', 一千); this.lsPass.model = 2; // 由于密码不正确,重新归来 model 2 this.updateMessage(); }else{ succ = true; // 密码精确,localStorage 存款和储蓄,并设置景况为 model 1 w.localStorage.setItem('HandLockPass', this.lsPass.temp.join('-')); // 存款和储蓄字符串 this.lsPass.model = 1; this.lsPass.pass = this.lsPass.temp; this.updateMessage(); } delete this.lsPass.temp; // 很主要,必定要删掉,bug }else if(model == 1){ // 验证密码 var tc = this.touchCircles, lp = this.lsPass.pass, flag = true; if(tc.length == lp.length){ for(var i = 0; i < tc.length; i++){ if(tc[i].id != lp[i]){ flag = false; } } }else{ flag = false; } if(!flag){ succ = false; this.showInfo('很可惜,密码错误', 1000); }else{ succ = true; this.showInfo('恭喜你,验证通过', 一千); } } },

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
checkPass: function(){
  var succ, model = this.lsPass.model; //succ 以后会用到
  if(model == 2){ // 设置密码
    if(this.touchCircles.length < 5){ // 验证密码长度
      succ = false;
      this.showInfo('密码长度至少为 5!', 1000);
    }else{
      succ = true;
      this.lsPass.temp = []; // 将密码放到临时区存储
      for(var i = 0; i < this.touchCircles.length; i++){
        this.lsPass.temp.push(this.touchCircles[i].id);
      }
      this.lsPass.model = 3;
      this.showInfo('请再次输入密码', 1000);
      this.updateMessage();
    }
  }else if(model == 3){// 确认密码
    var flag = true;
    // 先要验证密码是否正确
    if(this.touchCircles.length == this.lsPass.temp.length){
      var tc = this.touchCircles, lt = this.lsPass.temp;
      for(var i = 0; i < tc.length; i++){
        if(tc[i].id != lt[i]){
          flag = false;
        }
      }
    }else{
      flag = false;
    }
    if(!flag){
      succ = false;
      this.showInfo('两次密码不一致,请重新输入', 1000);
      this.lsPass.model = 2; // 由于密码不正确,重新回到 model 2
      this.updateMessage();
    }else{
      succ = true; // 密码正确,localStorage 存储,并设置状态为 model 1
      w.localStorage.setItem('HandLockPass', this.lsPass.temp.join('-')); // 存储字符串
      this.lsPass.model = 1;
      this.lsPass.pass = this.lsPass.temp;
      this.updateMessage();
    }
    delete this.lsPass.temp; // 很重要,一定要删掉,bug
  }else if(model == 1){ // 验证密码
    var tc = this.touchCircles, lp = this.lsPass.pass, flag = true;
    if(tc.length == lp.length){
      for(var i = 0; i < tc.length; i++){
        if(tc[i].id != lp[i]){
          flag = false;
        }
      }
    }else{
      flag = false;
    }
    if(!flag){
      succ = false;
      this.showInfo('很遗憾,密码错误', 1000);
    }else{
      succ = true;
      this.showInfo('恭喜你,验证通过', 1000);
    }
  }
},

密码的装置要参照前边那张图,要每十二三十一日警醒状态的改动。

if(pswObj.step ==1) {

本文由胜博发-前端发布,转载请注明来源:根据canvas的大小来平均分配半径sbf282.com:,就是