H5实现签字版签名功能

news/2024/10/18 0:30:48/

前言: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.ppmy.cn/news/1003155.html

相关文章

Mr. Cappuccino的第58杯咖啡——MacOS配置Maven和Java环境

MacOS配置Maven和Java环境 查看Mac使用的是哪个shell下载并准备Maven下载Maven配置前准备 下载并安装JDK下载JDK安装JDK 配置Maven和Java环境添加配置加载配置 验证环境 查看Mac使用的是哪个shell echo $SHELL如果使用的是bash&#xff0c;则使用以下命令 open ~/.bash_profi…

visual studio 生成dll文件以及修改输出dll文件名称操作

目录 visual studio 生成dll文件以及修改dll文件名称一、准备测试代码二、设置导出dll属性三、生成dll文件 .lib .dll .pdb 的简单介绍dll文件使用方式lib文件使用方式1、动态链接 &#xff08;原理&#xff09;2、静态链接&#xff1a; visual studio 生成dll文件以及修改dll文…

Gradio-YOLOv5-YOLOv7 搭建Web GUI

目录 0 相关资料&#xff1a;1 Gradio介绍2 环境搭建3 GradioYOLOv54 GradioYOLOv75 源码解释 0 相关资料&#xff1a; Gradio-YOLOv5-Det&#xff1a;https://gitee.com/CV_Lab/gradio_yolov5_det 【手把手带你实战YOLOv5-入门篇】YOLOv5 Gradio搭建Web GUI: https://www.bi…

webpack基础知识二:说说webpack的构建流程?

一、运行流程 webpack 的运行流程是一个串行的过程&#xff0c;它的工作流程就是将各个插件串联起来 在运行过程中会广播事件&#xff0c;插件只需要监听它所关心的事件&#xff0c;就能加入到这条webpack机制中&#xff0c;去改变webpack的运作&#xff0c;使得整个系统扩展…

redis原理 6:小道消息 —— PubSub

前面我们讲了 Redis 消息队列的使用方法&#xff0c;但是没有提到 Redis 消息队列的不足之处&#xff0c;那就是它不支持消息的多播机制。 img 消息多播 消息多播允许生产者生产一次消息&#xff0c;中间件负责将消息复制到多个消息队列&#xff0c;每个消息队列由相应的消费组…

Consul实战

Consul实战 什么是Consul Consul是一种为分布式系统提供服务发现、配置共享和健康检查的开源工具&#xff1b; 可以用来做微服务架构里的注册中心和配置中心。Consul的特定和功能有&#xff1a; 1.服务发现 consul允许微服务注册自己的实例到Consul, 并查询consul来获取可用的…

【Leetcode】73.矩阵置零

一、题目 1、题目描述 给定一个 m x n 的矩阵,如果一个元素为 0 ,则将其所在行和列的所有元素都设为 0 。请使用 原地 算法。 示例1: 输入:matrix = [[1,1,1],[1,0,1],[1,1,1]] 输出:[[1,0,1],[0,0,0],[1,0,1]]示例2: 输入:matrix = [[0,1,2,0],[3,4,5,2],[1,3,1…

【uniapp】样式合集

1、修改uni-data-checkbox多选框的样式为单选框的样式 我原先是用的单选&#xff0c;但是单选并不支持选中后&#xff0c;再次点击取消选中&#xff1b;所以我改成了多选&#xff0c;然后改变多选样式&#xff0c;让他看起来像单选 在所在使用的页面上修改样式即可 <uni-d…