轻松创建自定义手势图案锁 - Vue 手势密码锁组件

说在前面

🎈Vue 手势密码锁组件是一个功能强大且易于集成的解决方案,旨在为您的应用程序提供安全的登录体验。该组件允许用户通过绘制特定的手势图案来解锁应用程序,取代传统的用户名和密码输入方式。通过引入图案手势锁,用户可以享受到更高的安全性,因为手势图案具有较高的复杂度和难以猜测性。同时,使用手势密码锁还可以提升用户体验,让登录过程更加便捷和流畅。Vue 手势密码锁组件支持高度定制化,您可以轻松地调整样式、主题和验证规则,以适应不同的应用场景。无论您是开发移动应用、网页应用还是桌面应用,Vue 手势密码锁组件都是您打造安全、时尚和高效的登录界面的理想选择。

效果展示

组件实现效果如下图:
JYeontu组件库 - Google Chrome 2022_5_30 1_01_58 00_00_00-00_00_30.gif

预览地址

http://jyeontu.xyz/jvuewheel/#/JAppsLock

实现步骤

完成一个组件需要几步?

1.组件设计

首先我们应该要知道我们要做怎样的组件,具备怎样的功能,这样才可以开始动手去实现。
功能上其实是已经很明确了,就是仿照手机上现有的图案锁来进行网页版组件开发。这里我们对入参和回调先进行一个大致的设计。

size

图案的尺寸,默认为3,即图案的大小为 3 * 3,4的话即为4 * 4;

showArrow

是否显示划线轨迹箭头,有的时候我们并不希望图案划到的轨迹箭头显示,这样的保密性会更高,所以这里需要一个开关来控制箭头的显示与否;

commit

commit为划动结束时的回调函数,我们可以在父组件接收到划动轨迹列表。

2.组件分析

接下来就需要对组件实现过程中使用到的关键技术点做一个分析了:

(1)触屏事件&鼠标移动事件

我们需要在页面上画出图案,那么我们肯定需要利用到网页的触屏事件和鼠标移动事件,鼠标移动事件主要是用于pc端,而在移动端使用时,我们则需要利用到网页的触屏事件。

(2)点之间的连线和箭头方向

我们需要在划到的相邻的两个点之间进行连线并用箭头标出其划线方向,这里我们需要借助一点数学三角函数的知识来计算,这里就不展开了,后面会对其进行分析解释。

(3)连线完回调获得滑动轨迹

这个时候我们需要监听鼠标抬起事件和触屏结束事件,在鼠标抬起或触屏结束的时候执行回调,将滑动的图案轨迹以数组的形式返回。

3.组件实现

(1)鼠标事件和触屏事件监听

首先我们应该先对鼠标事件和触屏事件进行监听,这样才可以捕捉到我们划动图案的轨迹。\

  • vue中的鼠标事件和触屏事件
    Vue中已经为我们定义了鼠标事件和触屏事件,我们可以直接这样使用:
@mousedown.prevent="mousedown()"
@touchstart.prevent="mousedown()"
@mouseover="mouseover(cInd)"
@touchmove="mouseover(cInd)"
  • JavaScript监听鼠标事件和触屏事件
    在JavaScript中我们需要对鼠标事件和触屏事件进行监听:
const content = document.getElementById(this.JAppsLockId);
content.addEventListener("mousedown", this.mousedown);
content.addEventListener("mouseup", this.mouseup);window.addEventListener("mouseup", this.mouseup);content.addEventListener("touchstart", this.mousedown);
content.addEventListener("touchend", this.mouseup);window.addEventListener("touchend", this.mouseup);content.addEventListener("dragstart", () => {});
content.addEventListener("touchmove", this.touchmove);
(2)鼠标事件和触屏事件定义
  • 鼠标按下 & 手指触屏开始
    在组件内鼠标按下或者手指触屏开始的时候,我们应该做一个标记,标记当前状态为鼠标按下状态。
mousedown() {this.isDown = true;this.choosePoints = [];this.removeLines();
},
  • 鼠标移动 & 触屏划动
    当当前为鼠标按下状态且鼠标在移动时,我们需要判断鼠标是否移动经过某一个点,这里的鼠标移动事件和触屏划动事件有点区别,需要分别定义。
mouseover(ind) {if (!this.isDown) return;if (this.choosePoints.includes(ind)) return;this.choosePoints.push(ind);
},
touchmove(event) {if (!this.isDown) return;if (this.pointsArea.length === 0) {this.initPointsArea();}const content = document.getElementById(this.JAppsLockId + "lock");let nx = event.targetTouches[0].pageX - content.offsetLeft;let ny = event.targetTouches[0].pageY - content.offsetTop;for (let i = 0; i < this.pointsArea.length; i++) {const item = this.pointsArea[i];const { x, y, r } = item;if (Math.pow(x - nx, 2) + Math.pow(y - ny, 2) <= r * r) {if (this.choosePoints.includes(i)) return;this.choosePoints.push(i);break;}}
},
(3)鼠标抬起和触屏划动结束回调

在鼠标抬起和触屏划动结束的时候需要进行回调,将当前划动过程中经过的图案轨迹输出。

mouseup() {if (!this.isDown) return;this.isDown = false;this.drawLine();this.$emit("commit", this.choosePoints);
},
(4)组件数据初始化

我们需要先确定当前组件的id,当父组件定义了子组件的id时则使用定义的id,否则则自动生成id

initData() {let id = this.id;if (id == "") {id = getUId();}this.JAppsLockId = id;
},
(5)图案数据初始化

我们需要根据传来的size参数来渲染不同尺寸的图案点阵。

initCell() {const id = this.JAppsLockId;const size = this.size;const content = document.getElementById(id);const cH = content.offsetHeight;const cW = content.offsetWidth;const cellH = (cH - 20 - size * 6 * 2) / size + "px";const cellW = (cW - 20 - size * 6 * 2) / size + "px";this.cellH = cellH;this.cellW = cellW;
}
(6)获取图案点阵的位置数据

我们可以先获取图案点阵的圆心坐标及半径,为后续进行判断计算作准备。

initPointsArea() {this.pointsArea === [];const cell = document.getElementsByClassName("j-apps-lock-cell")[0];for (let i = 0; i < this.size * this.size; i++) {const point = document.getElementById("point-" + i);const x =(point.offsetLeft + point.offsetWidth + point.offsetLeft) /2;const y =(point.offsetTop + point.offsetHeight + point.offsetTop) /2;const r = cell.offsetHeight / 2;this.pointsArea.push({ x, y, r });}
},
(7)图案连线
首先我们需要先计算好需要连线的两个图案的坐标。
drawLine() {const domPoints = this.getPoints();for (let i = 1; i < domPoints.length; i++) {const x1 =domPoints[i - 1].offsetWidth + domPoints[i - 1].offsetLeft;const x2 = domPoints[i].offsetWidth + domPoints[i].offsetLeft;const y1 =domPoints[i - 1].offsetHeight + domPoints[i - 1].offsetTop;const y2 = domPoints[i].offsetHeight + domPoints[i].offsetTop;this.createLine(x1,x2,y1,y2,domPoints[i - 1],domPoints[i]);}
}
通过计算好的坐标数据,生成对应的线段
createLine(x1, x2, y1, y2, p1, p2) {let line = document.createElement("span");line.classList.add("j-apps-lock-line");line.style.position = "absolute";line.style.display = "flex";line.style.left = "50%";line.style.top = "50%";line.style.margin = "center";line.style.width = Math.max(Math.abs(x2 - x1), 2) + "px";line.style.height = Math.max(Math.abs(y2 - y1), 2) + "px";line.style.backgroundColor = "gray";if (this.showArrow)line.appendChild(this.createArrow(x1, x2, y1, y2));if (x1 != x2 && y1 != y2) {const x = Math.abs(x1 - x2);const y = Math.abs(y1 - y2);line.style.height = Math.sqrt(x * x + y * y) + "px";line.style.width = "2px";let angle = (Math.atan(x / y) * 180) / Math.PI;if ((x2 > x1 && y2 > y1) || (x2 < x1 && y2 < y1))angle = "-" + angle;line.style.transform = `rotate(${angle}deg)`;line.style.transformOrigin = "left top";if (y2 > y1) p1.appendChild(line);else p2.appendChild(line);} else if (x2 > x1 || y2 > y1) {p1.appendChild(line);} else {p2.appendChild(line);}return line;
},

由上面代码我们可以看到,在连线的绘制中,我们使用到了css中的旋转属性,其旋转角度是使用Math.atan计算出来的,所以我们需要先对三角函数进行一定了解。

javascript中计算三角函数

image.png

三角函数的定义
正弦(sin)      sinA = a / c       sinθ = y / r
余弦(cos)     cosA = b / c      cosθ = y / r
正切(tan)      tanA = a / b      tanθ = y / x
余切(cot)      cotA = b / a      cotθ = x / y
js中计算三角函数用Math.sin()等静态方法,参数为弧度
角度与弧度都是角的度量单位

角度:两条射线从圆心向圆周射出,形成一个夹角和夹角正对的一段弧。当这段弧长正好等于圆周长的360分之一时,两条射线的夹角的大小为1度。
弧度:两条射线从圆心向圆周射出,形成一个夹角和夹角正对的一段弧。当这段弧长正好等于圆的半径时,两条射线的夹角大小为1弧度。

1弧度时,弧长等于半径,那弧长是半径的倍数就是弧度了
弧度 = 弧长 / 半径
弧长 = 弧度 * 半径
弧长 = (角度 / 360) * 周长
角度与弧度换算

角度 = 弧长 / 周长 = 弧长/(2πr) = 弧度*r/(2πr) = 弧度/(2π)
弧度 = 弧长 / 半径 = [(角度 / 360) * 周长] / 半径 =[ (角度 / 360) * 2πr] / r = 角度 * π / 180

js计算三角函数
var sin30 = Math.sin(30 * Math.PI / 180)
console.log(sin30);  //0.49999999999999994var cos60 = Math.cos(60 * Math.PI / 180)
console.log(cos60);  //0.5000000000000001var tan45 = Math.tan(45 * Math.PI / 180)
console.log(tan45);  //0.9999999999999999var asin30 = Math.round(Math.asin(sin30) * 180 / Math.PI)
console.log(asin30); //30var acos60 = Math.round(Math.acos(cos60) * 180 / Math.PI)
console.log(acos60); //60var atan45 = Math.round(Math.atan(tan45) * 180 / Math.PI)
console.log(atan45); //45
(8)图案连线轨迹箭头

我们只需要将箭头元素添加到线段元素中,作为线段元素的子元素,我们便不用单独对箭头元素的旋转角度进行处理。

createArrow(x1, x2, y1, y2) {let arrow = document.createElement("span");arrow.classList.add("j-apps-lock-arrow");arrow.style.position = "relative";arrow.style.margin = "auto";arrow.style.fontSize = "1.5rem";arrow.style.zIndex = "10";arrow.style.display = "block";arrow.style.minWidth = "1.4rem";arrow.style.textAlign = "center";if (y1 === y2) {arrow.innerText = x1 > x2 ? "<" : ">";arrow.style.top = "-0.8rem";} else {arrow.innerText = y1 > y2 ? "∧" : "∨";arrow.style.left = "-0.65rem";}return arrow;
},

4.组件使用

image.png

<template><div class="content"><j-apps-lock @commit="commit" size="4"></j-apps-lock></div>
</template>
<script>export default {data() {return {}},methods:{commit(password) {this.$JToast(password);}}}
</script>

组件库引用

这里我将这个组件打包进了自己的一个组件库,并将其发布到了npm上,有需要的同学也可以直接引入该组件进行使用。
引入教程可以看这里:http://jyeontu.xyz/jvuewheel/#/installView
引入后即可直接使用。

源码地址

Gitee源码

Gitee源码:gitee.com/zheng_yongt…

觉得有帮助的同学可以帮忙给我点个star,感激不尽~~~
有什么想法或者改良可以给我提个pr,十分欢迎~~~
有什么问题都可以在评论告诉我~~~

公众号

关注公众号『前端也能这么有趣』发送 组件库即可获取源码。

组件使用

目前该组件库已经发布到npm,除了手势密码锁之外还有其他许多好玩的组件,后续会继续维护,源码也已经开源,感兴趣的朋友可以瞧瞧,觉得有点意思的可以顺手点个star

组件文档:http://jyeontu.xyz/jvuewheel/#/installView

组件仓库:https://gitee.com/zheng_yongtao/jyeontu-component-warehouse.git

说在后面

🎉 这里是 JYeontu,现在是一名前端工程师,有空会刷刷算法题,平时喜欢打羽毛球 🏸 ,平时也喜欢写些东西,既为自己记录 📋,也希望可以对大家有那么一丢丢的帮助,写的不好望多多谅解 🙇,写错的地方望指出,定会认真改进 😊,偶尔也会在自己的公众号『前端也能这么有趣』发一些比较有趣的文章,有兴趣的也可以关注下。在此谢谢大家的支持,我们下文再见 🙌。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/266389.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2023年团体程序设计天梯赛——总决赛题

F-L1-1 最好的文档 有一位软件工程师说过一句很有道理的话&#xff1a;“Good code is its own best documentation.”&#xff08;好代码本身就是最好的文档&#xff09;。本题就请你直接在屏幕上输出这句话。 输入格式&#xff1a; 本题没有输入。 输出格式&#xff1a; 在一…

对象的生离死别

对象的生离死别 实验介绍 在构建一个类时&#xff0c;一般情况下需要编写构造函数、拷贝构造函数以及析构函数&#xff0c;这将直接影响程序的运行。而初始化列表是在调用构造函数时初始化参数的方式。 一个对象从实例化到销毁的历程&#xff1a; 知识点 内存分区构造函数exp…

C语言经典错误总结(一)

注&#xff1a;本文是结合《C陷阱和缺陷》所写&#xff01; 一.和 我们都知道在C语言中表示赋值操作符&#xff0c;表示比较&#xff0c;那么你知道为啥单等号为&#xff0c;双等号为比较吗&#xff1f; 这里扩展下&#xff1a;因为在C语言中赋值操作符相对于比较符号较常出…

deepface:实现人脸的识别和分析

deepface介绍 deepface能够实现的功能 人脸检测&#xff1a;deepface 可以在图像中检测出人脸的位置&#xff0c;为后续的人脸识别任务提供基础。 人脸对齐&#xff1a;为了提高识别准确性&#xff0c;deepface 会将检测到的人脸进行对齐操作&#xff0c;消除姿态、光照和表…

美团一面凉凉,怒刷3000前端面试题逆袭字节,含泪分享面经

前言 本人二本大学科班出身&#xff0c;首次面试美团一面凉了。先分享一下美团的凉经&#xff0c;希望大家做好避坑准备&#xff1a; 一面&#xff1a; 1.http和https的区别&#xff0c;非对称加密和对称加密的原理&#xff0c;还有http2和http1的区别&#xff08;多路复用、…

日志门面slf4j和各日志框架

简介 简单日志门面(Simple Logging Facade For Java) SLF4J主要是为了给Java日志访问提供一套标准、规范的API框架&#xff0c; 其主要意义在于提供接口&#xff0c;具体的实现可以交由其他日志框架&#xff0c;如log4j、logback、log4j2。 对于一般的Java项目而言&#xff…

31、卷积 - 参数 dilation 以及空洞卷积

在卷积算法中,还有一个不常见的参数叫做dilation(中文:膨胀)。 很多同学可能没听说过这个参数,下面看看这个参数有什么作用,用来控制什么的。 我们还是放这个经典的卷积运算图,图中是看不出 dilation 这个参数的存在的。 如果再换一张图呢,发现两图的区别了吗? 没错…

用微元思想求解三重积分——基于Matlab

仅作自己学习使用 1. 题目 求解下列三重积分&#xff0c;其中A&#xff0c;μ&#xff0c;r都是常数。 求解的准确性可以用下式进行评估&#xff1a; 听过考研数一张宇课程的朋友应该指导&#xff0c;求解三重积分就是就一个面包&#xff0c;我们将面包无限细分为一个小块&a…

【JAVA基础】----第一天

【JAVA基础】----第一天 命名规则注释方式对HelloWorld代码进行解释常量&#xff0c;进制转换和机器码展现计算过程常量类型1.字符串常量2.整数常量 提供了四种表现形式2.1 二进制2.2 八进制2.3 十进制2.4 十六进制2.5 进制之间的转化2.5.1 其他进制转化为十进制2.5.2 十进制转…

天津大数据培训机构品牌 数据分析师的发展方向

大数据专业还是有一定难度的&#xff0c;毕竟大数据开发技术所包含的编程技术知识是比较杂且多的如果是计算机专业的学生或者自身有一定基础的人学&#xff0c;相对来说会比较容易&#xff0c;但对于零基础小伙伴学习来说&#xff0c;想要学习大数据&#xff0c;难度还是很高的…

基于SpringBoot+Vue前后端分离的商城管理系统(Java毕业设计)

大家好&#xff0c;我是DeBug&#xff0c;很高兴你能来阅读&#xff01;作为一名热爱编程的程序员&#xff0c;我希望通过这些教学笔记与大家分享我的编程经验和知识。在这里&#xff0c;我将会结合实际项目经验&#xff0c;分享编程技巧、最佳实践以及解决问题的方法。无论你是…

RT-Thread 工程创建(1)

方式一&#xff0c; 利用已经有的bsp进行创建 距离BearPi IOT Std 板 1. 下载 RT-Thread 官方 Env工具a. 下载 [Env 工具下载](https://www.rt-thread.org/download.html#download-rt-thread-env-tool) &#xff0c; 并解压缩b. 将env注册到系统中, 这样就在右键菜单中出现&am…