关于js实现手写签名

原有的业务逻辑是用户线下签字,然后拍照后将照片上传到后台系统。用户将手机端照片转移到电脑也很复杂。特别是对于非自己的电脑的图片上传。所以将业务转到ipad或者surface上,减少操作逻辑。

1. 实现操作逻辑

这个直接参考MDN的触摸事件的文档。里面提供了完整的代码。

将其整理成React组件形式如下:

js 复制代码
import React, { useEffect, forwardRef, useImperativeHandle, useState } from 'react';

const Signature: React.FC = (props, ref) => {
    const color = "#000";
    useImperativeHandle(ref, () => ({
        getImage: () => new Promise((resolve,reject) => {
            // 返回处理后的数据形式
        })
    }));

    useEffect(() => {
        const el = document.querySelector("#signCanvas");
        el?.addEventListener("touchstart", handleStart, false);
        el?.addEventListener("touchend", handleEnd, false);
        el?.addEventListener("touchmove", handleMove, false);
    }, [])
    
    // 所有方法参考MDN内的实现方法
    const ongoingTouches = [];
    function copyTouch(touch) {}
    function ongoingTouchIndexById(idToFind) { }
    function handleStart(evt) {}
    function handleMove(evt) {}
    function handleEnd(evt) {}
  return (
        <canvas id="signCanvas" width="600" height="600" style={{border: "solid #aaa 1px"}}>
          你的浏览器不支持 canvas 元素。
        </canvas>
  );
};

export default forwardRef(Signature);

2. 注意细节

1. 获取canvas元素

ini 复制代码
// MDN中获取元素的类型
const el = document.getElementsByTagName("canvas")[0];

如果代码中已经有了canvas元素,比如antd中使用的QrCode组件。

2. 坐标设置(无滚动条)

MDN中的demo使用的pageX和pageY默认使用的是屏幕左上角,我使用的时候用的是弹窗打开,弹窗位于页面中间。 此时应该基于弹窗的位置进行设置。

为啥是top*2?试出来的,我也不知道

js 复制代码
// 在start方法中挪动坐标位置
    function handleStart(evt) {
        evt.preventDefault();
        const el = document.querySelector("#signCanvas");
        const ctx = el?.getContext("2d");
        const { left, top } = el?.getBoundingClientRect();
        ctx?.translate(0 - left, 0 - top*2);
        .......
     }
// 每次画完再把坐标复原,不然,坐标每画一次挪一次,就越来越远了。
    function handleEnd(evt) {
        evt.preventDefault();
        const el = document.querySelector("#signCanvas");
        const ctx = el?.getContext("2d");       
        const { left, top } = el?.getBoundingClientRect();
        ctx?.translate(0 + left, 0 + top*2);  
        ......
    }

3. 坐标取值(有滚动条)

当弹窗下的页面出现滚动条时,pageX,pageY会把滚动条计算进去。此时要改成screenX,screenY

4. 将签名转图片保存

1. 转base64图片

js 复制代码
            const el = document.querySelector("#signCanvas");
            el?.toBlob((blob) => {
                var reader = new FileReader();
                reader.readAsDataURL(blob); 
                reader.onloadend = function() {
                  var base64data = reader.result;   
                  resolve(base64data)        
                }
            },  "image/png")

2. 转file类型

js 复制代码
// 解析文件
export const base64ToFile = (dataurl) => {
  const arr = dataurl.split(',');
  const mime = arr[0].match(/:(.*?);/)[1];
  const bstr = window.atob(arr[1]);
  let n = bstr.length;
  const u8arr = new Uint8Array(n);
  while (n--) {
    u8arr[n] = bstr.charCodeAt(n);
  }
  let blob = new File([u8arr], 'file.png', { type: mime });
  return blob; 
};

// 将base64转为file
  const file = base64ToFile(base64data);   

5. 兼容性

用到了Touch事件,不支持safari浏览器

相关推荐
花生侠13 分钟前
记录:前端项目使用pnpm+husky(v9)+commitlint,提交代码格式化校验
前端
一涯20 分钟前
Cursor操作面板改为垂直
前端
我要让全世界知道我很低调27 分钟前
记一次 Vite 下的白屏优化
前端·css
1undefined229 分钟前
element中的Table改造成虚拟列表,并封装成hooks
前端·javascript·vue.js
蓝倾1 小时前
淘宝批量获取商品SKU实战案例
前端·后端·api
comelong1 小时前
Docker容器启动postgres端口映射失败问题
前端
花海如潮淹1 小时前
硬件产品研发管理工具实战指南
前端·python
用户3802258598241 小时前
vue3源码解析:依赖收集
前端·vue.js
WaiterL1 小时前
一文读懂 MCP 与 Agent
前端·人工智能·cursor
gzzeason1 小时前
使用Vite创建React初始化项目
前端·javascript·react.js