使用canvas绘制九宫格解锁

本篇讲述如何用canvas实现九宫格解锁

概括

canvas是 HTML5 新增的元素,可使用JavaScript脚本来绘制图形。例如:画图,合成照片,创建动画甚至实时视频处理与渲染。

用法

html

1
<canvas id="canvas" width="150" height="150"></canvas>

注意:这个宽高写在行内 跟通过id,class来控制宽高是不一样的。

javscript

1
2
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");

方法(基本操作)

所有的方法都是ctx身上的方法

示例:

1
2
3
4
5
6
7
ctx.beginPath();
ctx.moveTo(75,50);
ctx.lineTo(100,75);
ctx.lineTo(100,25);
ctx.fillStyle = "rgba(255,0,0,0.5)";
ctx.fill();
closePath()

坐标:左上点为园点,x为从左到右依次递增,y为从上到下依次递增

设置样式

设置图形的填充颜色
1
fillStyle = color

设置图形轮廓的颜色
1
strokeStyle = color

1
2
3
4
5
// 这些 fillStyle 的值均为 '橙色'
fillStyle = "orange";
fillStyle = "#FFA500";
fillStyle = "rgb(255,165,0)";
fillStyle = "rgba(255,165,0,0.5)";

设置线宽
1
ctx.lineWidth = 3
新建一条路径,生成之后,图形绘制命令被指向到路径上生成路径
1
beginPath()
闭合路径之后图形绘制命令又重新指向到上下文中。
1
closePath()
抬起笔尖移动到-
1
moveTo(x,y);
路径从上一个位置移动到-
1
lineTo(x,y);
通过线条来绘制图形轮廓。
1
stroke()
通过填充路径的内容区域生成实心的图形。
1
fill()
绘制一个填充的矩形
1
fillRect(x, y, width, height)
绘制一个矩形的边框
1
strokeRect(x, y, width, height)

绘制一个圆

画一个以(x,y)为圆心的以radius为半径的圆弧(圆),从startAngle开始到endAngle结束,按照anticlockwise给定的方向(默认为顺时针)来生成。
1
arc(x, y, radius, startAngle, endAngle, anticlockwise)

根据给定的控制点和半径画一段圆弧,再以直线连接两个控制点。
1
arcTo(x1, y1, x2, y2, radius)

注意:这里的角度只能以Math.PI算,Math.PI==180度;


清除指定矩形区域,让清除部分完全透明
1
clearRect(x, y, width, height)

这里的清除,是指清除到背景色。如果你通过ID,CLASS设置了canvas的背景色,那么,清理后会显示你设置的背景色的;


多加练习以后上边提到的方法后我们就可以实现我们的九宫格解锁了

思路

html

1
<canvas id="canvas" width="300px" height="300px"></canvas>

javascript
1
2
3
4
5
6
var canvas=document.getElementById("canvas");
var ctx = canvas.getContext("2d");
ctx.lineWidth = 3;
ctx.fillStyle="#000";
ctx.fillRect(0,0,300,300);
ctx.clearRect(0,0,300,300);

在开始之前,我们先想一下,我们需要做什么,一个九宫格。肯定需要九个圆,九个圆心;所以我们先获取一下这9个圆的圆心;


先获取cnavas的宽高,然后计算得出3个中点,通过中点,得到9个圆的圆心的坐标,这里的flag稍后会解释。
1
2
3
4
5
6
7
8
arr=[50.5,150.5,250.5]; //这是那3个中点
var data=[];
for(var i=0;i<arr.length;i++){
for(var j=0;j<arr.length;j++){㥐
var obj={x:arr[j],y:arr[i],flag:false}
data.push(obj)
}
}

这里的data就是九个圆心的坐标,接下来我们是需要画圆,这里先封装一个画圆的函数。通过flag来判断是画实心圆跟空心圆这里的flag跟上边的flag是不一样的。

画圆函数

1
2
3
4
5
6
7
8
9
10
11
12
function rend_arc(x,y,r,flag,color){
ctx.beginPath();
ctx.strokeStyle = color;
ctx.fillStyle=color;
ctx.arc(x,y, r, 0, Math.PI * 2, true);
if(!flag){
ctx.stroke();
}else{
ctx.fill();
}
ctx.closePath();
}

之后我们需要调用这个函数画9个圆。为了逼真,我们画了大圆跟小圆,这里画了10半径的实心圆跟30半径的空心圆。

1
2
3
4
for(var i=0;i<data.length;i++){
rend_arc(data[i].x,data[i].y,10,true,"red")
rend_arc(data[i].x,data[i].y,30,false,"red")
}

这里我们的页面中就会有九个圆在了:

手指触摸事件

接下来我们就需要手指触摸事件
我先介绍一下

1
touchstart(触摸开始) touchmove (触摸移动) touchend (抬起手指)

触摸开始

触摸开始的时候,我们需要先判断是否在哪一个点上,如果不是在任何一个点上,说明这次触摸不起作用;
首先我们假设他开始点击,就把第一个圆变成红色的点中状态;

1
2
3
4
5
6
7
8
9
10
11
12
13
$("#canvas").on("touchstart",function(e){
ctx.clearRect(0,0,300,300); //先清理,再重新绘制;
data[0].flag=false; //这里我们把按钮1的状态变成被选中
for(var i=0;i<data.length;i++){
if(data[i].flag){
rend_arc(data[i].x,data[i].y,10,true,"red")
rend_arc(data[i].x,data[i].y,30,false,"red")
}else{
rend_arc(data[i].x,data[i].y,10,true,"#000")
rend_arc(data[i].x,data[i].y,30,false,"#000")
}
}
})

在这里你就知道了 data 的 flag 是来判断每一个点的是否被点击过;因为在上边我们改变了data[0].flag=true ,也就是说把第一个点变的选中了。渲染出来就是如图

触摸移动

接下来我们在手指移动过程中,把页面重回,在加上当前点的位置,就变成动态的移动了,我们写move事件,这里手机端的触摸事件的事件源的坐标是在
e.originalEvent.changedTouches[0]身上的。

我们先写一个函数,就是绘制这个当前的点的位置跟已经点击过的位置;

1
2
var zuobiao=[]; //就是鼠标滑过的点的坐标这里我们先把第一个点放进去;
zuobiao.push(dian) //在start的时候,把dian变成0;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//move 的时候要每秒重绘
function send_fill(x,y){
make_arc(); //画圆;
ctx.beginPath()
ctx.strokeStyle = "red";
ctx.moveTo(data[zuobiao[0]].x,data[zuobiao[0]].y);//将画笔移到x0,y0处
for(var n=1;n<zuobiao.length;n++){
ctx.lineTo(data[zuobiao[n]].x,data[zuobiao[n]].y);//将画笔移到x0,y0处
}
ctx.lineTo(x,y);//从x0,y0到x1,y1画一条线
ctx.stroke();//画线
ctx.closePath();
}
1
2
3
4
5
6
7
8
9
//move 函数
$("#canvas").on("touchmove",function(e){
var _e=e.originalEvent.changedTouches[0];
var x=_e.pageX;
var y=_e.pageY;
var l=$("canvas").offset().left;
var t=$("canvas").offset().top;
send_fill(x-l,y-t); //这里传过去的x,y就是当前手指的位置;
})

这样之后,在move 的时候效果就会如图所示:

大家思考一下,到了这一步,我们所缺的就是一个判断而已;就是判断当前的位置在哪一个点的范围内。把哪一个点的flag变成true就好;
接下来我们实现这一步

判断范围

我们先判断一个大范围。就是说目前这个点在哪个区域里面
计算是哪个点开始点击的; 返回一个点的id
这里面的条件就是边界,0-100-200-300;

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
function set(x,y){
if(x>0&&x<100){
if(y>0&&y<100){
return 0;
}else if(y>100&&y<200){
return 3;
}else if(y>200&&y<300){
return 6;
}
}else if(x>100&&x<200){
if(y>0&&y<100){
return 1;
}else if(y>100&&y<200){
return 4;
}else if(y>200&&y<300){
return 7;
}
}else if(x>200&&x<300){
if(y>0&&y<100){
return 2;
}else if(y>100&&y<200){
return 5;
}else if(y>200&&y<300){
return 8;
}
}
return 10; //这里写10的原因是我false。在另一个函数判断的时候。返回0也是false。所以就换成10
}

判断是否在圆范围

通过这个我们可以判断出一个大范围;接下来我们更加精密的判断一个小范围

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//这个的精密判断在圆的里面;
function set_arc(x,y){
var num=set(x,y)
if(num!=10){
var a=data[num].x;
var b=data[num].y;
if(Math.abs(x-a)<30&&Math.abs(y-b)<30){
//在里
return num;
}
}else{
return 10;
}
}

这里我们判断出了大的范围。并且说这个是!==10,意思就是手指的触摸点是在canvas上,
这里我们换一种想法来思考。
我们前边画的圆的半径是30,那么就是说从这个圆的圆心开始,x|y +30或者-30 就是每个圆的边界,换句话说这就是范围;
我们用当前触摸点的x,y通过set()获取一个大的范围,在这个大范围内。我们再判断。 data[set()].x,data[set()].y 跟我们现在的x,y相减一下。如果是在30范围内。这里我用Math.abs()取正一下。这个数<30. 那么就说明是在这个点内。
我们就可以把这个点的data[点].flag=true;

触摸停止

鼠标抬起就更好了;把这个canavs清空重绘就好;或者显示,都可以安你的需求来。,这里我选择的是重绘

全部代码

我们把代码概括一下;

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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
var canvas=document.getElementById("canvas"); //得到一个canavs对象
var ctx = canvas.getContext("2d"); //得到一个2d画笔
ctx.lineWidth = 3; //设置线宽
ctx.fillStyle="#000"; //设置画笔的颜色
ctx.fillRect(0,0,300,300); //这个是清除到原来的默认颜色;并不是透明色
ctx.clearRect(0,0,300,300); // 清理一块坐标内的内容
// 这是中心的中心
var arr=[50.5,150.5,250.5];
var data=[]; // 最初的9个圆心的坐标
for(var i=0;i<arr.length;i++){
for(var j=0;j<arr.length;j++){
var obj={x:arr[j],y:arr[i],flag:false}
data.push(obj)
}
}
var zuobiao=[]; //就是鼠标滑过的点的坐标
make_arc(); //画圆点
function make_arc(){
ctx.clearRect(0,0,300,300); //清空
for(var i=0;i<data.length;i++){
if(data[i].flag){
rend_arc(data[i].x,data[i].y,10,true,"red")
rend_arc(data[i].x,data[i].y,30,false,"red")
}else{
rend_arc(data[i].x,data[i].y,10,true,"#000")
rend_arc(data[i].x,data[i].y,30,false,"#000")
}
}
};
//画圆的函数
function rend_arc(x,y,r,flag,color){
ctx.beginPath();
ctx.strokeStyle = color;
ctx.fillStyle=color;
ctx.arc(x,y, r, 0, Math.PI * 2, true);
if(!flag){
ctx.stroke();
}else{
ctx.fill();
}
ctx.closePath();
}
var me_f=false; //判断第一次就点到点上了,如果没有move的时候就不执行
$("#canvas").on("touchstart",function(e){
me_f=false;
$("h1").html("请输入密码");
var _e=e.originalEvent.changedTouches[0];
var x=_e.pageX;
var y=_e.pageY;
var l=$("canvas").offset().left;
var t=$("canvas").offset().top;
var num=set_arc(x-l,y-t)+1;
if(num!=11&&num){
num=num-1;
me_f=true;
//放弃清理原来的了。。总是清理的有点多
// ctx.clearRect(data[num].x-50.5,data[num].y-50.5,data[num].x+49.5,data[num].y+49.5);
data[num].flag=true;
zuobiao.push(num);
rend_arc(data[num].x,data[num].y,10,true,"red")
rend_arc(data[num].x,data[num].y,30,false,"red")
}
})
$("#canvas").on("touchmove",function(e){
if(!me_f){
return false;
}
var _e=e.originalEvent.changedTouches[0];
var x=_e.pageX;
var y=_e.pageY;
var l=$("canvas").offset().left;
var t=$("canvas").offset().top;
send_fill(x-l,y-t);
//这里是要判断 经过的点;
var dian=set_arc(x-l,y-t)+1;
// var dian=set(x-l,y-t);
if(dian!=11){
if(dian){
dian=dian-1;
/* console.log(dian)
return;*/
if(!data[dian].flag){
data[dian].flag=true;
zuobiao.push(dian);
}
}
};
})
//move 的时候要每秒重绘
function send_fill(x,y){
make_arc(); //画圆;
ctx.beginPath()
ctx.strokeStyle = "red";
// ctx.moveTo(data[zuobiao[0]].x,data[zuobiao[0]].y);//将画笔移到x0,y0处
ctx.moveTo(data[zuobiao[0]].x,data[zuobiao[0]].y);//将画笔移到x0,y0处
for(var n=1;n<zuobiao.length;n++){
ctx.lineTo(data[zuobiao[n]].x,data[zuobiao[n]].y);//将画笔移到x0,y0处
}
ctx.lineTo(x,y);//从x0,y0到x1,y1画一条线
// ctx.fill();//填充
ctx.stroke();//画线
ctx.closePath();
}
//end 的时候。。所有的值要恢复原值;判断 重置。
$("#canvas").on("touchend",function(e){
if(!me_f){
return false;
}
panduan(zuobiao);
zuobiao=[];
for(var i=0;i<data.length;i++){
data[i].flag=false;
}
make_arc(); //画圆;
})
// 计算是哪个点开始点击的; 返回一个点的id
function set(x,y){
if(x>0&&x<100){
if(y>0&&y<100){
return 0;
}else if(y>100&&y<200){
return 3;
}else if(y>200&&y<300){
return 6;
}
}else if(x>100&&x<200){
if(y>0&&y<100){
return 1;
}else if(y>100&&y<200){
return 4;
}else if(y>200&&y<300){
return 7;
}
}else if(x>200&&x<300){
if(y>0&&y<100){
return 2;
}else if(y>100&&y<200){
return 5;
}else if(y>200&&y<300){
return 8;
}
}
return 10;
}
var cir=[]; //每一个点的位置的大小,,也就是每一个圆的大小;
for(var i=0;i<data.length;i++){
var obj={}
obj.max_w=data[i].x+30;
obj.min_w=data[i].x-30;
obj.max_h=data[i].y+30;
obj.min_h=data[i].y-30;
cir.push(obj);
}
//这个的精密判断在圆的里面;
function set_arc(x,y){
var num=set(x,y)
if(num!=10){
var a=data[num].x;
var b=data[num].y;
if(Math.abs(x-a)<30&&Math.abs(y-b)<30){
//在里
return num;
}
}else{
return 10;
}
}

总结

在这里我详细讲述了如何用canavs来实现九宫格解锁的功能。可能比较乱,大家多多包涵。不过内容比较详细,所用的方法也是新手易懂的。
在我的github上有一个我用这个方法写的成功案例,如果需要可以参考一下.谢谢。

本文总阅读量

坚持原创技术分享,您的支持将鼓励我继续创作!