原有的业务逻辑是用户线下签字,然后拍照后将照片上传到后台系统。用户将手机端照片转移到电脑也很复杂。特别是对于非自己的电脑的图片上传。所以将业务转到ipad或者surface上,减少操作逻辑。
1. 实现操作逻辑
这个直接参考MDN的触摸事件的文档。里面提供了完整的代码。
将其整理成React组件形式如下:
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元素
// MDN中获取元素的类型
const el = document.getElementsByTagName("canvas")[0];
如果代码中已经有了canvas元素,比如antd中使用的QrCode组件。
2. 坐标设置(无滚动条)
MDN中的demo使用的pageX和pageY默认使用的是屏幕左上角,我使用的时候用的是弹窗打开,弹窗位于页面中间。 此时应该基于弹窗的位置进行设置。
// 在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);
.......
}
// 每次画完再把坐标复原,不然,坐标每画一次挪一次,就越来越远了。
function handleEnd(evt) {
evt.preventDefault();
const el = document.querySelector("#signCanvas");
const ctx = el?.getContext("2d");
const { left, top } = el?.getBoundingClientRect();
const touches = evt.changedTouches;
for (let i = 0; i < touches.length; i++) {
const idx = ongoingTouchIndexById(touches[i].identifier);
if (idx >= 0) {
...
// 在终点画一个正方形
ongoingTouches.splice(idx, 1); // 用完后移除
// 结束时恢复ctx,便于下次使用
ctx?.translate(0 + left, 0 + top);
}
}
......
}
3. 坐标取值(有滚动条)
当弹窗下的页面出现滚动条时,pageX,pageY会把滚动条计算进去。此时要改成clientX,clientY。
4. 将签名转图片保存
1. 转base64图片
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类型
// 解析文件
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. 兼容性
MON的浏览器兼容性表格中,提到用到了Touch事件,不支持safari浏览器。
但是!官方demo在ipad的safari浏览器中可以正常打开!