用 canvas 实现 Web 手势解锁
来源:互联网 发布:ios炫字体 源码 编辑:程序博客网 时间:2024/06/06 03:17
实现的最终效果就像下面这张图这样:
基本要求是这样的:将密码保存到 localStorage 里,开始的时候会从本地读取密码,如果没有就让用户设置密码,密码最少为五位数,少于五位要提示错误。需要对第一次输入的密码进行验证,两次一样才能保持,然后是验证密码,能够对用户输入的密码进行验证。
H5 手势解锁
准备及布局设置
我这里用了一个比较常规的做法:
常规方法,比较易懂和操作,弊端就是,可以被随意的修改。
传入的参数中要包含一个 dom 对象,会在这个 dom 对象內创建一个 canvas。当然还有一些其他的 dom 参数,比如 message,info 等。
关于 css 的话,懒得去新建文件了,就直接內联了。
canvas
1. 学习 canvas 并搞定画圆
MDN 上面有个简易的教程,大致浏览了一下,感觉还行。Canvas教程。
先创建一个 canvas,然后设置其大小,并通过 getContext 方法获得绘画的上下文:
然后呢,先画 n*n 个圆出来:
画圆函数,需要注意:如何确定圆的半径和每个圆的圆心坐标(这个我是参考的),如果以圆心为中点,每个圆上下左右各扩展一个半径的距离,同时为了防止四边太挤,四周在填充一个半径的距离。那么得到的半径就是 width / ( 4 * n + 2),对应也可以算出每个圆所在的圆心坐标,也有一套公式,GET。
2. 画线
画线需要借助 touch event 来完成,也就是,当我们 touchstart 的时候,传入开始时的相对坐标,作为线的一端,当我们 touchmove 的时候,获得坐标,作为线的另一端,当我们 touchend 的时候,开始画线。
这只是一个测试画线功能,具体的后面再进行修改。
有两个函数,获得当前 touch 的相对坐标:
画线:然后就是监听 canvas 的 touchstart、touchmove、和 touchend 事件了。
3. 画折线
所谓的画折线,就是,将已经触摸到的点连起来,可以把它看作是画折线。
首先,要用两个数组,一个数组用于已经 touch 过的点,另一个数组用于存储未 touch 的点,然后在 move 监听时候,对 touch 的相对位置进行判断,如果触到点,就把该点从未 touch 移到 touch 中,然后,画折线,思路也很简单。
4. 标记已画
前面已经说了,我们把已经 touch 的点(圆)放到数组中,这个时候需要将这些已经 touch 的点给标记一下,在圆心处画一个小实心圆:
同时添加一个 reset 函数,当 touchend 的时候调用,400ms 调用 reset 重置 canvas。
到现在为止,一个 H5 手势解锁的简易版已经基本完成。
password
为了要实现记住和重置密码的功能,把 password 保存在 localStorage 中,但首先要添加必要的 html 和样式。
1. 添加 message 和 单选框
为了尽可能的使界面简洁(越丑越好),直接在 body 后面添加了:
将添加到 dom 已 option 的形式传给 handLock:2. info 信息显示
关于 info 信息显示,自己写了一个悬浮窗,然后默认为 display: none,然后写了一个 showInfo 函数用来显示提示信息,直接调用:
关于 info 的样式,在 html 中呢。
3. 关于密码
先不考虑从 localStorage 读取到情况,新加一个 lsPass 对象,专门用于存储密码,由于密码情况比较多,比如设置密码,二次确认密码,验证密码,为了方便管理,暂时设置了密码的三种模式,分别是:
model:1 验证密码模式
model:2 设置密码模式
model:3 设置密码二次验证
具体看下面这个图:
这三种 model ,只要处理好它们之间如何跳转就 ok 了,即状态的改变。
所以就有了 initPass:
有必要再来介绍一下 lsPass 的格式:
因为之前已经有了一个基本的实现框架,现在只需要在 touchend 之后,写一个函数,功能就是先对当前的 model 进行判断,实现对应的功能,这里要用到 touchCircles 数组,表示密码的顺序:密码的设置要参考前面那张图,要时刻警惕状态的改变。
4. 手动重置密码
思路也很简单,就是添加点击事件,点击之后,改变 model 即可,点击事件如下:
ps:这里面还有几个小的 bug,因为 model 只有 3 个,所以设置的时候,当点击重置密码的时候,没有设置密码成功,又切成验证密码状态,此时无法提升沿用旧密码,原因是 model 只有三个。5. 添加 touchend 颜色变化
实现这个基本上就大功告成了,这个功能最主要的是给用户一个提醒,若用户划出的密码符合规范,显示绿色,若不符合规范或错误,显示红色警告。
因为之前已经设置了一个 succ 变量,专门用于重绘。
那么,一个可以演示的版本就生成了,尽管还存在一些 bug,随后会来解决。(详情分支 password)
一些 bugs
有些 bugs 在做的时候就发现了,一些 bug 后来用手机测试的时候才发现,比如,我用 chrome 的时候,没有察觉这个 bug,当我用 android 手机 chrome 浏览器测试的时候,发现当我 touchmove 向下的时候,会触发浏览器的下拉刷新,解决办法:加了一个 preventDefault,没想到居然成功了。
关于 showInfo
由于showInfo 中有 setTimeout 函数,可以看到函数里的演出为 1s,导致如果我们操作的速度比较快,在 1s 内连续 show 了很多个 info,后面的 info 会被第一个 info 的 setTimeout 弄乱,显示的时间小于 1s,或更短。比如,当重复点击设置手势密码和验证手势密码,会产生这个 bug。
解决办法有两个,一个是增加一个专门用于显示的数组,每次从数组中取值然后显示。另一种解题思路和防抖动的思路很像,就是当有一个新的 show 到来时,把之前的那个 setTimeout 清除掉。
这里采用第二种思路:
解决小尾巴
所谓的小尾巴,如下:
解决办法也很简单,在 touchend 的时候,先进行 clearRect 就 ok 了。
关于优化
性能优化一直都是一个大问题,不要以为前端不需要考虑内存,就可以随便写代码。
之前在设计自己网页的时候,用到了滚动,鼠标滑轮轻轻一碰,滚动函数就执行了几十多则几百次,之前也考虑过解决办法。
优化 canvas 部分
对于 touchmove 函数,原理都是一样的,手指一划,就执行了 n 多次,这个问题后面在解决,先来看另一个问题。
touchmove 是一个高频函数,看到这里,如果你并没有仔细看我的代码,那你对我采用的 canvas 画图方式可能不太了解,下面这个是 touchmove 函数干了哪些事:
1、先判断,如果当前处于未选中一个密码状态,则继续监视当前的位置,直到选中第一个密码,进入第二步;
2、进入 update 函数,update 函数主要干四件事,重绘圆(密码)、判断当前位置、重绘点、重绘线;
第二步是一个很揪心的动作,为什么每次都要重绘圆,点和线呢?
上面这个图可以很好的说明问题,因为在设置或验证密码的过程中,我们需要用一条线来连接触点到当前的最后一个密码,并且当 touchmove 的时候,能看到它们在变化。这个功能很棒,可以勾勒出 touchmove 的轨迹。
但是,这就必须要时刻刷新 canvas,性能大大地降低,刷新的那可是整个 canvas。
因为 canvas 只有一个,既要画背景圆(密码),又要画已选密码的点,和折线。这其中好多步骤,自始至终只需要一次就好了,比如背景圆,只需在启动的时候画一次,已选密码,只要当 touchCircles 新加元素的时候才会用一次,还不用重绘,只要画就可以了。折线分成两部分,一部分是已选密码之间的连线,还有就是最后一个密码点到当前触点之间的连线。
如果有两个 canvas 就好了,一个存储静态的,一个专门用于重绘。
为什么不可以有呢!
我的解决思路是,现在有两个 canvas,一个在底层,作为描绘静态的圆、点和折线,另一个在上层,一方面监听 touchmove 事件,另一方面不停地重绘最后一个密码点的圆心到当前触点之间的线。如果这样可以的话,touchmove 函数执行一次的效率大大提高。
插入第二个 canvas:
要改换对第二个 ctx2 进行 touch 监听,并设置一个 this.reDraw 参数,表示有新的密码添加进来,需要对点和折线添加新内容, update 函数要改成这样:
相应的 drawPoints 和 drawLine 函数也要对应修改,由原理画所有的,到现在只需要画新加的。
效果怎么样:
move 函数执行多次,而其他函数只有当新密码加进来的时候才执行一次。
加入节流函数
之前也已经说过了,这个 touchmove 函数执行的次数比较多,尽管我们已经用两个 canvas 对重绘做了很大的优化,但 touchmove 还是有点大开销。
这个时候我想到了防抖动和节流,首先防抖动肯定是不行的,万一我一直处于 touch 状态,重绘会延迟死的,这个时候节流会好一些。防抖和节流。
先写一个节流函数:
节流函数的意思:在延迟为 delay 的时间内,如果函数再次触发,则重新计时,这个功能和防抖动是一样的,第三个参数 mustRun 是一个时间间隔,表示在时间间隔大于 mustRun 后的一个函数可以立即直接执行。
然后对 touchmove 的回调函数进行改造:
关于 delay 和 mustRun 的时间间隔问题,web 性能里有一个 16ms 的概念,就是说如果要达到每秒 60 帧,间隔为 1000/60 大约为 16 ms。如果间隔大于 16ms 则 fps 会比 60 低。
鉴于此,我们这里将 delay 和 mustRun 都设为 16,在极端的情况下,也就是最坏的情况下,或许需要 15 + 15 = 30ms 才会执行一次,这个时候要设置两个 8 才合理,不过考虑到手指活动是一个连续的过程,怎么可能会每 15 秒执行一次,经过在线测试,发现设置成 16 效果还不错。
性能真的能优化吗,我们来看两个图片,do 和 wantdo 表示真实执行和放到节流函数中排队准备执行。
当 touchmove 速度一般或很快的时候:
当 touchmove 速度很慢的时候:
可以看出来,滑动过程中,速度一般和快速,平均优化了一半,慢速效果也优化了 20 到 30% 之间,平时手势锁解锁时候,肯定速度很快。可见,节流的优化还是很明显的。
关键是,优化之后的流程性,没有受到任何影响。
这个节流函数最终还是出现了一个 bug:由于是延迟执行的,导致 e.preventDefault 失效,在手机浏览器向下滑会出现刷新的情况,这也算事件延迟的一个危害吧。
解决办法:在节流函数提前取消默认事件:
总结
基本要求是这样的:将密码保存到 localStorage 里,开始的时候会从本地读取密码,如果没有就让用户设置密码,密码最少为五位数,少于五位要提示错误。需要对第一次输入的密码进行验证,两次一样才能保持,然后是验证密码,能够对用户输入的密码进行验证。
H5 手势解锁
准备及布局设置
我这里用了一个比较常规的做法:
常规方法,比较易懂和操作,弊端就是,可以被随意的修改。
传入的参数中要包含一个 dom 对象,会在这个 dom 对象內创建一个 canvas。当然还有一些其他的 dom 参数,比如 message,info 等。
关于 css 的话,懒得去新建文件了,就直接內联了。
canvas
1. 学习 canvas 并搞定画圆
MDN 上面有个简易的教程,大致浏览了一下,感觉还行。Canvas教程。
先创建一个 canvas,然后设置其大小,并通过 getContext 方法获得绘画的上下文:
然后呢,先画 n*n 个圆出来:
画圆函数,需要注意:如何确定圆的半径和每个圆的圆心坐标(这个我是参考的),如果以圆心为中点,每个圆上下左右各扩展一个半径的距离,同时为了防止四边太挤,四周在填充一个半径的距离。那么得到的半径就是 width / ( 4 * n + 2),对应也可以算出每个圆所在的圆心坐标,也有一套公式,GET。
2. 画线
画线需要借助 touch event 来完成,也就是,当我们 touchstart 的时候,传入开始时的相对坐标,作为线的一端,当我们 touchmove 的时候,获得坐标,作为线的另一端,当我们 touchend 的时候,开始画线。
这只是一个测试画线功能,具体的后面再进行修改。
有两个函数,获得当前 touch 的相对坐标:
画线:然后就是监听 canvas 的 touchstart、touchmove、和 touchend 事件了。
3. 画折线
所谓的画折线,就是,将已经触摸到的点连起来,可以把它看作是画折线。
首先,要用两个数组,一个数组用于已经 touch 过的点,另一个数组用于存储未 touch 的点,然后在 move 监听时候,对 touch 的相对位置进行判断,如果触到点,就把该点从未 touch 移到 touch 中,然后,画折线,思路也很简单。
4. 标记已画
前面已经说了,我们把已经 touch 的点(圆)放到数组中,这个时候需要将这些已经 touch 的点给标记一下,在圆心处画一个小实心圆:
同时添加一个 reset 函数,当 touchend 的时候调用,400ms 调用 reset 重置 canvas。
到现在为止,一个 H5 手势解锁的简易版已经基本完成。
password
为了要实现记住和重置密码的功能,把 password 保存在 localStorage 中,但首先要添加必要的 html 和样式。
1. 添加 message 和 单选框
为了尽可能的使界面简洁(越丑越好),直接在 body 后面添加了:
将添加到 dom 已 option 的形式传给 handLock:2. info 信息显示
关于 info 信息显示,自己写了一个悬浮窗,然后默认为 display: none,然后写了一个 showInfo 函数用来显示提示信息,直接调用:
关于 info 的样式,在 html 中呢。
3. 关于密码
先不考虑从 localStorage 读取到情况,新加一个 lsPass 对象,专门用于存储密码,由于密码情况比较多,比如设置密码,二次确认密码,验证密码,为了方便管理,暂时设置了密码的三种模式,分别是:
model:1 验证密码模式
model:2 设置密码模式
model:3 设置密码二次验证
具体看下面这个图:
这三种 model ,只要处理好它们之间如何跳转就 ok 了,即状态的改变。
所以就有了 initPass:
有必要再来介绍一下 lsPass 的格式:
因为之前已经有了一个基本的实现框架,现在只需要在 touchend 之后,写一个函数,功能就是先对当前的 model 进行判断,实现对应的功能,这里要用到 touchCircles 数组,表示密码的顺序:密码的设置要参考前面那张图,要时刻警惕状态的改变。
4. 手动重置密码
思路也很简单,就是添加点击事件,点击之后,改变 model 即可,点击事件如下:
ps:这里面还有几个小的 bug,因为 model 只有 3 个,所以设置的时候,当点击重置密码的时候,没有设置密码成功,又切成验证密码状态,此时无法提升沿用旧密码,原因是 model 只有三个。5. 添加 touchend 颜色变化
实现这个基本上就大功告成了,这个功能最主要的是给用户一个提醒,若用户划出的密码符合规范,显示绿色,若不符合规范或错误,显示红色警告。
因为之前已经设置了一个 succ 变量,专门用于重绘。
那么,一个可以演示的版本就生成了,尽管还存在一些 bug,随后会来解决。(详情分支 password)
一些 bugs
有些 bugs 在做的时候就发现了,一些 bug 后来用手机测试的时候才发现,比如,我用 chrome 的时候,没有察觉这个 bug,当我用 android 手机 chrome 浏览器测试的时候,发现当我 touchmove 向下的时候,会触发浏览器的下拉刷新,解决办法:加了一个 preventDefault,没想到居然成功了。
关于 showInfo
由于showInfo 中有 setTimeout 函数,可以看到函数里的演出为 1s,导致如果我们操作的速度比较快,在 1s 内连续 show 了很多个 info,后面的 info 会被第一个 info 的 setTimeout 弄乱,显示的时间小于 1s,或更短。比如,当重复点击设置手势密码和验证手势密码,会产生这个 bug。
解决办法有两个,一个是增加一个专门用于显示的数组,每次从数组中取值然后显示。另一种解题思路和防抖动的思路很像,就是当有一个新的 show 到来时,把之前的那个 setTimeout 清除掉。
这里采用第二种思路:
解决小尾巴
所谓的小尾巴,如下:
解决办法也很简单,在 touchend 的时候,先进行 clearRect 就 ok 了。
关于优化
性能优化一直都是一个大问题,不要以为前端不需要考虑内存,就可以随便写代码。
之前在设计自己网页的时候,用到了滚动,鼠标滑轮轻轻一碰,滚动函数就执行了几十多则几百次,之前也考虑过解决办法。
优化 canvas 部分
对于 touchmove 函数,原理都是一样的,手指一划,就执行了 n 多次,这个问题后面在解决,先来看另一个问题。
touchmove 是一个高频函数,看到这里,如果你并没有仔细看我的代码,那你对我采用的 canvas 画图方式可能不太了解,下面这个是 touchmove 函数干了哪些事:
1、先判断,如果当前处于未选中一个密码状态,则继续监视当前的位置,直到选中第一个密码,进入第二步;
2、进入 update 函数,update 函数主要干四件事,重绘圆(密码)、判断当前位置、重绘点、重绘线;
第二步是一个很揪心的动作,为什么每次都要重绘圆,点和线呢?
上面这个图可以很好的说明问题,因为在设置或验证密码的过程中,我们需要用一条线来连接触点到当前的最后一个密码,并且当 touchmove 的时候,能看到它们在变化。这个功能很棒,可以勾勒出 touchmove 的轨迹。
但是,这就必须要时刻刷新 canvas,性能大大地降低,刷新的那可是整个 canvas。
因为 canvas 只有一个,既要画背景圆(密码),又要画已选密码的点,和折线。这其中好多步骤,自始至终只需要一次就好了,比如背景圆,只需在启动的时候画一次,已选密码,只要当 touchCircles 新加元素的时候才会用一次,还不用重绘,只要画就可以了。折线分成两部分,一部分是已选密码之间的连线,还有就是最后一个密码点到当前触点之间的连线。
如果有两个 canvas 就好了,一个存储静态的,一个专门用于重绘。
为什么不可以有呢!
我的解决思路是,现在有两个 canvas,一个在底层,作为描绘静态的圆、点和折线,另一个在上层,一方面监听 touchmove 事件,另一方面不停地重绘最后一个密码点的圆心到当前触点之间的线。如果这样可以的话,touchmove 函数执行一次的效率大大提高。
插入第二个 canvas:
要改换对第二个 ctx2 进行 touch 监听,并设置一个 this.reDraw 参数,表示有新的密码添加进来,需要对点和折线添加新内容, update 函数要改成这样:
相应的 drawPoints 和 drawLine 函数也要对应修改,由原理画所有的,到现在只需要画新加的。
效果怎么样:
move 函数执行多次,而其他函数只有当新密码加进来的时候才执行一次。
加入节流函数
之前也已经说过了,这个 touchmove 函数执行的次数比较多,尽管我们已经用两个 canvas 对重绘做了很大的优化,但 touchmove 还是有点大开销。
这个时候我想到了防抖动和节流,首先防抖动肯定是不行的,万一我一直处于 touch 状态,重绘会延迟死的,这个时候节流会好一些。防抖和节流。
先写一个节流函数:
节流函数的意思:在延迟为 delay 的时间内,如果函数再次触发,则重新计时,这个功能和防抖动是一样的,第三个参数 mustRun 是一个时间间隔,表示在时间间隔大于 mustRun 后的一个函数可以立即直接执行。
然后对 touchmove 的回调函数进行改造:
关于 delay 和 mustRun 的时间间隔问题,web 性能里有一个 16ms 的概念,就是说如果要达到每秒 60 帧,间隔为 1000/60 大约为 16 ms。如果间隔大于 16ms 则 fps 会比 60 低。
鉴于此,我们这里将 delay 和 mustRun 都设为 16,在极端的情况下,也就是最坏的情况下,或许需要 15 + 15 = 30ms 才会执行一次,这个时候要设置两个 8 才合理,不过考虑到手指活动是一个连续的过程,怎么可能会每 15 秒执行一次,经过在线测试,发现设置成 16 效果还不错。
性能真的能优化吗,我们来看两个图片,do 和 wantdo 表示真实执行和放到节流函数中排队准备执行。
当 touchmove 速度一般或很快的时候:
当 touchmove 速度很慢的时候:
可以看出来,滑动过程中,速度一般和快速,平均优化了一半,慢速效果也优化了 20 到 30% 之间,平时手势锁解锁时候,肯定速度很快。可见,节流的优化还是很明显的。
关键是,优化之后的流程性,没有受到任何影响。
这个节流函数最终还是出现了一个 bug:由于是延迟执行的,导致 e.preventDefault 失效,在手机浏览器向下滑会出现刷新的情况,这也算事件延迟的一个危害吧。
解决办法:在节流函数提前取消默认事件:
总结
大概花了三天左右的时间,将这个 H5 的手势解锁给完成,自己还是比较满意的,虽然可能达不到专业老师的认可,不过自己在做的过程中,学习到了很多新知识。
原文链接:http://bbs.jointforce.com/topic/26231
0 0
- 用 canvas 实现 Web 手势解锁
- 用 canvas 实现 Web 手势解锁
- canvas手势解锁思路
- 手势解锁代码实现
- 手势解锁的实现
- Android实现手势解锁
- 手势解锁实现PinLockView
- HTML5实现屏幕手势解锁
- android 手势解锁的实现
- 手势密码 图形解锁 实现
- canvas实现一个解锁demo
- 实现一个简单的手势解锁
- 【UI基础】手势解锁简单实现
- android利用paint 实现手势解锁
- Appium实现app手势密码解锁 LockViewPatter
- 手势解锁
- 手势解锁
- 手势解锁
- Dubbo性能优化
- HDU 1024 Let theBalloon Rise
- out/target/common/obj/JAVA_LIBRARIES/core-libart_intermediates/with-local/classes.dex] Error 41
- 加盐加密
- 升级10.11后使用CocoaPod出现-bash: pod: command not found 解决办法
- 用 canvas 实现 Web 手势解锁
- Android RecycleView出现大间隔问题分析
- Linux 的 core 文件
- PHP MVC初体验
- 如何从链表中删除重复数据
- POJ 2000 Gold Coins G++
- 对上拉电阻的理解
- 使用gem命令时出现的几种错误
- MATLAB R2014b Failed to start pool