H5实现签字版签名功能

前言:H5时常需要给C端用户签名的功能,以下是基于Taro框架开发的H5页面实现

一、用到的技术库

  1. 签字库:react-signature-canvas
  2. 主流React Hooks 库:ahooks

二、组件具体实现

解决H5样式问题,主要还是通过两套样式实现横屏和竖屏的处理

index.tsx

import { useState, useRef, useCallback, useEffect, useMemo } from 'react';
import Taro from '@tarojs/taro';
import SignatureCanvas from 'react-signature-canvas';
import { useSize } from 'ahooks';
import { View } from '@tarojs/components';
import { rotateImg } from './utils';
import './index.less';interface IProps {visible: boolean;setVisible: (e) => void;signText?: string;onChange?: (e?) => void; // 生成的图片onSure: (e?) => void; // 确定的回调
}
// 签字版组件
const SignatureBoard = (props: IProps) => {const { visible, setVisible, signText = '请在此空白处签下您的姓名', onChange, onSure } = props;const [signTip, setSignTip] = useState(signText);const sigCanvasRef = useRef<SignatureCanvas | null>(null);const canvasContainer = useRef<HTMLElement>(null);const compContainer = useRef<HTMLElement>(null);const compSize = useSize(compContainer);const canvasSize = useSize(canvasContainer);const [isLandscape, setIsLandscape] = useState<boolean>(false); // 是否横屏// 提示的文字数组,为了在竖屏的情况下,每个字样式旋转const tipText = useMemo(() => {return signTip?.split('') || [];}, [signTip]);// 重签const clearSign = useCallback(() => {setSignTip(signText);sigCanvasRef?.current?.clear();}, [signText]);// 取消const cancelSign = useCallback(() => {clearSign();setVisible?.(false);}, [clearSign, setVisible]);// 确定const sureSign = useCallback(() => {const pointGroupArray = sigCanvasRef?.current?.toData();if (pointGroupArray.flat().length < 30) {Taro.showToast({ title: '请使用正楷字签名', icon: 'none' });return;}if (isLandscape) {// 横屏不旋转图片onSure?.(sigCanvasRef.current.toDataURL());} else {rotateImg(sigCanvasRef?.current?.toDataURL(), result => onSure?.(result), 270);}setVisible?.(false);}, [isLandscape, onSure, setVisible]);// 由于 onorientationchange 只能判断自动旋转,无法判断手动旋转,因此不选择监听 orientationchange;// 监听 resize 可以实现,比较宽高即可判断是否横屏,即宽大于高就是横屏状态,与下面为了方便使用 ahooks 的 useSize 思想一致useEffect(() => {// 如果宽度大于高度,就表示是在横屏状态if ((compSize?.width ?? 0) > (compSize?.height ?? 1)) {// console.log('横屏状态');setIsLandscape(true);clearSign();} else {// console.log('竖屏状态');setIsLandscape(false);clearSign();}}, [clearSign, compSize?.height, compSize?.width]);if (!visible) return null;return (<View ref={compContainer} className='signature-board-comp' onClick={e => e.stopPropagation()}><View className='sign-board-btns'><View className='board-btn' onClick={cancelSign}><View className='board-btn-text'>取消</View></View><View className='board-btn' onClick={clearSign}><View className='board-btn-text'>重签</View></View><View className='board-btn confirm-btn' onClick={sureSign}><View className='board-btn-text'>确定</View></View></View><View className='sign-board' ref={canvasContainer}><SignatureCanvaspenColor='#000' // 笔刷颜色minWidth={1} // 笔刷粗细maxWidth={1}canvasProps={{id: 'sigCanvas',width: canvasSize?.width,height: canvasSize?.height, // 画布尺寸className: 'sigCanvas'}}ref={sigCanvasRef}onBegin={() => setSignTip('')}onEnd={() => {onChange?.(sigCanvasRef?.current?.toDataURL());}}/>{signTip && (<div className='SignatureTips'>{tipText &&tipText?.map((item, index) => (<View key={`${index.toString()}`} className='tip-text'>{item}</View>))}</div>)}</View></View>);
};export default SignatureBoard;

inde.less

@media screen and (orientation: portrait) {/*竖屏 css*/.signature-board-comp {position: fixed;top: 0;right: 0;bottom: 0;left: 0;z-index: 9;display: flex;flex-wrap: nowrap;align-items: stretch;box-sizing: border-box;width: 100vw;height: 100vh;padding: 48px 52px 48px 0px;background-color: #ffffff;.sign-board-btns {display: flex;flex-direction: column;flex-wrap: nowrap;align-items: center;justify-content: flex-end;box-sizing: border-box;width: 142px;padding: 0px 24px;.board-btn {display: flex;align-items: center;justify-content: center;width: 96px;height: 312px;margin-top: 32px;border: 1px solid #181916;border-radius: 8px;opacity: 1;&:active {opacity: 0.9;}.board-btn-text {color: #181916;font-size: 30px;transform: rotate(90deg);}}.confirm-btn {color: #ffffff;background: #181916;.board-btn-text {color: #ffffff;}}}.sign-board {position: relative;flex: 1;.sigCanvas {width: 100%;height: 100%;background: #f7f7f7;border-radius: 10px;}.SignatureTips {position: absolute;top: 0;left: 50%;display: flex;flex-direction: column;align-items: center;justify-content: center;width: 50px;height: 100%;color: #a2a0a8;font-size: 46px;transform: translateX(-50%);pointer-events: none;.tip-text {line-height: 50px;transform: rotate(90deg);}}}}
}@media screen and (orientation: landscape) {/*横屏 css*/.signature-board-comp {position: fixed;top: 0;right: 0;bottom: 0;left: 0;z-index: 9;display: flex;flex-direction: column-reverse;flex-wrap: nowrap;box-sizing: border-box;width: 100vw;height: 100vh;padding: 0px 48px 0px 48px;background-color: #ffffff;.sign-board-btns {display: flex;flex-wrap: nowrap;flex-wrap: nowrap;align-items: center;justify-content: flex-end;box-sizing: border-box;width: 100%;height: 20vh;padding: 12px 0px;.board-btn {display: flex;align-items: center;justify-content: center;width: 156px;height: 100%;max-height: 48px;margin-left: 16px;border: 1px solid #181916;border-radius: 4px;opacity: 1;&:active {opacity: 0.9;}.board-btn-text {color: #181916;font-size: 15px;}}.confirm-btn {color: #ffffff;background: #181916;.board-btn-text {color: #ffffff;}}}.sign-board {position: relative;flex: 1;box-sizing: border-box;height: 80vh;.sigCanvas {box-sizing: border-box;width: 100%;height: 80vh;background: #f7f7f7;border-radius: 5px;}.SignatureTips {position: absolute;top: 0;left: 0;display: flex;align-items: center;justify-content: center;box-sizing: border-box;width: 100%;height: 100%;color: #a2a0a8;font-size: 23px;pointer-events: none;}}}
}

utils.ts

// canvas绘制图片旋转270度
export const rotateImg = (src, callback, deg = 270) => {const canvas = document.createElement('canvas');const ctx = canvas.getContext('2d');const image = new Image();image.crossOrigin = 'anonymous';image.src = src;image.onload = function () {const imgW = image.width; // 图片宽度const imgH = image.height; // 图片高度const size = imgW > imgH ? imgW : imgH; // canvas初始大小canvas.width = size * 2;canvas.height = size * 2;// 裁剪坐标const cutCoor = {sx: size,sy: size - imgW,ex: size + imgH,ey: size + imgW};ctx?.translate(size, size);ctx?.rotate((deg * Math.PI) / 180);// drawImage向画布上绘制图片ctx?.drawImage(image, 0, 0);// getImageData() 复制画布上指定矩形的像素数据const imgData = ctx?.getImageData(cutCoor.sx, cutCoor.sy, cutCoor.ex, cutCoor.ey);canvas.width = imgH;canvas.height = imgW;// putImageData() 将图像数据放回画布ctx?.putImageData(imgData as any, 0, 0);callback(canvas.toDataURL());};
};

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

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

相关文章

a-date-picker报错TypeError: date4.locale is not a function

问题描述 使用日期选择器&#xff0c;数据从后端获得&#xff0c;再赋值给a-date-picker做数据回显&#xff0c;遇到这个报错&#xff0c;排错后定位到a-date-picker组件本身接收数据的问题。 如果使用了dayjs或moment库来处理时间字符串&#xff0c;并且使用.format对时间数据…

day20-101. 对称二叉树

101. 对称二叉树 力扣题目链接 给定一个二叉树&#xff0c;检查它是否是镜像对称的。 思路 镜像对称必要的条件就是根节点的左右子树互相对称 左子树的左孩子 右子树的右孩子左子树的右孩子 右子树的左孩子 递归 使用递归前要确定递归的顺序&#xff0c;是前序、后序还…

【Android】在AndroidStudio开发工具运行Java程序

在Android Studio开发工具中&#xff0c;Android系统开始就是用java语言开发的&#xff0c;还可以java代码来写程序&#xff0c;控制台&#xff0c;桌面应用&#xff0c;还可以写可调用的模块&#xff0c;这里讲一下创建Java程序步骤&#xff0c;方便入门java语言开发。 新建一…

Leetcode-每日一题【剑指 Offer 39. 数组中出现次数超过一半的数字】

题目 数组中有一个数字出现的次数超过数组长度的一半&#xff0c;请找出这个数字。 你可以假设数组是非空的&#xff0c;并且给定的数组总是存在多数元素。 示例 1: 输入: [1, 2, 3, 2, 2, 2, 5, 4, 2]输出: 2 限制&#xff1a; 1 < 数组长度 < 50000 解题思路 前置知…

Docker Compose编排部署LNMP服务

目录 安装docker-ce 阿里云镜像加速器 文件 启动 安装docker-ce [rootlocalhost ~]# wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo --2023-08-03 18:34:32-- http://mirrors.aliyun.com/repo/Centos-7.repo 正在解析主机 m…

观察者模式(Observer)

观察着模式是一种行为设计模式&#xff0c;可以用来定义对象间的一对多依赖关系&#xff0c;使得每当一个对象状态发生改变时&#xff0c;其相关依赖对象皆得到通知并被自动更新。 观察者模式又叫做发布-订阅&#xff08;Publish/Subscribe&#xff09;模式、模型-视图&#xf…

Elasticsearch分词详解:ES分词介绍、倒排索引介绍、分词器的作用、停用词

详见&#xff1a;https://blog.csdn.net/weixin_40612128/article/details/123476053

新手指南:流程图中各种图形的含义及用法解析

我们经常在技术设计、沟通、业务演示等一些领域看到流程图&#xff0c;它也可以称为输入输出图。顾名思义&#xff0c;它是指一种简单的工作流程的具体步骤&#xff0c;比如包括一次会议的流程&#xff0c;以及一次生产制造的顺序和过程等。本文将为大家介绍流程图的含义和具体…

【零基础学Rust | 基础系列 | 基础语法】变量,数据类型,运算符,控制流

文章目录 简介&#xff1a;一&#xff0c;变量1&#xff0c;变量的定义2&#xff0c;变量的可变性3&#xff0c;变量的隐藏 二、数据类型1&#xff0c;标量类型2&#xff0c;复合类型 三&#xff0c;运算符1&#xff0c;算术运算符2&#xff0c;比较运算符3&#xff0c;逻辑运算…

BigDecimal的加减乘除

加法 add() 示例&#xff1a;a b BigDecimal a new BigDecimal("1.23"); BigDecimal b new BigDecimal("4.56"); BigDecimal sum a.add(b); ------------ 减法 subtract() 示例…

在windows配置redis的一些错误及解决方案

目录 Unable to connect to Redis; nested exception is io.lettuce.core.RedisConnectionException:用客户端Redis Desktop Manager一样的密码端口&#xff0c;是可以正常连接的&#xff0c;但是运行java程序之后使用接口请求就会报错 Unable to connect to Redis; nested e…

【JAVA】正则表达式是啥?

个人主页&#xff1a;【&#x1f60a;个人主页】 系列专栏&#xff1a;【❤️初识JAVA】 文章目录 前言正则表达式正则表达式语法正则表达式的特点捕获组实例 前言 如果我们想要判断给定的字符串是否符合正则表达式的过滤逻辑&#xff08;称作“匹配”&#xff09;&#xff0c…

Screens 4 for mac VNC客户端 强大的远程控制工具

Screens 4 for Mac 是一款功能强大的 VNC 客户端软件&#xff0c;为 Mac 用户提供了便捷的远程访问和控制解决方案。无论您是需要远程管理服务器、办公电脑&#xff0c;还是需要远程协助他人解决问题&#xff0c;Screens 4 都是您的理想选择。 Screens 4 for Mac具备简洁直观的…

Mybatis 实体类属性名和表中字段名不一致怎么处理

一. 前言 最近耀哥有学生出去面试&#xff0c;被问到 “Mybatis实体类的属性名和表中的字段名不一致该怎么处理&#xff1f;”&#xff0c;这其实是一个很经典的面试题&#xff0c;接下来耀哥就为大家详细解析一下这道面试题。 二. 分析 2.1 实体类和字段名不一致所带来的后果…

GPT带我学-设计模式-工厂模式

1 你好&#xff0c;请问你知道设计模式的工厂模式吗 当然知道&#xff0c;工厂模式是一种创建型设计模式&#xff0c;它提供了一种创建对象的方式&#xff0c;而不需要暴露对象创建的逻辑细节。工厂模式通过使用工厂类来创建对象&#xff0c;从而将对象的实例化逻辑与客户端代…

Json文件编辑功能

1 Json格式 JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。它基于 ECMAScript&#xff08;European Computer Manufacturers Association, 欧洲计算机协会制定的js规范&#xff09;的一个子集&#xff0c;采用完全独立于编程语言的文本格式来存储和表示数据。…

Maven引入本地jar包

maven做为一种强大的依赖管理工具&#xff0c;可以帮助我们更方便的管理项目中的依赖&#xff1b;而在使用过程中我们难免会有需要引入本地jar包的需求&#xff0c;这里踩过坑之后我分享俩种引入方式&#xff1b; 1.上传jar到本地maven仓库&#xff0c;再引入 使用此方法后可…

echarts datazoom功能设置

dataZoom: [ {type: slider,handleSize: "0%",moveHandleSize: 0,height: 8,backgroundColor: "#F2F3F8",fillerColor: "#DCDFE6",bottom: 0,startValue: 0,endValue: 5,showDetail: false ,zoomLock: true,// 锁定窗口的大小filterMode:empty,m…

CAD产品设计逆向软件 FARO RevEng Crack

CAD产品设计逆向软件 FARO RevEng 软件平台能为用户带来全面的数字设计体验。该反向工程软件有助于利用三维点云创建和编辑高质量的网格和 CAD 表面&#xff0c;以实现反向工程工作流程。然后&#xff0c;工业设计师可以利用这些网格模型进行进一步设计或三维打印。 RevEng 的商…

leetcode 983. 最低票价

在一个火车旅行很受欢迎的国度&#xff0c;你提前一年计划了一些火车旅行。在接下来的一年里&#xff0c;你要旅行的日子将以一个名为 days 的数组给出。每一项是一个从 1 到 365 的整数。 火车票有 三种不同的销售方式 &#xff1a; 一张 为期一天 的通行证售价为 costs[0] …