使用canvas实现签名功能
使用canvas画签名并上传到阿里云
import React, { useEffect, useRef, useState } from "react";
import CanvasDraw from "react-canvas-draw";
import MessageBox from "../../../../components/message-box"; //弹框
import request from "../../../../network/request";
import axios from "axios";
import styles from "./index.module.scss";
import getExploreName from "./util";
import { useHistory, useLocation } from "react-router-dom";
import Loading from "../../../../components/loading";
export default function SignatureTemplate(props) {
const saveBlen: any = useRef(null);
let baseUrl = useRef("");
const [width, setWidth] = useState(0);
const [height, setHeight] = useState(0);
const history = useHistory();
const location = useLocation();
useEffect(() => {
// eslint-disable-next-line no-restricted-globals
// console.log(location,history, 'props');
history.listen((pre) => {
console.log(location.pathname, pre.pathname, "路由变化");
if (location.pathname !== pre.pathname) {
Loading.hide();
MessageBox.removeAll();
}
});
}, [location]);
function windowChangeEvent() {
let visualWidth =
window.innerWidth ||
document.documentElement.clientWidth ||
document.body.clientWidth; // 获取当前窗口的宽度(包含滚动条) || 可视区域宽
let visualHeight =
window.innerHeight ||
document.documentElement.clientHeight ||
document.body.clientHeight; // 获取当前窗口的高度(包含滚动条) || 可视区域高
let bowser = getExploreName(); //获取浏览器类型和版本
console.log(visualWidth, visualHeight, bowser, "bowser");
setWidth(visualWidth);
setHeight(visualHeight);
// 把对应的canvas都修改
for (let key in saveBlen.current) {
if (saveBlen.current.canvas) {
saveBlen.current.canvas[key].width = visualWidth;
saveBlen.current.canvas[key].height = visualHeight;
}
}
// 兼容 IOS 手机上非原生浏览器
if (window.orientation === 90 || window.orientation === -90) {
//判断屏幕旋转角度
// 横屏
if (visualWidth <= visualHeight || bowser === "Unkonwn") {
visualWidth = window.screen.height; // 屏幕分辨率高
visualHeight = window.screen.width; // 屏幕分辨率宽
}
} else {
// 竖屏
if (visualHeight <= visualWidth || bowser === "Unkonwn") {
visualWidth = window.screen.width; // 屏幕分辨率宽
visualHeight = window.screen.height; // 屏幕分辨率高
}
}
}
let preventDefault = (e) => {
e.preventDefault();
};
useEffect(() => {
console.log(saveBlen, "saveBlen");
// 缓存空白 canvas 的 base64
// eslint-disable-next-line react-hooks/exhaustive-deps
//返回base64的png格式图片,保留最初的空画板(签名后base64会发生变化)
baseUrl = saveBlen.current.getDataURL("image/png", "", "#fff"); //getDataURL是canvas提供的方法
// 进入页面禁止滚动
document.addEventListener("touchmove", preventDefault, false);
// 组件销毁时恢复页面滚动
return () => {
window.addEventListener(
"popstate",
(e) => {
// 恢复页面滚动
document.removeEventListener("touchmove", preventDefault, false);
},
false
);
};
}, []);
// 监听屏幕大小变化
useEffect(() => {
window.addEventListener("resize", windowChangeEvent, {
passive: false,
});
console.log(saveBlen, "saveBlen");
saveBlen.current.canvas.grid.style.background = "#eee";
return () => {
window.removeEventListener("resize", windowChangeEvent);
};
}, []);
// 清空签名画板
const Eliminate = () => {
saveBlen.current.clear();
};
// 确认签名
const confirm = async (): Promise<any> => {
Loading.show("签名中");
let pic = saveBlen.current.getDataURL("image/png", "", "#fff"); //返回base64的png格式图片
// 比较是否是空白 canvas
if (baseUrl === pic) {
MessageBox.confirm("请先签名,在确认提交!", {
showCancelButton: false,
}).then(() => {
Loading.hide();
});
return;
}
// base64转出file对象
const data = pic.split(",");
const mime = data[0].match(/:(.*?);/)![1];
const binary = atob(data[1].trim());
// 字符转换为二进制格式
let n = binary.length;
const u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = binary.charCodeAt(n);
}
// 转换成file对象
const file = new File([u8arr], `${new Date().getTime()}.png`, {
type: mime,
});
console.log(file, "file");
var reader = new FileReader();
reader.readAsArrayBuffer(file); // 这个读法是异步的
reader.onloadend = function () {
upload("/getUploadUrl", reader.result, file);
};
};
const upload = async (url: string, binary: any, fileObj: any) => {
// 配置上传到阿里云的配置(headers)
let config = {
headers: {
"Content-Type": "application/octet-stream", //Content-Type,告知浏览器这是一个字节流
},
};
try {
// 请求后端接口,得到阿里云上传地址
const data: any = await request(url, { fileName: fileObj.name }, "POST");
console.log(data, "data");
let formatFile = fileObj.name + "|" + data.downloadUrl;
console.log(data.downloadUrl, "data.downloadUrl");
console.log(formatFile, "formatFile");
let uploadUrl = data.uploadUrl; //阿里云上传地址
axios
.put(uploadUrl, binary, config)
.then(() => {
// 此时阿里云已经上传成功
let params;
let saveUrl = "xxxxx";
params = {
id: "id",
signPic: formatFile,
};
// 调用后端接口,记录上传成功的文件
request(saveUrl, params, "POST").then((res) => {
MessageBox.toast("上传成功");
history.goBack();
});
Loading.hide();
})
.catch((err) => {
console.log(err);
Loading.hide();
MessageBox.confirm("系统异常,请重新签名!", {
showCancelButton: false,
}).then(() => {
Eliminate();
});
});
} catch (err) {
Loading.hide();
MessageBox.confirm("系统异常,请重新签名!", {
showCancelButton: false,
}).then(() => {
Eliminate();
});
}
};
return (
<div>
{/* hideGrid 隐藏表格 */}
<div className={styles.draw}>
<CanvasDraw
ref={saveBlen}
hideGrid
hideInterface
canvasWidth={width !== 0 ? width : document.body.clientWidth}
canvasHeight={
height !== 0 ? height - 60 : document.body.clientHeight - 60
}
brushRadius={2}
lazyRadius={2}
/>
</div>
<div className={styles.singeBottom}>
<div className={styles.singeDescribe}>请在上方空白处签下您的姓名</div>
<div className={styles.singeBtn}>
<button type="button" className={styles.btnClear} onClick={Eliminate}>
重置
</button>
<button type="button" className={styles.btnSure} onClick={confirm}>
确认
</button>
</div>
</div>
</div>
);
}
获取浏览器信息
export default function getExploreName() {
var Sys: any = {};
var ua = navigator.userAgent.toLowerCase();
var s;
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
(s = ua.match(/rv:([\d.]+)\) like gecko/))
? (Sys.ie = s[1])
: (s = ua.match(/msie ([\d\.]+)/))
? (Sys.ie = s[1])
: (s = ua.match(/edge\/([\d\.]+)/))
? (Sys.edge = s[1])
: (s = ua.match(/firefox\/([\d\.]+)/))
? (Sys.firefox = s[1])
: (s = ua.match(/(?:opera|opr).([\d\.]+)/))
? (Sys.opera = s[1])
: (s = ua.match(/chrome\/([\d\.]+)/))
? (Sys.chrome = s[1])
: (s = ua.match(/version\/([\d\.]+).*safari/))
? (Sys.safari = s[1])
: 0;
// 根据关系进行判断
if (Sys.ie) return "IE: " + Sys.ie;
if (Sys.edge) return "EDGE: " + Sys.edge;
if (Sys.firefox) return "Firefox: " + Sys.firefox;
if (Sys.chrome) return "Chrome: " + Sys.chrome;
if (Sys.opera) return "Opera: " + Sys.opera;
if (Sys.safari) return "Safari: " + Sys.safari;
return "Unkonwn";
}

浙公网安备 33010602011771号