HTML5 Canvas手机九宫格手势密码解锁 ,支持N*N个点
在开始之前,先介绍一个数学知识,怎么判断一个点是否在圆内 通过判断一个点到圆心的距离是否大于半径。 例如:半径是R 如O(x,y)点圆心,任意一点P(x1,y1) (x-x1)*(x-x1)+(y-y1)*(y-y1)>R*R 那么在圆外 反之在圆内
准备及布局设置
本例引入了jQuery,写成了jQuery插件的形式,真实使用的时候,可以去掉jQuery,用原生的js写。
jQuery插件的写法
1;(function ($) {2 $.fn.locked = function (settings) {3 settings = $.extend({}, defaultSettings, settings)4 }5})(jQuery)67$(function () {8 $('#canvas').locked({9 n: 4,10 })11})
默认配置 jQuery插件调用时传入的配置会覆盖defaultSettings 其中document.body.offsetWidth获取的是网页可见区域宽,没特殊需要可以不改
1//两个变量记录,所有的点以及选中的点2var pointArr = [] //点数组3var pointActiveArr = [] //已激活点数组45//默认配置6var defaultSettings = {7 r: 25, //大圆半径8 sr: 8, //小圆半径,小圆既选中状态,内部的小圆9 w: document.body.offsetWidth, //canvas宽度10 h: document.body.offsetWidth, //canvas高度11 n: 3, //数量n*n12 pointColor: '#ff0000', //选中状态点线颜色13 pointDefault: '#686868', //默认点颜色14}
获取到canvas对象,并根据设备dpi对settings配置进行修改
1var canvas = $(this)[0] //jQuery对象转成js对象2var ctx = canvas.getContext('2d')34//防止手机端canvas模糊,计算dpi5var dpi = getPixelRatio(ctx)6settings.r *= dpi7settings.sr *= dpi8settings.w *= dpi9settings.h *= dpi1011//设置canvas宽度高度12canvas.width = settings.w13canvas.height = settings.h
获取dpi的方式
1//获取dpi2function getPixelRatio(context) {3 var backingStore =4 context.backingStorePixelRatio ||5 context.webkitBackingStorePixelRatio ||6 context.mozBackingStorePixelRatio ||7 context.msBackingStorePixelRatio ||8 context.oBackingStorePixelRatio ||9 context.backingStorePixelRatio ||10 11112 return (window.devicePixelRatio || 1) / backingStore13}
创建N\*N个点并将x,y点的坐标记录到pointArr数组中,点的位置根据canvas宽高平均分配
1pointArr = creatPoint()23//创建n*n个点4function creatPoint() {5 var points = []6 for (var row = 0; row < settings.n; row++) {7 for (var col = 0; col < settings.n; col++) {8 points.push({9 x: (settings.w / (settings.n + 1)) * (col + 1),10 y: (settings.h / (settings.n + 1)) * (row + 1),11 })12 }13 }14 return points15}
添加事件监听
1//事件监听2canvas.addEventListener(3 'touchstart',4 function (e) {5 touch(e)6 },7 false8)9canvas.addEventListener(10 'touchmove',11 function (e) {12 touch(e)13 },14 false15)16canvas.addEventListener(17 'touchend',18 function (e) {19 touch(e)20 },21 false22)
touch函数统一处理滑动事件
1//事件监听处理2function touch(e) {3 var e = e || window.event4 console.log(e.type)5 switch (e.type) {6 case 'touchstart':7 isSelect(e.touches[0])8 break9 case 'touchend':10 draw()11 alert('密码结果是:' + pointActiveArr.join('-'))12 pointActiveArr = []13 break14 case 'touchmove':15 isSelect(e.touches[0])16 draw(e.touches[0])17 e.preventDefault()18 break19 }20}
滑动开始和滑动过程中通过isSelect判断当前的点是否进入了某个圆内,如果进入了某个圆且这个点没有添加到pointActiveArr数组中,则添加。
1//判断是否在圆内2//通过数学计算来判断3function isSelect(touche) {4 //遍历所有点,查看当前位置是否在圆内5 for (var i = 0; i < pointArr.length; i++) {6 var point = pointArr[i]7 var x_diff = Math.abs(point.x - touche.pageX * dpi)8 var y_diff = Math.abs(point.y - touche.pageY * dpi)910 //如果 (x_diff*x_diff + y_diff*y_diff) > settings.r*settings.r 则在圆外11 if (x_diff * x_diff + y_diff * y_diff < settings.r * settings.r) {12 if (pointActiveArr.indexOf(i) < 0) {13 pointActiveArr.push(i)14 }15 break16 }17 }18}
滑动结束以后输出当前pointActiveArr中的内容,作为密码进行判断,具体判断的逻辑可以自己实现
接下来就是最主要的,绘制canvas,先上代码
1//绘制canvas2function draw(touch) {3 ctx.clearRect(0, 0, settings.w, settings.h)4 //绘制n*n个圈5 for (var i = 0; i < pointArr.length; i++) {6 var point = pointArr[i]7 ctx.fillStyle = settings.pointDefault8 ctx.beginPath()9 ctx.arc(point.x, point.y, settings.r, 0, Math.PI * 2, true)10 ctx.closePath()11 ctx.fill()12 ctx.fillStyle = '#ffffff'13 ctx.beginPath()14 ctx.arc(point.x, point.y, settings.r - 6, 0, Math.PI * 2, true)15 ctx.closePath()16 ctx.fill()1718 //如果当前点已被选中,这中间添加一个小的圆19 if (pointActiveArr.indexOf(i) >= 0) {20 ctx.fillStyle = settings.pointColor21 ctx.beginPath()22 ctx.arc(point.x, point.y, settings.sr, 0, Math.PI * 2, true)23 ctx.closePath()24 ctx.fill()25 }26 }2728 //如果有传当前移动位置,则添加和最后一个选中点的连线29 if (touch != null) {30 var lastPoint = pointArr[pointActiveArr[pointActiveArr.length - 1]]31 ctx.beginPath()32 ctx.moveTo(lastPoint.x, lastPoint.y)33 ctx.lineTo(touch.pageX * dpi, touch.pageY * dpi)34 ctx.stroke()35 ctx.closePath()36 }3738 //绘制选中的线39 if (pointActiveArr.length > 0) {40 ctx.beginPath()41 for (var i = 0; i < pointActiveArr.length; i++) {42 var index = pointActiveArr[i]43 ctx.lineTo(pointArr[index].x, pointArr[index].y)44 }45 ctx.lineWidth = 1046 ctx.strokeStyle = settings.pointColor47 ctx.stroke()48 ctx.closePath()49 }50}
绘制的时候先画了我们pointArr中定义的点,画N*N个圆。如果当前点在pointActiveArr中存在,则在圆内部画一个小圆。 根据pointActiveArr,在相邻的两个点中间,画直线。 touch传入的是当前滑动到的位置,将其与 pointActiveArr中的最后一个相连。
到此位置基本的功能就实现完了。
本例可以做一些进一步的优化
- canvas底部的N\*N的点不需要每次都重绘,可以用两个canvas进行叠加
- 本例只是实现了绘制的部分,并没有进行具体的解锁逻辑处理。
可以将密码保存到 localStorage 里,页面打开的时候从本地读取密码,如果没有设置就让用户设置密码,具体的密码规则自己定制。
- 没有添加密码错误/正确的样式,可以在配置中添加相应的颜色,再绘制canvas的时候选择对象的颜色。
暂无评论,来抢沙发吧。