vue2 手勢密碼
阿新 • • 發佈:2018-11-26
元件:
<template> <div class="masks" v-show="currentValue"> <div class="gesturePwd"> <div class="box"> <h4 ref="gestureTitle" class="gestureTitle">請繪製您的圖形密碼</h4> <a class="reset" ref="updatePassword" @click="updatePassword()">重置密碼</a> <a class="close" ref="updatePassword" @click="closePwd(false)">關閉</a> <canvas ref="canvas"></canvas> </div> </div> </div> </template> <script> export default { props: { value: { type: Boolean, default: false }, }, data() { return { currentValue: false, ctx: '', width: 0, height: 0, devicePixelRatio: 0, chooseType: '', r: '',// 公式計算 lastPoint: [], arr: [], restPoint: [], pswObj: {step: 2}, canvas: '' } }, watch: { value: { handler: function (val) { this.currentValue = val }, immediate: true }, currentValue(val) { this.$emit(val ? 'on-show' : 'on-hide') this.$emit('input', val) } }, created() { if (typeof this.value !== 'undefined') { this.currentValue = this.value } }, mounted() { this.setChooseType(3); }, methods: { closePwd(bol) { this.$emit("handPwd",bol); this.currentValue = false; }, drawCle(x, y) { // 初始化解鎖密碼面板 小圓圈 this.ctx.strokeStyle = '#87888a';//密碼的點點預設的顏色 this.ctx.lineWidth = 2; this.ctx.beginPath(); this.ctx.arc(x, y, this.r, 0, Math.PI * 2, true); this.ctx.closePath(); this.ctx.stroke(); }, drawPoint(style) { // 初始化圓心 for (var i = 0; i < this.lastPoint.length; i++) { this.ctx.fillStyle = style; this.ctx.beginPath(); this.ctx.arc(this.lastPoint[i].x, this.lastPoint[i].y, this.r / 2.5, 0, Math.PI * 2, true); this.ctx.closePath(); this.ctx.fill(); } }, drawStatusPoint(type) { // 初始化狀態線條 for (var i = 0; i < this.lastPoint.length; i++) { this.ctx.strokeStyle = type; this.ctx.beginPath(); this.ctx.arc(this.lastPoint[i].x, this.lastPoint[i].y, this.r, 0, Math.PI * 2, true); this.ctx.closePath(); this.ctx.stroke(); } }, drawLine(style, po, lastPoint) {//style:顏色 解鎖軌跡 this.ctx.beginPath(); this.ctx.strokeStyle = style; this.ctx.lineWidth = 3; this.ctx.moveTo(this.lastPoint[0].x, this.lastPoint[0].y); for (var i = 1; i < this.lastPoint.length; i++) { this.ctx.lineTo(this.lastPoint[i].x, this.lastPoint[i].y); } this.ctx.lineTo(po.x, po.y); this.ctx.stroke(); this.ctx.closePath(); }, createCircle() {// 建立解鎖點的座標,根據canvas的大小來平均分配半徑 var n = this.chooseType; var count = 0; this.r = this.ctx.canvas.width / (2 + 4 * n);// 公式計算 this.lastPoint = []; this.arr = []; this.restPoint = []; var r = this.r; for (var i = 0; i < n; i++) { for (var j = 0; j < n; j++) { count++; var obj = { x: j * 4 * r + 3 * r, y: i * 4 * r + 3 * r, index: count }; this.arr.push(obj); this.restPoint.push(obj); } } this.ctx.clearRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height); for (var i = 0; i < this.arr.length; i++) { this.drawCle(this.arr[i].x, this.arr[i].y); } }, getPosition(e) {// 獲取touch點相對於canvas的座標 var rect = e.currentTarget.getBoundingClientRect(); var po = { x: (e.touches[0].clientX - rect.left) * this.devicePixelRatio, y: (e.touches[0].clientY - rect.top) * this.devicePixelRatio }; return po; }, update(po) {// 核心變換方法在touchmove時候呼叫 this.ctx.clearRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height); for (var i = 0; i < this.arr.length; i++) { // 每幀先把面板畫出來 this.drawCle(this.arr[i].x, this.arr[i].y); } this.drawPoint('#27AED5');// 每幀花軌跡 this.drawStatusPoint('#27AED5');// 每幀花軌跡 this.drawLine('#27AED5', po, this.lastPoint);// 每幀畫圓心 for (var i = 0; i < this.restPoint.length; i++) { if (Math.abs(po.x - this.restPoint[i].x) < this.r && Math.abs(po.y - this.restPoint[i].y) < this.r) { this.drawPoint(this.restPoint[i].x, this.restPoint[i].y); this.lastPoint.push(this.restPoint[i]); this.restPoint.splice(i, 1); break; } } }, checkPass(psw1, psw2) {// 檢測密碼 var p1 = '', p2 = ''; for (var i = 0; i < psw1.length; i++) { p1 += psw1[i].index + psw1[i].index; } for (var i = 0; i < psw2.length; i++) { p2 += psw2[i].index + psw2[i].index; } return p1 === p2; }, storePass(psw) {// touchend結束之後對密碼和狀態的處理 if (this.pswObj.step == 1) { if (this.checkPass(this.pswObj.fpassword, psw)) { this.pswObj.step = 2; this.pswObj.spassword = psw; this.$refs.gestureTitle.innerHTML = '密碼儲存成功'; this.drawStatusPoint('#2CFF26'); this.drawPoint('#2CFF26'); window.localStorage.setItem('passwordxx', JSON.stringify(this.pswObj.spassword)); window.localStorage.setItem('chooseType', this.chooseType); } else { this.$refs.gestureTitle.innerHTML = '兩次不一致,重新輸入'; this.drawStatusPoint('red'); this.drawPoint('red'); delete this.pswObj.step; } } else if (this.pswObj.step == 2) { if (this.checkPass(this.pswObj.spassword, psw)) { var gestureTitle = this.$refs.gestureTitle; gestureTitle.style.color = "#2CFF26"; gestureTitle.innerHTML = '解鎖成功'; this.drawStatusPoint('#2CFF26');//小點點外圈高亮 this.drawPoint('#2CFF26'); this.drawLine('#2CFF26', this.lastPoint[this.lastPoint.length - 1], this.lastPoint);// 每幀畫圓心 this.closePwd(true); } else if (psw.length < 4) { this.drawStatusPoint('red'); this.drawPoint('red'); this.drawLine('red', this.lastPoint[this.lastPoint.length - 1], this.lastPoint);// 每幀畫圓心 var gestureTitle = this.$refs.gestureTitle; gestureTitle.style.color = "red"; gestureTitle.innerHTML = '請連線4個點'; } else { this.drawStatusPoint('red'); this.drawPoint('red'); this.drawLine('red', this.lastPoint[this.lastPoint.length - 1], this.lastPoint);// 每幀畫圓心 var gestureTitle = this.$refs.gestureTitle; gestureTitle.style.color = "red"; gestureTitle.innerHTML = '密碼錯誤'; } } else { this.pswObj.step = 1; this.pswObj.fpassword = psw; this.$refs.gestureTitle.innerHTML = '再次輸入'; } }, makeState() { if (this.pswObj.step == 2) { this.$refs.updatePassword.style.display = 'block'; var gestureTitle = this.$refs.gestureTitle; gestureTitle.style.color = "#87888a"; gestureTitle.innerHTML = '請解鎖'; } else if (this.pswObj.step == 1) { this.$refs.updatePassword.style.display = 'none'; } else { this.$refs.updatePassword.style.display = 'block'; } }, setChooseType(type) { this.chooseType = type; this.init(); }, updatePassword() { window.localStorage.removeItem('passwordxx'); window.localStorage.removeItem('chooseType'); this.pswObj = {}; this.$refs.gestureTitle.innerHTML = '繪製解鎖圖案'; this.reset(); }, initDom() { this.chooseType = Number(window.localStorage.getItem('chooseType')) || 3; this.devicePixelRatio = window.devicePixelRatio || 1; var canvas = this.$refs.canvas; var width = this.width || 320; var height = this.height || 320; // 高清屏鎖放 canvas.style.width = width + "px"; canvas.style.height = height + "px"; canvas.height = height * this.devicePixelRatio; canvas.width = width * this.devicePixelRatio; }, init() { this.initDom(); this.pswObj = window.localStorage.getItem('passwordxx') ? { step: 2, spassword: JSON.parse(window.localStorage.getItem('passwordxx')) } : {}; this.lastPoint = []; this.makeState(); this.touchFlag = false; this.canvas = this.$refs.canvas; this.ctx = this.canvas.getContext('2d'); this.createCircle(); this.bindEvent(); }, reset() { this.makeState(); this.createCircle(); }, bindEvent() { var self = this; this.canvas = this.$refs.canvas; this.canvas.addEventListener("touchstart", function (e) { e.preventDefault();// 某些android 的 touchmove不宜觸發 所以增加此行程式碼 var po = self.getPosition(e); for (var i = 0; i < self.arr.length; i++) { if (Math.abs(po.x - self.arr[i].x) < self.r && Math.abs(po.y - self.arr[i].y) < self.r) { self.touchFlag = true; self.drawPoint(self.arr[i].x, self.arr[i].y); self.lastPoint.push(self.arr[i]); self.restPoint.splice(i, 1); break; } } }, false); this.canvas.addEventListener("touchmove", function (e) { if (self.touchFlag) { self.update(self.getPosition(e)); } }, false); this.canvas.addEventListener("touchend", function (e) { if (self.touchFlag) { self.touchFlag = false; self.storePass(self.lastPoint); setTimeout(function () { self.reset(); }, 1000); } }, false); } } } </script> <style scoped> .masks { text-align: center; position: fixed; z-index: 1000; top: 0; right: 0; left: 0; bottom: 0; background: rgba(0, 0, 0, 0.6); } .gesturePwd { position: fixed; z-index: 5000; width: 100%; height: 100%; top: 50%; left: 50%; -webkit-transform: translate(-50%, -50%); transform: translate(-50%, -50%); text-align: center; border-radius: 3px; overflow: hidden; background-color: #000; } .gestureTitle { color: #87888a; margin-top: 85px; font-size: 20px; font-weight: normal; } .box{ position: absolute; top:0; left:0; right:0; bottom:0; } .box a{ position: absolute; top: 5px; color:#fff; font-size: 13px; display:block; } a.reset{ left: 5px; } a.close{ right :5px; } .box canvas{ background-color: #000; display: inline-block; margin-top: 76px; width: 320px; height: 320px; } </style>
呼叫:
<template> <div class="hello"> <button @click="showClicked" style="width:90px;height:50px;font-size:16px;background-color:#eee">手勢密碼</button> <pwd v-model="showPwd" @handPwd="handPwd"></pwd> </div> </template> <script> import pwd from '@/components/pwd' export default { name: 'hello', data() { return { showPwd: false } }, methods: { showClicked() { this.showPwd = true; }, handPwd(val) { console.log(val); } }, components: { pwd }, } </script>