canvas电子签名

48 阅读1分钟
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      * {
        margin: 0;
        padding: 0;
      }
    </style>
  </head>
  <body>
    <div
      style="
        background-color: pink;
        width: 400px;
        height: 400px;
        margin: 0 auto;
        margin-top: 200px;
      "
    >
      <canvas></canvas>
      <div>
        <button onclick="cancel()">取消</button>
        <button onclick="save()">保存</button>
      </div>
    </div>
  </body>

  <script>
    const config = {
      width: 400, // 宽度
      height: 400, // 高度
      lineWidth: 3, // 线宽
      strokeStyle: "#000", // 线条颜色
      lineCap: "round", // 设置线条两端圆角
      lineJoin: "round", // 线条交汇处圆角
    };
    const canvas = document.querySelector("canvas");
    canvas.width = config.width;
    canvas.height = config.height;

    const ctx = canvas.getContext("2d");
    ctx.fillStyle = "transparent";

    ctx.fillRect(0, 0, config.width, config.height);

    const mobileStatus = /Mobile|Android|iPhone/i.test(navigator.userAgent);

    const draw = (event) => {
      // 获取当前坐标点位
      const { offsetX, offsetY } = mobileStatus
        ? event.changedTouches[0]
        : event;
      // 根据坐标点位移动添加线条
      ctx.lineTo(offsetX, offsetY);

      // 绘制
      ctx.stroke();
    };

    const init = (event) => {
      // 获取偏移量及坐标
      const { offsetX, offsetY, pageX, pageY } = mobileStatus
        ? event.changedTouches[0]
        : event;

      // 清除以上一次 beginPath 之后的所有路径,进行绘制
      ctx.beginPath();

      // 根据配置文件设置进行相应配置
      ctx.lineWidth = config.lineWidth;
      ctx.strokeStyle = config.strokeStyle;
      ctx.lineCap = config.lineCap;
      ctx.lineJoin = config.lineJoin;

      // 设置画线起始点位
      ctx.moveTo(offsetX, offsetY);

      // 监听 鼠标移动或手势移动
      canvas.addEventListener(mobileStatus ? "touchmove" : "mousemove", draw);
    };

    canvas.addEventListener(mobileStatus ? "touchstart" : "mousedown", init);

    // 结束绘制
    const cloaseDraw = () => {
      // 结束绘制
      ctx.closePath();
      // 移除鼠标移动或手势移动监听器
      canvas.removeEventListener("mousemove", draw);
    };

    // 创建鼠标/手势 弹起/离开 监听器
    canvas.addEventListener(mobileStatus ? "touchend" : "mouseup", cloaseDraw);

    canvas.addEventListener("mouseout", cloaseDraw);

    const cancel = () => {
      // 清空当前画布上的所有绘制内容
      ctx.clearRect(0, 0, config.width, config.height);
    };

    // 保存-将画布内容保存为图片
    const save = () => {
      // 将canvas上的内容转成blob流
      canvas.toBlob((blob) => {
        // 获取当前时间并转成字符串,用来当做文件名
        const date = Date.now().toString();
        // 创建一个 a 标签
        const a = document.createElement("a");
        console.log(URL.createObjectURL(blob));
        // 设置 a 标签的下载文件名
        a.download = `${date}.png`;
        // 设置 a 标签的跳转路径为 文件流地址
        a.href = URL.createObjectURL(blob);
        // 手动触发 a 标签的点击事件
        a.click();
        // 移除 a 标签
        a.remove();
      });
    };
  </script>
</html>