使用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";
}

 

posted @ 2022-06-01 16:45  木杉呀  阅读(189)  评论(0编辑  收藏  举报