鸿蒙-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端ÿ…...
Zustand 状态管理库:极简而强大的解决方案
Zustand 是一个轻量级、快速和可扩展的状态管理库,特别适合 React 应用。它以简洁的 API 和高效的性能解决了 Redux 等状态管理方案中的繁琐问题。 核心优势对比 基本使用指南 1. 创建 Store // store.js import create from zustandconst useStore create((set)…...
【快手拥抱开源】通过快手团队开源的 KwaiCoder-AutoThink-preview 解锁大语言模型的潜力
引言: 在人工智能快速发展的浪潮中,快手Kwaipilot团队推出的 KwaiCoder-AutoThink-preview 具有里程碑意义——这是首个公开的AutoThink大语言模型(LLM)。该模型代表着该领域的重大突破,通过独特方式融合思考与非思考…...
自然语言处理——Transformer
自然语言处理——Transformer 自注意力机制多头注意力机制Transformer 虽然循环神经网络可以对具有序列特性的数据非常有效,它能挖掘数据中的时序信息以及语义信息,但是它有一个很大的缺陷——很难并行化。 我们可以考虑用CNN来替代RNN,但是…...
第 86 场周赛:矩阵中的幻方、钥匙和房间、将数组拆分成斐波那契序列、猜猜这个单词
Q1、[中等] 矩阵中的幻方 1、题目描述 3 x 3 的幻方是一个填充有 从 1 到 9 的不同数字的 3 x 3 矩阵,其中每行,每列以及两条对角线上的各数之和都相等。 给定一个由整数组成的row x col 的 grid,其中有多少个 3 3 的 “幻方” 子矩阵&am…...
华硕a豆14 Air香氛版,美学与科技的馨香融合
在快节奏的现代生活中,我们渴望一个能激发创想、愉悦感官的工作与生活伙伴,它不仅是冰冷的科技工具,更能触动我们内心深处的细腻情感。正是在这样的期许下,华硕a豆14 Air香氛版翩然而至,它以一种前所未有的方式&#x…...
基于TurtleBot3在Gazebo地图实现机器人远程控制
1. TurtleBot3环境配置 # 下载TurtleBot3核心包 mkdir -p ~/catkin_ws/src cd ~/catkin_ws/src git clone -b noetic-devel https://github.com/ROBOTIS-GIT/turtlebot3.git git clone -b noetic https://github.com/ROBOTIS-GIT/turtlebot3_msgs.git git clone -b noetic-dev…...
Ubuntu Cursor升级成v1.0
0. 当前版本低 使用当前 Cursor v0.50时 GitHub Copilot Chat 打不开,快捷键也不好用,当看到 Cursor 升级后,还是蛮高兴的 1. 下载 Cursor 下载地址:https://www.cursor.com/cn/downloads 点击下载 Linux (x64) ,…...
【C++】纯虚函数类外可以写实现吗?
1. 答案 先说答案,可以。 2.代码测试 .h头文件 #include <iostream> #include <string>// 抽象基类 class AbstractBase { public:AbstractBase() default;virtual ~AbstractBase() default; // 默认析构函数public:virtual int PureVirtualFunct…...
数据库——redis
一、Redis 介绍 1. 概述 Redis(Remote Dictionary Server)是一个开源的、高性能的内存键值数据库系统,具有以下核心特点: 内存存储架构:数据主要存储在内存中,提供微秒级的读写响应 多数据结构支持&…...
路由基础-路由表
本篇将会向读者介绍路由的基本概念。 前言 在一个典型的数据通信网络中,往往存在多个不同的IP网段,数据在不同的IP网段之间交互是需要借助三层设备的,这些设备具备路由能力,能够实现数据的跨网段转发。 路由是数据通信网络中最基…...
