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项目中实现手势密码的主要内容,如果未能解决你的问题,请参考以下文章
从片段中获取意图值后,我如何在 recyclerview 项目中实现单击