vue项目中实现手势密码

Posted wuyufei

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了vue项目中实现手势密码相关的知识,希望对你有一定的参考价值。

本来是写在一个页面中的,但后来一想创建和登录的时候都得用,干脆就写成组件吧。自知写的很差,还望各位大佬指教一二

父组件中不传值的话就默认是创建手势密码(创建时密码长度不能小于4),需要输入两次,两次密码必须一致

如果是登录,父组件就把密码传给子组件,子组件就会根据密码判断当前输入是否正确,具体执行时请看控制台

演示:

父组件未传值,此时是创建手势密码

技术图片技术图片

 

 

 

 登录时,父组件穿的值为<gestureUnlock :fatherPassword="[1,2,3,4,5]"></gestureUnlock>

下面分别是密码正确和密码错误的情况

技术图片

 

 

技术图片

 

 

 

 

密码组件:

  1 <!--
  2 组件用法:
  3   由父组件传来fatherPassword,不传的话就默认是一个空数组,则此时就是创建密码,需要输入两次相同的密码完成创建
  4   登录时传过来的值是一个长度大于等于4的数组,则此时就是登录,只判断一次
  5 不足之处:
  6   这里圆的半径是25.5,不能写固定值,但我又不想算
  7   还未处理创建或登录成功之后的事情
  8   还有一个就是canvas的高度问题,这里是用的设备高度的86%,如果九个点下面紧挨着有其他按钮的话是点不了的,因为被canvas覆盖了
  9 -->
 10 <template>
 11   <div class="gestureUnlock">
 12     <div class="gesture">
 13       <ul>
 14         <li ref="selectLi" v-for="(item, index) in list" :key="item.id"
 15             :class="{‘selectedOuter‘: password.indexOf(index) !== -1 ? true : false,
 16             ‘selectedOuter2‘: password.indexOf(index) !== -1 && redStyle ? true : false}">
 17           <span :class="{‘selectedInside‘: password.indexOf(index) !== -1 ? true : false,
 18                 ‘selectedInside2‘: password.indexOf(index) !== -1 && redStyle ? true : false}">
 19             <!--圆心-->
 20             <i ref="selectLiO"></i>
 21           </span>
 22         </li>
 23       </ul>
 24     </div>
 25     <canvas id="canvas"  @touchstart="start" @touchmove="move" @touchend="end">此浏览器不支持canvas</canvas>
 26   </div>
 27 </template>
 28 
 29 <script>
 30   export default {
 31     name: "GestureUnlock",
 32     data () {
 33       return {
 34         list: [
 35           {id:0, top: 0, left: 0, isSelected: false},
 36           {id:1, top: 0, left: 0, isSelected: false},
 37           {id:2, top: 0, left: 0, isSelected: false},
 38           {id:3, top: 0, left: 0, isSelected: false},
 39           {id:4, top: 0, left: 0, isSelected: false},
 40           {id:5, top: 0, left: 0, isSelected: false},
 41           {id:6, top: 0, left: 0, isSelected: false},
 42           {id:7, top: 0, left: 0, isSelected: false},
 43           {id:8, top: 0, left: 0, isSelected: false},
 44         ],
 45         left: [], // 圆心x坐标
 46         top: [], // 圆心y坐标
 47         password: [], // 用来存储创建密码,从上到下,从左到右依次是123,456,789
 48         cas: ‘‘, // 画笔
 49         clientWidth: 0,
 50         clientHeight: 0,
 51         isCorrect: true, // 密码是否且是否正确
 52         redStyle: false, // li样式是否为红色
 53         createPassword: Array // 这个用来存一下父组件传过来的fatherPassword,因为子组件不能直接修改父组件传过来的值
 54       }
 55     },
 56     props: {
 57       // 存储确认密码,变成组件后由父组件传过来,默认是空数组
 58       fatherPassword: {
 59         default: ()=>[], // 这个地方不能写成default: []
 60         type: Array
 61       }
 62     },
 63     created () {
 64       // 存一下父组件传过来的fatherPassword,因为子组件不能直接修改父组件传过来的值
 65       this.createPassword = this.fatherPassword
 66     },
 67     methods: {
 68       // 手指点下
 69       start (e) {
 70         if(e.touches.length > 1 || e.scale && e.scale !== 1) { // 多点触碰或者缩放
 71           console.log(‘这样不行‘, e)
 72         } else {
 73           console.log(‘start‘, e.touches[0].pageX , e.touches[0].pageY)
 74         }
 75       },
 76       // 手指移动
 77       move (e) {
 78         // this.cas.clearRect(0,0,this.clientWidth,100);
 79         let nowLeft = e.touches[0].pageX
 80         let nowTop = e.touches[0].pageY
 81         for (var i = 0; i < this.left.length; i++) {
 82           // 圆心坐标
 83           let oLeft = this.left[i]
 84           let oTop = this.top[i]
 85           // 这样判断的是个正方形,不是圆形
 86           if((oLeft - 25.5) <= nowLeft && nowLeft <= (oLeft + 25.5) && (oTop - 25.5) <= nowTop && nowTop <= (oTop + 25.5)) {
 87             if (this.password.length === 0 && this.password.indexOf(i) === -1) {
 88               this.password.push(i) // 直接存进密码
 89             } else if(this.password.indexOf(i) === -1){
 90               console.log(‘连中的值:‘, this.password[this.password.length - 1])
 91               let value = this.password[this.password.length - 1] // 根据此值(下标)找出对应的this.left和this.top
 92               // value是上一个点的值,i是当前连接点的值
 93               // 1-9 9-1、3-7 7-3、2-8 8-2、4-6 6-4
 94               if (i === 0 && value === 8 || i === 8 && value === 0 ||
 95                 i === 2 && value === 6 || i === 6 && value === 2 ||
 96                 i === 1 && value === 7 || i === 7 && value === 1 ||
 97                 i === 3 && value === 5 || i === 5 && value === 3) {
 98                 // this.password中存的是下标
 99                 if (this.password.indexOf(4) === -1) {this.password.push(4)}
100               } else if(i === 2 && value === 0 || i === 0 && value === 2) { // 1-3  3-1
101                 if (this.password.indexOf(1) === -1) {this.password.push(1)}
102               } else if(i === 6 && value === 8 || i === 8 && value === 6){ // 7-9  9-7
103                 if (this.password.indexOf(7) === -1) {this.password.push(7)}
104               }else if(i === 0 && value === 6 || i === 6 && value === 0){ // 1-7  7-1
105                 if (this.password.indexOf(3) === -1) {this.password.push(3)}
106               }else if(i === 2 && value === 8 || i === 8 && value === 2){ // 3-9  9-3
107                 if (this.password.indexOf(5) === -1) {this.password.push(5)}
108               }
109               // 存密码
110               this.password.push(i)
111             }
112           }
113         }
114         this.paint(nowLeft, nowTop, true)
115       },
116       // 画线的方法
117       paint (nowX, nowY, color) {
118         this.cas.clearRect(0,0,this.clientWidth,this.clientHeight); // 每次画都清空整个画布
119         this.cas.beginPath();
120         for (var i = 0; i < this.password .length; i++) {
121           this.cas.lineTo(this.left[this.password [i]], this.top[this.password [i]]); // 从这个开始
122         }
123         this.cas.lineTo(nowX, nowY);
124         if (!color) {
125           this.cas.strokeStyle = ‘#ff4b4b‘
126         } else {
127           this.cas.strokeStyle = ‘#498bcb‘
128         }
129         this.cas.lineJoin = "round"
130         this.cas.lineWidth = 2;
131         this.cas.stroke();
132         // 清除li内圆形区域的线条
133         this.password.forEach((item) => {
134           this.clearArcFun(this.left[item], this.top[item], 25)
135         })
136       },
137       // 清除li内的圆形区域
138       clearArcFun (centerX, centerY, radius) {
139         var stepClear = 1; //别忘记这一步
140         var _this = this
141         clearArc(centerX, centerY, radius);
142         function clearArc(x, y, radius){ // 圆心x,y,半径radius
143           var calcWidth = radius - stepClear;
144           var calcHeight = Math.sqrt(radius * radius - calcWidth * calcWidth);
145           var posX = x - calcWidth;
146           var posY = y - calcHeight;
147           var widthX = 2 * calcWidth;
148           var heightY = 2 * calcHeight;
149           if(stepClear <= radius){
150             _this.cas.clearRect(posX, posY, widthX, heightY);
151             stepClear += 1;
152             clearArc(x, y, radius);
153           }
154         }
155       },
156       // 手指松开
157       end () {
158         console.log(‘end‘, this.password)
159         if (this.createPassword.length === 0) { // 创建密码的第一次
160           if(this.password.length >= 4) {
161             // 此时再调用一次paint,传undefined, undefined,避免最后一条多余的线出现
162             this.paint(undefined, undefined, true)
163             // 不变红
164             this.redStyle = false
165             this.createPassword = this.password
166             // 500ms后清空样式
167             console.log(‘第一次设置密码createPassword:‘, this.createPassword)
168             console.log(‘第一次设置密码password:‘, this.password)
169             setTimeout(() => {
170               this.password = []
171               this.cas.clearRect(0,0,this.clientWidth,this.clientHeight);
172             }, 500)
173           } else {
174             console.log(‘创建密码时长度小于4‘)
175             this.paint(undefined, undefined, false)
176             // 长度小于4样式为红色
177             this.redStyle = true
178             // 清空画布,颜色变正常,不然下次输入还是红色
179             setTimeout(() => {
180               this.password = []
181               this.cas.clearRect(0,0,this.clientWidth,this.clientHeight);
182               this.redStyle = false // 颜色变蓝,不然下次输入还是红色
183             }, 500)
184           }
185         } else { // 创建密码的第二次 或者 登录,不管是啥反正都是拿password(第一次输入的密码)和createPassword比较
186           console.log(‘createPassword.length不为0,进入密码比较环节‘)
187           console.log(‘createPassword:‘, this.createPassword)
188           console.log(‘password:‘, this.password)
189           if (this.password.toString() === this.createPassword.toString()) {
190             // 设置/登录成功
191             console.log(‘设置/登录成功‘)
192             setTimeout(() => {
193               this.password = []
194               this.cas.clearRect(0,0,this.clientWidth,this.clientHeight);
195               this.redStyle = false // 没true好像就可以没有false,加上吧保险一点
196             }, 500)
197           } else {
198             this.paint(undefined, undefined, false)
199             // 两次输入不一致/密码不正确 样式为红色
200             this.redStyle = true // 有true下面必得有false
201             console.log(‘失败‘)
202             // 清空画布,颜色变蓝
203             setTimeout(() => {
204               this.password = [] // 还有蓝色是因为前几个存在于那个数组,得把password清空
205               this.cas.clearRect(0,0,this.clientWidth,this.clientHeight);
206               this.redStyle = false
207               console.log(this.redStyle)
208             }, 500)
209           }
210         }
211       }
212     },
213     mounted() {
214       // 获取到的是每个方块的左上角的位置,再加上25.5就是圆心坐标
215       for (let i = 0; i < this.$refs.selectLiO.length; i++) {
216         this.left.push(this.$refs.selectLiO[i].getBoundingClientRect().left)
217         this.top.push(this.$refs.selectLiO[i].getBoundingClientRect().top)
218       }
219       console.log(this.left)
220       console.log(this.top)
221       this.clientWidth = document.documentElement.clientWidth
222       this.clientHeight = document.documentElement.clientHeight
223       console.log(‘设备宽高:‘, this.clientWidth, this.clientHeight)
224       this.cas = document.getElementById(‘canvas‘).getContext(‘2d‘);
225       // 这个地方我也不知道为什么要用canvas而不是this.cas
226       canvas.width = this.clientWidth;
227       canvas.height = this.clientHeight*0.86;
228     }
229   }
230 </script>
231 
232 <style lang="less" scoped>
233   .gestureUnlock{
234     margin: 0 auto;
235   }
236   .gesture{
237     margin: 1.0rem auto 0;
238     ul{
239       margin: auto;
240       display: flex;
241       width: 8.88rem;
242       height: 8.88rem;
243       justify-content: space-between;
244       align-content: space-between;
245       flex-wrap: wrap;
246       li{
247         display: flex;
248         align-items:center;
249         justify-content:center;
250         margin: 0.45rem 0.45rem;
251         border-radius: 50%;
252         width: 1.2rem;
253         height: 1.2rem;
254         border: 0.08rem solid #e0e0e0;
255         /*宽度是1.2rem,边框是0.08rem,所以半径是0.68rem,1rem=37.5px,所以0.68x37.5 = 25.5px*/
256         span{
257           display: flex;
258           align-items:center;
259           justify-content:center;
260           width: 0.40rem;
261           height: 0.40rem;
262           border-radius: 50%;
263           i{
264             display: inline-block;
265             width: 1px;
266             height: 1px;
267           }
268         }
269       }
270       /*被选中的样式*/
271       .selectedOuter{
272         border: 0.08rem solid #498bcb;
273         .selectedInside{
274           background: #498bcb;
275         }
276       }
277       .selectedOuter2{
278         border: 0.08rem solid #ff4b4b;
279         .selectedInside2{
280           background: #ff4b4b;
281         }
282       }
283     }
284   }
285   #canvas{
286     position: fixed;
287     top:0;
288     left: 0;
289     /*border:1px solid #42b983;*/
290     /*background: rgba(0,0,0,0.1);*/
291   }
292 </style>

 

 

父组件调用:

 1 <template>
 2   <div class="createGesture">
 3     <div class="picture">
 4       <img src="../../assets/images/standard.png" >
 5     </div>
 6     <div class="words">
 7       <p>Confirm gesture password</p>
 8       <p>Draw a pattern connecting at least four dots.</p>
 9     </div>
10     <!--此页面是创建密码,需要输入两次,组件不传值,fatherPassword默认是一个空数组-->
11     <gestureUnlock></gestureUnlock>
12     <!--下面这是模拟登录时传密码过去-->
13     <!--<gestureUnlock :fatherPassword="[1,2,3,4,5]"></gestureUnlock>-->
14     <div class="bottom">
15       <p>You may create the gesture password later in Settings.</p>
16       <button>Skip</button>
17     </div>
18   </div>
19 </template>
20 
21 <script>
22   import gestureUnlock from ‘../../components/gestureUnlock‘
23   export default {
24     name: "createGesture",
25     components: {
26       gestureUnlock
27     }
28   }
29 </script>
30 
31 <style lang="less" scoped>
32   .createGesture{
33     padding: 0 0.5rem;
34     height: 100%;
35   }
36   .picture{
37     padding-top: 0.533rem;
38     text-align: center;
39     img {
40       /*width: 3rem;*/
41       height: 3rem;
42     }
43   }
44   .words{
45     text-align: center;
46     color: #498bcb;
47     p:nth-child(1) {
48       margin: 0.267rem 0;
49       font-size: 0.723rem;
50     }
51     p:nth-child(2) {
52       font-size: 0.373rem;
53     }
54   }
55   .bottom{
56     position: fixed;
57     bottom: 0;
58     left: 0;
59     width: 100%;
60     p{
61       padding: 0 0.5rem;
62       text-align: left;
63     }
64     button{
65       margin: 0.353rem 0 0.337rem;
66       width: 93%;
67       height: 0.933rem;
68       border-radius: 10px;
69       background-image: linear-gradient(#fff, #f5f5f5);
70       border: 1px solid #93bfe6;
71       box-shadow: 0 0 2px #ececec;
72       color: #498bcb;
73     }
74   }
75 </style>

 

以上是关于vue项目中实现手势密码的主要内容,如果未能解决你的问题,请参考以下文章

Vue项目中实现用户登录及token验证

Vue项目中实现用户登录及token验证

Vue项目中实现用户登录及token验证

从片段中获取意图值后,我如何在 recyclerview 项目中实现单击

在我的 laravel 项目中实现 vue.js 代码时遇到问题

Vue项目中实现用户登录及token验证的实现方法