html实现手势密码
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>手势密码</title><style>body {font-family: Arial, sans-serif;background-color: #e2e2e2;margin: 0;padding: 0;}.draw-container {width: 100%;height: 100vh;display: flex;flex-direction: column;align-items: center;justify-content: flex-start;}.message-tip {height: 8vh;background: #f2f2f2;text-align: center;line-height: 8vh;color: #0087ce;font-size: 16px;width: 100%;}.hackout-draw-lock-wrapper {width: 100%;height: 80vh;display: flex;justify-content: center;align-items: center;}canvas {border: 1px solid #ccc;}.popup-wrapper {position: fixed;top: 0;left: 0;width: 100%;height: 100%;background: rgba(0, 0, 0, 0.5);display: flex;justify-content: center;align-items: center;z-index: 1000;display: none;}.pop-div {width: 300px;background: #ccc;padding: 20px;border-radius: 10px;text-align: center;}.title-content {display: flex;justify-content: space-between;align-items: center;background: #0087ce;color: #fff;padding: 10px;border-radius: 5px 5px 0 0;}.btns {margin-top: 20px;}.btn {display: block;margin: 10px auto;padding: 10px;background: #fff;color: #0087ce;border: 1px solid #0087ce;border-radius: 5px;cursor: pointer;}</style>
</head><body><div class="draw-container"><div class="message-tip" id="message-tip">绘制您的解锁图案</div><div class="hackout-draw-lock-wrapper"><canvas id="firstCanvas" width="350" height="350"></canvas></div></div><div class="popup-wrapper" id="popup"><div class="pop-div"><div class="title-content"><span>请选择</span><span class="close" onclick="closePopup()">×</span></div><div class="btns"><div class="btn" onclick="resetHandLock()">重绘解锁图案</div><div class="btn" onclick="closeHandLock()">关闭解锁图案</div></div></div></div><script>const canvas = document.getElementById('firstCanvas');const context = canvas.getContext('2d');const messageTip = document.getElementById('message-tip');const popup = document.getElementById('popup');const width = canvas.width;const height = canvas.height;const background = 'rgba(0,0,0,0)';const lineColor = '#0087ce';const errorColor = '#f00';const lineBackground = 'rgba(0, 135, 206, 0.5)'; // 蓝色外圆的透明度const errorBackground = 'rgba(255, 0, 0, 0.5)'; // 红色外圆的透明度const circleWidth = 34;const rowPont = 3;const colPont = 3;let initCircleCoordinate = [];let selectedCoordinate = [];let candidateCoordinate = [];let isActive = false;let circleR = 0;let userGesture = ''; // 已设置的用户手势密码let firstGesture = ""; // 第一次手势let pass = ''; // 手势let lineError = false; // 是否显示错误circleR = Math.min(width, height) * 28 / 375;initCanvas();function initCanvas() {initCircleCoordinate = getCircleCoordinate();candidateCoordinate = initCircleCoordinate;selectedCoordinate = [];lineError = false; // 重置错误状态draw();}function touchStart(event) {event.preventDefault();initCanvas();const po = getPosition(event);if (!po) return;for (let i = 0; i < candidateCoordinate.length; i++) {if (collisionDetection(po, candidateCoordinate[i])) {isActive = true;selectedCoordinate.push(candidateCoordinate[i]);candidateCoordinate.splice(i, 1);draw();break;}}}function touchEnd(event) {if (isActive) {isActive = false;draw();const gesture = getPassword();console.log('gesture', gesture);if (gesture.length < 4) {lineError = true;messageTip.textContent = '请至少连接4个点';return;}const gestureStr = gesture.join('');if (!userGesture) {if (firstGesture === "") {firstGesture = gestureStr;messageTip.textContent = '再次绘制您的解锁图案';initCanvas();} else if (firstGesture !== gestureStr) {messageTip.textContent = '两次绘制不一致!';lineError = true;drawError(); // 绘制错误提示setTimeout(() => {lineError = false;initCanvas();}, 1000); // 1秒后恢复} else {userGesture = gestureStr;messageTip.textContent = '您的新解锁图案';lineError = false;popup.style.display = 'flex';}} else {pass = gestureStr;if (pass !== userGesture) {messageTip.textContent = '解锁图案绘制错误!';lineError = true;drawError(); // 绘制错误提示setTimeout(() => {lineError = false;initCanvas();}, 1000); // 1秒后恢复return;}messageTip.textContent = '解锁图案验证成功~';lineError = false;}}}function touchMove(event) {if (isActive) {const po = getPosition(event);if (!po) return;updateCanvas(po);}}function updateCanvas(po) {draw();const last = selectedCoordinate[selectedCoordinate.length - 1];context.beginPath();context.moveTo(po.x, po.y);context.lineTo(last.x, last.y);context.closePath();context.stroke();for (let i = 0; i < candidateCoordinate.length; i++) {if (collisionDetection(po, candidateCoordinate[i])) {selectedCoordinate.push(candidateCoordinate[i]);candidateCoordinate.splice(i, 1);break;}}}function collisionDetection(a, b) {const rX = Math.abs(a.x - b.x);const rY = Math.abs(a.y - b.y);return rX * rX + rY * rY < circleR * circleR;}function getPosition(event) {const rect = canvas.getBoundingClientRect();if (event.touches) {return {x: event.touches[0].clientX - rect.left,y: event.touches[0].clientY - rect.top};} else {return {x: event.clientX - rect.left,y: event.clientY - rect.top};}}function draw() {onDefaultDraw();}function onDefaultDraw() {context.clearRect(0, 0, width, height);context.fillStyle = background;context.fillRect(0, 0, width, height);context.lineWidth = 1;context.strokeStyle = lineColor;context.beginPath();for (let i = 0; i < initCircleCoordinate.length; i++) {context.moveTo(initCircleCoordinate[i].x + circleR, initCircleCoordinate[i].y);context.arc(initCircleCoordinate[i].x, initCircleCoordinate[i].y, circleR, 0, Math.PI * 2, true);}context.stroke();context.closePath();context.strokeStyle = lineColor;context.beginPath();for (let i = 0; i < selectedCoordinate.length; i++) {context.lineTo(selectedCoordinate[i].x, selectedCoordinate[i].y);}context.stroke();context.closePath();context.fillStyle = lineBackground;context.beginPath();for (let i = 0; i < selectedCoordinate.length; i++) {context.moveTo(selectedCoordinate[i].x + circleR / 2, selectedCoordinate[i].y);context.arc(selectedCoordinate[i].x, selectedCoordinate[i].y, circleR, 0, Math.PI * 2, true);}context.fill();context.closePath();context.fillStyle = lineColor;context.beginPath();for (let i = 0; i < selectedCoordinate.length; i++) {context.moveTo(selectedCoordinate[i].x + circleR / 2, selectedCoordinate[i].y);context.arc(selectedCoordinate[i].x, selectedCoordinate[i].y, circleR * 0.6, 0, Math.PI * 2, true);}context.fill();context.closePath();}function drawError() {context.clearRect(0, 0, width, height);context.fillStyle = background;context.fillRect(0, 0, width, height);context.lineWidth = 1;context.strokeStyle = errorColor;context.beginPath();for (let i = 0; i < initCircleCoordinate.length; i++) {context.moveTo(initCircleCoordinate[i].x + circleR, initCircleCoordinate[i].y);context.arc(initCircleCoordinate[i].x, initCircleCoordinate[i].y, circleR, 0, Math.PI * 2, true);}context.stroke();context.closePath();context.strokeStyle = errorColor;context.beginPath();for (let i = 0; i < selectedCoordinate.length; i++) {context.lineTo(selectedCoordinate[i].x, selectedCoordinate[i].y);}context.stroke();context.closePath();context.fillStyle = errorBackground;context.beginPath();for (let i = 0; i < selectedCoordinate.length; i++) {context.moveTo(selectedCoordinate[i].x + circleR / 2, selectedCoordinate[i].y);context.arc(selectedCoordinate[i].x, selectedCoordinate[i].y, circleR, 0, Math.PI * 2, true);}context.fill();context.closePath();context.fillStyle = errorColor;context.beginPath();for (let i = 0; i < selectedCoordinate.length; i++) {context.moveTo(selectedCoordinate[i].x + circleR / 2, selectedCoordinate[i].y);context.arc(selectedCoordinate[i].x, selectedCoordinate[i].y, circleR * 0.6, 0, Math.PI * 2, true);}context.fill();context.closePath();}function getCircleCoordinate() {const offsetx = (width - rowPont * circleR * 2) / (rowPont + 1);const offsety = (height - colPont * circleR * 2) / (colPont + 1);const circleCoordinate = [];for (let col = 0; col < colPont; col++) {for (let row = 0; row < rowPont; row++) {circleCoordinate.push({x: offsetx * (row + 1) + circleR * (2 * row + 1),y: offsety * (col + 1) + circleR * (2 * col + 1),key: 3 * col + row + 1});}}return circleCoordinate;}function getPassword() {return selectedCoordinate.map(item => item.key);}function closePopup() {popup.style.display = 'none';}function resetHandLock() {userGesture = "";firstGesture = "";messageTip.textContent = '设置您的解锁图案';closePopup();initCanvas(); // 重置画布}function closeHandLock() {userGesture = "";closePopup();initCanvas(); // 重置画布}// 根据设备类型绑定事件var userAgent = navigator.userAgent;var isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(userAgent);var deviceType = isMobile ? "移动端" : "PC端";if (deviceType == "移动端") {console.log('移动端');canvas.addEventListener('touchstart', touchStart);canvas.addEventListener('touchmove', touchMove);canvas.addEventListener('touchend', touchEnd);} else {console.log('PC端');canvas.addEventListener('mousedown', touchStart);canvas.addEventListener('mousemove', touchMove);canvas.addEventListener('mouseup', touchEnd);}</script>
</body></html>
相关文章:
html实现手势密码
<!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>手势密码</title><style>body {font-fam…...
【区块链安全 | 第八篇】多签机制及恶意多签
部分参考:慢雾科技 文章目录 为什么需要多签多签机制Tron钱包下的恶意多签Tron 钱包多签权限分类Tron 多签机制的运作方式 恶意多签的过程黑客通过多签机制控制账户黑客剥夺用户权限,完全控制账户 恶意多签成因 在区块链中,多签(M…...
项目如何安装本地tgz包并配置局部registry
一、判断包来源是否正确 1. 检查url curl <registry_url>2. 查看包是否存在 npm view <package_name> --registry<registry_url>二、局部registry配置步骤: 1. 全局配置 如果你希望对所有项目生效,可以将这行配置添加到全局.npmr…...
二月公开赛Web-ssrfme
目录 环境搭建 题目分析 分析代码 解题过程 Redis未授权访问 寻找Flag 环境搭建 进入含有docker-compose.yml的文件内,拉取容器镜像 docker-compose up -d 题目分析 访问容器地址172.25.254.200:8091查看题目 分析代码 url通过GET请求访问界面,…...
JavaRedis和数据库相关面试题
JavaRedis面试题 1. Redis是什么以及Redis为什么快? Redis(Remote Dictionary Server)是一个开源的内存键值数据库,支持多种数据结构(如字符串、哈希、列表、集合等),并提供持久化、复制、…...
告别枯燥工作,走向自动化
嘿,小伙伴们!今天给你们介绍两款超实用的RPA办公自动化软件,用它们,再也不用像机器一样做重复劳动啦,超省时间! 工具名称:影刀RPA(类似产品,八爪鱼 RPA,操作上…...
Spring的 @Conditional @ConditionalOnProperty 注解 笔记250330
Spring的 Conditional ConditionalOnProperty 注解 Spring 的 Conditional 与 ConditionalOnProperty 注解详解 在 Spring 框架中,Conditional 和 ConditionalOnProperty 是用于动态控制 Bean 注册的重要注解。虽然它们都服务于条件化配置,但定位和使用…...
可信数据空间:构筑安全可控数据流通
前言:可信数据空间是一种数据基础设施,发展可信数据空间是全国及各地数据基础设施建设的重要方面。国内数据空间的探索和实践仍然数据探索阶段。本期分享:可信数据空间构筑安全可控数据流通,包括可信数据空间技术介绍、如何助力数…...
Zookeeper特性与节点数据类型
数据结构和监听机制 CP 文件系统形式存储 观察者模式监听节点数据变化、 临时节点客户端超时或发生异常节点就会删除 2888同步数据 3888选举端口 1.什么是Zookeeper ZooKeeper 是一个开源的分布式协调框架,是Apache Hadoop 的一个子项目,主要用来…...
【C++游戏引擎开发】《线性代数》(6):SVD(奇异值分解)的数学原理与实现
一、奇异值分解(SVD)的数学定义 奇异值分解(Singular Value Decomposition,SVD)是一种将任意实数或复数矩阵分解为三个特定矩阵乘积的方法。其数学定义如下: 1.1 分解形式 给定一个秩为 r r r的矩阵 A ∈ R m n \mathbf{A} \in \mathbb{R}^{m \times n} A∈Rmn(或…...
C语言pthread库创建线程的案例
一、代码案例 #include<stdio.h> #include<stdlib.h> // 多线程库 #include<pthread.h> // 线程1的逻辑描述 void* thread_method_01(void* v){ printf("线程1执行完毕。\n"); return NULL; } // 线程2的执行逻辑 void* thread_meth…...
处理 Linux 信号:进程控制与异常管理的核心
个人主页:chian-ocean 文章专栏-Linux 前言: 在 Linux 操作系统中,信号是用于进程间通信的一种机制,能够向进程发送通知,指示某些事件的发生。信号通常由操作系统内核、硬件中断或其他进程发送。接收和处理信号是 Li…...
【蓝桥杯每日一题】4.1
🏝️专栏: 【蓝桥杯备篇】 🌅主页: f狐o狸x "今日秃头刷题,明日荣耀加冕!" 今天我们来练习二分算法 不熟悉二分算法的朋友可以看:【C语言刷怪篇】二分法_编程解决算术问题-CSDN博客 …...
分享系列项目的基础项目
本人分享了一系列的框架项目,它们共同需要依赖这个公共基础,结构如下图所示: 其中: audit: JPA的审计信息基础类auth:认证授权相关类config: 包括redis配置,client中token配置,openai文档配置…...
为 MinIO AIStor 引入模型上下文协议(MCP)服务器
Anthropic 最近宣布的模型上下文协议 (MCP) 将改变我们与技术交互的方式。它允许自然语言通信替换许多任务的复杂命令行语法。不仅如此,语言模型还可以总结传统工具的丰富输出,并以人类可读的形式呈现关键信息。MinIO 是世界领先的…...
spring boot前后端开发上传文件时报413(Request Entity Too Large)错误的可能原因及解决方案
可能原因及解决方案 1. Spring Boot默认文件大小限制 原因:Spring Boot默认单文件最大为1MB,总请求体限制为10MB。解决方案: 在application.properties中配置:spring.servlet.multipart.max-file-size10MB # 单文件最大 spring…...
数据结构实验1.1: 顺序表的操作及其应用
这里写自定义目录标题 一、实验目的二、注意事项三、实验内容(一)问题描述(二)基本要求 四,操作步骤(一)使用visual studio集成环境编写程序 五,示例代码六,运行效果 一、…...
基于yolov11的汽车损伤检测系统python源码+onnx模型+评估指标曲线+精美GUI界面
【算法介绍】 基于YOLOv11的汽车损伤检测系统是一种先进的计算机视觉技术,旨在快速准确地识别汽车的各种损伤类型。该系统利用YOLOv11模型的强大性能,实现了对车辆损伤的精确检测与分类。 该系统能够识别的损伤类型包括裂纹(crackÿ…...
OpenCV 图形API(2)为什么需要图形API?
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 G-API背后的动机 G-API模块为OpenCV带来了基于图的执行模型。本章简要描述了这种新模型如何在两个方面帮助软件开发者:优化和移植图像处理算法…...
基于Spring Boot的平面设计课程在线学习平台系统的设计与实现(LW+源码+讲解)
专注于大学生项目实战开发,讲解,毕业答疑辅导,欢迎高校老师/同行前辈交流合作✌。 技术范围:SpringBoot、Vue、SSM、HLMT、小程序、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容:…...
esp32 idf中的外部组件
通常外部组件都定义在“main/idf_component.yml”里面。 例如小智智能语音的外部组件: ## IDF Component Manager Manifest File dependencies:waveshare/esp_lcd_sh8601: "1.0.2"espressif/esp_lcd_ili9341: "1.2.0"espressif/esp_lcd_gc9a01…...
【JavaEE】MyBatis - Plus
目录 一、快速使用二、CRUD简单使用三、常见注解3.1 TableName3.2 TableFiled3.3 TableId 四、条件构造器4.1 QueryWrapper4.2 UpdateWrapper4.3 LambdaQueryWrapper4.4 LambdaUpdateWrapper 五、自定义SQL 一、快速使用 MyBatis Plus官方文档:MyBatis Plus官方文档…...
经典卷积神经网络LeNet实现(pytorch版)
LeNet卷积神经网络 一、理论部分1.1 核心理论1.2 LeNet-5 网络结构1.3 关键细节1.4 后期改进1.6 意义与局限性二、代码实现2.1 导包2.1 数据加载和处理2.3 网络构建2.4 训练和测试函数2.4.1 训练函数2.4.2 测试函数2.5 训练和保存模型2.6 模型加载和预测一、理论部分 LeNet是一…...
【2】数据结构的单链表章
目录标题 单链表的定义单链表的初始化单链表的建立头插法创建尾插法创建 查找操作按序号查找按内容查找 插入操作删除操作合并操作 单链表总代码与调试 单链表的定义 结点(Node)的定义:数据域(data)和指针域ÿ…...
Linux(十一)fork实例练习、文件操作示例及相关面试题目分享
一、fork实例练习 1、思考下面这段代码的打印结果是什么? #include<stdio.h> #include<unistd.h> #include<assert.h> #include<stdlib.h>int main(){int i0;for(;i<2;i){fork();printf("A\n");} exit(0); }所以一共打印6…...
android Fragment使用
在 Android Fragment 中,导入 id(findViewById)并给控件赋值的逻辑通常应该写在 onViewCreated() 方法中,而不是 onCreateView()。 Fragment 生命周期 & 适合的位置 方法作用适合的操作onCreateView()创建并返回 Fragment 的…...
open3d教程 (三)点云的显示
官方文档位置: Visualization - Open3D 0.19.0 documentationhttps://www.open3d.org/docs/release/tutorial/visualization/visualization.html核心方法: o3d.visualization.draw_geometries([几何对象列表]) import open3d as o3dprint("Load …...
根据模板将 Excel 明细数据生成 Txt 文档|邮件合并
在日常办公中,我们常常会遇到需要批量生成文档的任务。以往,若要将 Excel 中的每一条数据都转化为单独的文档,且文档部分内容依据 Excel 数据动态变化,手动操作不仅繁琐,还容易出错。现在,有一种便捷的方法…...
【学Rust写CAD】22 双圆径向渐变的结构体(two_circle_radial_gradient.rs)
源码 //two_circle_radial_gradient.rs //! 定义双圆径向渐变的结构体和相关功能/// 表示一个双圆径向渐变的源 /// /// 该结构体描述了两个圆之间的渐变,支持矩阵变换和颜色查找表优化 #[derive(Debug, Clone, PartialEq)] pub struct TwoCircleRadialGradientSou…...
LVGL Dropdown和Calendar详解
LVGL Dropdown和Calendar详解 一、Dropdown详解创建和初始化设置下拉框选项获取选项获取选中项文本:获取选中项索引:设置选中项: 事件处理其他功能和样式设置设置下拉按钮样式:设置下拉框方向:设置最大高度:…...
