鸿蒙-Canvas-图片滑动验证
文章目录
- 过程
- 绘制形状方式详细解释
- 定义变量
- 布局
- 整图Canvas
- 需要滑动的形状
- 需要处理图片的方式
- 处理抠图
- 绘制抠出来的图
- 总结
群里有朋友问图片滑块验证码怎么做,就是一张图上扣出来一块,然后拖动这一小块完成拼图。
第一个想法就是偷懒一下:直接让设计在图片上抠出来一小块,把这两个图片和抠图的坐标一块下发,用Image或者canvas自己绘制一下,监听一下手指移动,当手指抬起的时候,如果移动的坐标和抠图的坐标误差在指定范围内,就算成功。
后来说Android那边是自己处理的,下发整张图片,然后客户端自己抠图,自己处理。
Android能做的,鸿蒙应该也能做,这时候就应该掏出来Canvas怼一波了
过程
两个Canvas,一个使用drawImage画整张图片,画出来后,随机两个坐标值使用getImageData获取指定位置的图片内容。然后在这个区域绘制上边框或者填充颜色,告诉用户获取的是这个区域的内容。想上难度的话,不提示这个截取位置也行。
在另外一个Canvas上使用putImageData将图片绘制出来,绑定一下移动手势监听,然后不断更新绘制图片的坐标。当抬起手指的时候,对比一下移动的坐标和抠图的坐标,在允许的范围内,判定为成功。
结束。打完收工。完结撒花。

绘制形状方式详细解释
先看下面不需要处理抠图的,这个简单点,我们循序渐进。
定义变量
两个Canvas,需要两个CanvasRenderingContext2D分别绘制两个Canvas上的内容。
一个能接受的误差值。
随机出来的抠图的横纵坐标。
抠图的大小。
private settings: RenderingContextSettings = new RenderingContextSettings(true)
private canvasRendering: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings)
private canvasRendering2: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings)
//允许的误差
private diffInterval: number = 10
//随机抠图的横坐标
private clip_start_x: number = 100
//随机抠图的纵坐标
private clip_start_y: number = 100
//抠图的宽度
private clip_image_width = 120
//抠图的高度
private clip_image_height = 120
布局
这个没啥好说的,Stack里面摞两个Canvas,底部的Canvas画整个图,上面的Canvas画形状。
整图Canvas
这里使用的本地图片,理论上讲,使用网络图片应该也能处理。
随机坐标时,注意减去抠图的宽度,否则万一随机出来的坐标在绘制完形状之后超出的图片范围就好玩了。
这里随机之后绘制了一个三角形。
Canvas(this.canvasRendering).width("100%").height("100%").onReady(() => {//这里用的本地图片let imageBitMap: ImageBitmap = new ImageBitmap("pages/playground/cat.webp")this.canvasRendering.drawImage(imageBitMap, 0, 0)hilog.error(0x01, 'SlideVerificationView2', 'imageBitMap width --> ' + imageBitMap.width)hilog.error(0x01, 'SlideVerificationView2', 'imageBitMap height --> ' + imageBitMap.height)//随机两个坐标,注意不要超出图片范围this.clip_start_x = Math.floor(Math.random() * (imageBitMap.width - this.clip_image_width))this.clip_start_y = Math.floor(Math.random() * (imageBitMap.height - this.clip_image_height))hilog.error(0x01, 'SlideVerificationView2', 'clip_start_x --> ' + this.clip_start_x)hilog.error(0x01, 'SlideVerificationView2', 'clip_start_y --> ' + this.clip_start_y)this.canvasRendering.lineWidth = 2this.canvasRendering.strokeStyle = '#FFFFFF'//在对应的区域绘制标识,这里画了个三角形,想画其他的自己调整就好this.canvasRendering.moveTo(this.clip_start_x + this.clip_image_width / 2, this.clip_start_y)this.canvasRendering.lineTo(this.clip_start_x + this.clip_image_width, this.clip_start_y + this.clip_image_height)this.canvasRendering.lineTo(this.clip_start_x, this.clip_start_y + this.clip_image_height)this.canvasRendering.lineTo(this.clip_start_x + this.clip_image_width / 2, this.clip_start_y)this.canvasRendering.stroke()})
需要滑动的形状
我们拿到了随机的坐标后,在新的Canvas上绘制相同的形状。
这里需要监听手指的滑动,我们使用了priorityGesture来绑定PanGesture。注意这里滑动最小距离为5vp时识别成功。
这里我们限制了只能横向滑动。想加点难度的话,可以在横纵方向上都能滑动。
最后在onActionEnd的时候判断一下移动的坐标是否满足条件
Canvas(this.canvasRendering2).width("100%").height("100%").onReady(() => {})//绑定优先识别手势.priorityGesture(//平移手势,滑动最小距离为5vp时识别成功。PanGesture().onActionStart((event: GestureEvent) => {}).onActionUpdate((event: GestureEvent) => {//重置一下画布this.canvasRendering2.reset()//绘制形状,和整图canvas中的形状、大小一致this.canvasRendering2.moveTo(event.offsetX + this.clip_image_width/2, this.clip_start_y)this.canvasRendering2.lineTo(event.offsetX + this.clip_image_width, this.clip_start_y + this.clip_image_height)this.canvasRendering2.lineTo(event.offsetX, this.clip_start_y + this.clip_image_height)this.canvasRendering2.lineTo(event.offsetX + this.clip_image_width/2, this.clip_start_y)this.canvasRendering2.strokeStyle = Color.Pinkthis.canvasRendering2.lineWidth = 2this.canvasRendering2.stroke()}).onActionEnd((event: GestureEvent) => {hilog.error(0x01, 'SlideVerificationView', `onActionEnd ${event.offsetX.toString()}`)//判定是否成功if (Math.abs(event.offsetX - this.clip_start_x) < this.diffInterval) {promptAction.showToast({ message: '验证成功' })} else {promptAction.showToast({ message: '验证失败' })this.canvasRendering2.reset()}}))
这种是最简单的,不需要处理图片,只需要绘制形状就好了
需要处理图片的方式
比起上面这种,我们只需要多定义一个ImageData就好了
private settings: RenderingContextSettings = new RenderingContextSettings(true)
private canvasRendering: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings)
private canvasRendering2: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings)
private diffInterval: number = 10
private clip_start_x: number = 100
private clip_start_y: number = 100
private clip_image_width = 120
private clip_image_height = 120
private imageData?: ImageData
处理抠图
在绘制整图的Canvas上调用getImageData获取一下抠出来的图片内容就好了。
由于ImageData是个正方形,我们这里需要处理成三角形,我没有找到很好的方法,只能对ImageData.data属性进行处理,它是一维数组,保存了相应的颜色数据,数据值范围为0到255。
Canvas(this.canvasRendering).width("100%").height("100%").onReady(() => {let imageBitMap: ImageBitmap = new ImageBitmap("pages/playground/cat.webp")this.canvasRendering.drawImage(imageBitMap, 0, 0)hilog.error(0x01, 'SlideVerificationView2', 'imageBitMap width --> ' + imageBitMap.width)hilog.error(0x01, 'SlideVerificationView2', 'imageBitMap height --> ' + imageBitMap.height)this.clip_start_x = Math.floor(Math.random() * (imageBitMap.width - this.clip_image_width))this.clip_start_y = Math.floor(Math.random() * (imageBitMap.height - this.clip_image_height))hilog.error(0x01, 'SlideVerificationView2', 'clip_start_x --> ' + this.clip_start_x)hilog.error(0x01, 'SlideVerificationView2', 'clip_start_y --> ' + this.clip_start_y)this.imageData = this.canvasRendering.getImageData(this.clip_start_x, this.clip_start_y, this.clip_image_width, this.clip_image_height)//在对应的区域绘制标识this.canvasRendering.lineWidth = 2this.canvasRendering.fillStyle = '#66FFFFFF'this.canvasRendering.moveTo(this.clip_start_x + this.clip_image_width / 2, this.clip_start_y)this.canvasRendering.lineTo(this.clip_start_x + this.clip_image_width, this.clip_start_y + this.clip_image_height)this.canvasRendering.lineTo(this.clip_start_x, this.clip_start_y + this.clip_image_height)this.canvasRendering.lineTo(this.clip_start_x + this.clip_image_width / 2, this.clip_start_y)this.canvasRendering.fill()//将ImageData处理成三角形if(this.imageData){let width = this.imageData.width * 4let height = this.imageData.heightlet rate = width / heightlet widthCenter = Math.floor(width / 2)for (let i = 0; i < height; i++) {//第几行for (let j = 0; j < width; j++) {//第几列if (j < widthCenter - rate * i / 2) {this.imageData.data[i * width +j] = 0} else if (j > widthCenter + rate * i / 2) {this.imageData.data[i * width +j] = 0}}}}})
绘制抠出来的图
这个就更简单了,相同的绑定手势方法,相同的判定方法。
唯一的变化就是在onActionUpdate回调中使用putImageData绘制图片
Canvas(this.canvasRendering2).width("100%").height("100%").onReady(() => {}).priorityGesture(PanGesture().onActionStart((event: GestureEvent) => {}).onActionUpdate((event: GestureEvent) => {hilog.error(0x01, 'SlideVerificationView', event.offsetX.toString())if (this.imageData) {this.canvasRendering2.reset()this.canvasRendering2.putImageData(this.imageData, event.offsetX, this.clip_start_y)}}).onActionEnd((event: GestureEvent) => {hilog.error(0x01, 'SlideVerificationView', `onActionEnd ${event.offsetX.toString()}`)if (Math.abs(event.offsetX - this.clip_start_x) < this.diffInterval) {promptAction.showToast({ message: '验证成功' })} else {promptAction.showToast({ message: '验证失败' })this.canvasRendering2.reset()}}))
到此,我们就完成了简单的滑动图片验证的功能
总结
整体的流程上面也说过了,这里就不再赘述。
我们还可以加大点难度,比如在抠图后不在原图上提示范围,让使用者自己找。
比如我们还可以将抠出来的图镜像一下,让使用者自己找。
比如我们还可以将抠出来的图隔像素点抽样一下。
比如我们还可以将抠出来的图中的像素调整一下颜色。
。。。
相关文章:
鸿蒙-Canvas-图片滑动验证
文章目录 过程绘制形状方式详细解释定义变量布局整图Canvas需要滑动的形状 需要处理图片的方式处理抠图绘制抠出来的图 总结 群里有朋友问图片滑块验证码怎么做,就是一张图上扣出来一块,然后拖动这一小块完成拼图。 第一个想法就是偷懒一下:直…...
Python应用算法之贪心算法理解和实践
一、什么是贪心算法? 贪心算法(Greedy Algorithm)是一种简单而高效的算法设计思想,其核心思想是:在每一步选择中,都采取当前状态下最优的选择(即“局部最优解”),希望通…...
网络运维学习笔记 017HCIA-Datacom综合实验01
文章目录 综合实验1实验需求总部特性 分支8分支9 配置一、 基本配置(IP二层VLAN链路聚合)ACC_SWSW-S1SW-S2SW-Ser1SW-CoreSW8SW9DHCPISPGW 二、 单臂路由GW 三、 vlanifSW8SW9 四、 OSPFSW8SW9GW 五、 DHCPDHCPGW 六、 NAT缺省路由GW 七、 HTTPGW 综合实…...
C++(17):为optional类型构造对象
C++(17):optional,多了一个合理的选择_c++17 max-CSDN博客 介绍了optional做为函数返回值的一种方式 其实optional也可以作为对象来使用 #include &...
Maven导入hutool依赖报错-java: 无法访问cn.hutool.core.io.IORuntimeException 解决办法
欢迎大家来到我的博客~欢迎大家对我的博客提出指导,有错误的地方会改进的哦~点击这里了解更多内容 目录 一、报错二、解决办法 一、报错 <dependency><groupId>cn.hutool</groupId><artifactId>hutool-captcha</artifactId> </de…...
Simulink库浏览器中有大量的模型组件工具箱介绍
Simulink库浏览器中有大量的模型组件工具箱,包括Simulink工具箱、Autosar工具箱、电机控制工具箱等,其中Simulink工具箱包含了几十个的子模块,这里介绍下这些子模块的功能,帮助读者全面的了解这些功能模块,在今后的模型…...
从0到1:固件分析
固件分析 0x01 固件提取 1、从厂商官网下载 例如D-link的固件: https://support.dlink.com/resource/products/ 2、代理或镜像设备更新时的流量 发起中间人攻击MITM #启用IP转发功能 echo 1 > /proc/sys/net/ipv4/ip_forward#配置iptables,将目…...
模电知识点总结(6)
1.选取频率高于1000Hz的信号时,可选用高通滤波器;抑制50Hz的交流干扰时,可选用带阻滤波器如果希望抑制500Hz以下的信号,可选用高通滤波器。 2.有用信号频率高于1000Hz,可选用高通滤波器;希望抑制50Hz的交流…...
【Java学习】多态
目录 一、方法相同 二、方法重写 1.概念 2.条件 三、向上转型 1.概念 2.方式 四、方法绑定 五、多态 一、方法相同 方法相同要求方法名相同、参数列表相同、返回值类型相同(与两方法修饰的访问限定符相不相同、静态非静态状态相不相同无关),而且在子类与父…...
Oracle 深入理解Lock和Latch ,解析访问数据块全流程
Oracle 锁机制介绍 根据保护对象的不同,单实例Oracle数据库锁可以分为以下几大类: DML lock(data locks,数据锁):用于保护数据的完整性; DDL lock(dictionary locks,字典…...
什么是事务?并发事务引发的问题?什么是MVCC?
文章目录 什么是事务?并发事务引发的问题?什么是MVCC?1.事务的四大特性2.并发事务下产生的问题:脏读、不可重复读、幻读3.如何应对并发事务引发的问题?4.什么是MVCC?5.可见性规则?参考资料 什么…...
【JavaEE进阶】MyBatis通过注解实现增删改查
目录 🍃前言 🍀打印日志 🌴传递参数 🎋增(Insert) 🚩返回主键 🎄删(Delete) 🌲改(Update) 🌳查(Select) 🚩起别名 🚩结果映射 🚩开启驼…...
Uptime Kuma实现业务接口自定义逻辑监控
背景 在现代分布式架构中,业务系统通常由多个微服务组成,微服务之间通过接口进行数据交互。为了确保业务的正常运行,我们需要对这些接口进行监控,及时发现并处理异常情况。然而,由于业务数据接口的复杂性,通用的监控方式往往难以满足需求,需要自定义逻辑来判断接口数据是否异常…...
基于 JavaWeb 的 Spring Boot 调查问卷管理系统设计和实现(源码+文档+部署讲解)
技术范围:SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容:免费功能设计、开题报告、任务书、中期检查PPT、系统功能实现、代码编写、论文编写和辅导、论…...
新手小白学习棒球规则·棒球1号位
新手小白学习棒球规则,可以从以下几个方面入手: 一、球场与球员 • 球场布局:棒球场呈菱形,由四个垒位(一垒、二垒、三垒和本垒)和一个投手板组成,外围是外场区域。内场为正方形,四…...
单元测试的策略有哪些,主要包括什么?
单元测试的策略及主要内容 单元测试(Unit Testing)是指对软件系统中的最小可测试单元(通常是一个函数、方法或类)进行验证,以确保其行为符合预期。常见的单元测试策略可以分为基于代码的策略和基于数据的策略…...
深度学习之图像回归(一)
前言 图像回归任务主要是理解一个最简单的深度学习相关项目的结构,整体的思路,数据集的处理,模型的训练过程和优化处理。 因为深度学习的项目思路是差不多的,主要的区别是对于数据集的处理阶段,之后模型训练有一些小…...
Docker 替换到 Containerd (nerdctl相关指令)
因为docker不给用了,所以使用Containerd来代替 前置准备 安装 Containerd # 安装 containerd yum install -y yum-utils yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo yum install -y containerd.io # 生成默认配置文件 mkdir -p…...
Ollama API 参考文档
文档来源:API 参考文档 -- Ollama 中文文档|Ollama官方文档 端点 生成完成生成聊天完成创建模型列出本地模型显示模型信息复制模型删除模型拉取模型推送模型生成嵌入列出正在运行的模型版本...
PHP房屋出租出售高效预约系统小程序源码
🏠 房屋出租出售高效预约系统 —— 您的智能找房新选择 💡 这是一款集智慧与匠心于一体的房屋出租出售预约系统,它巧妙地融合了ThinkPHP与Uniapp两大先进框架,精心打造而成。无论是小程序、H5网页,还是APP端ÿ…...
K8S认证|CKS题库+答案| 11. AppArmor
目录 11. AppArmor 免费获取并激活 CKA_v1.31_模拟系统 题目 开始操作: 1)、切换集群 2)、切换节点 3)、切换到 apparmor 的目录 4)、执行 apparmor 策略模块 5)、修改 pod 文件 6)、…...
盘古信息PCB行业解决方案:以全域场景重构,激活智造新未来
一、破局:PCB行业的时代之问 在数字经济蓬勃发展的浪潮中,PCB(印制电路板)作为 “电子产品之母”,其重要性愈发凸显。随着 5G、人工智能等新兴技术的加速渗透,PCB行业面临着前所未有的挑战与机遇。产品迭代…...
java调用dll出现unsatisfiedLinkError以及JNA和JNI的区别
UnsatisfiedLinkError 在对接硬件设备中,我们会遇到使用 java 调用 dll文件 的情况,此时大概率出现UnsatisfiedLinkError链接错误,原因可能有如下几种 类名错误包名错误方法名参数错误使用 JNI 协议调用,结果 dll 未实现 JNI 协…...
VTK如何让部分单位不可见
最近遇到一个需求,需要让一个vtkDataSet中的部分单元不可见,查阅了一些资料大概有以下几种方式 1.通过颜色映射表来进行,是最正规的做法 vtkNew<vtkLookupTable> lut; //值为0不显示,主要是最后一个参数,透明度…...
浅谈不同二分算法的查找情况
二分算法原理比较简单,但是实际的算法模板却有很多,这一切都源于二分查找问题中的复杂情况和二分算法的边界处理,以下是博主对一些二分算法查找的情况分析。 需要说明的是,以下二分算法都是基于有序序列为升序有序的情况…...
企业如何增强终端安全?
在数字化转型加速的今天,企业的业务运行越来越依赖于终端设备。从员工的笔记本电脑、智能手机,到工厂里的物联网设备、智能传感器,这些终端构成了企业与外部世界连接的 “神经末梢”。然而,随着远程办公的常态化和设备接入的爆炸式…...
让回归模型不再被异常值“带跑偏“,MSE和Cauchy损失函数在噪声数据环境下的实战对比
在机器学习的回归分析中,损失函数的选择对模型性能具有决定性影响。均方误差(MSE)作为经典的损失函数,在处理干净数据时表现优异,但在面对包含异常值的噪声数据时,其对大误差的二次惩罚机制往往导致模型参数…...
处理vxe-table 表尾数据是单独一个接口,表格tableData数据更新后,需要点击两下,表尾才是正确的
修改bug思路: 分别把 tabledata 和 表尾相关数据 console.log() 发现 更新数据先后顺序不对 settimeout延迟查询表格接口 ——测试可行 升级↑:async await 等接口返回后再开始下一个接口查询 ________________________________________________________…...
并发编程 - go版
1.并发编程基础概念 进程和线程 A. 进程是程序在操作系统中的一次执行过程,系统进行资源分配和调度的一个独立单位。B. 线程是进程的一个执行实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。C.一个进程可以创建和撤销多个线程;同一个进程中…...
android13 app的触摸问题定位分析流程
一、知识点 一般来说,触摸问题都是app层面出问题,我们可以在ViewRootImpl.java添加log的方式定位;如果是touchableRegion的计算问题,就会相对比较麻烦了,需要通过adb shell dumpsys input > input.log指令,且通过打印堆栈的方式,逐步定位问题,并找到修改方案。 问题…...
