vue实现手写签名及组件封装调用

手写签名主要是用canvas实现。通过监听手指触碰屏幕事件来完成(touchstart、touchmove、touchcancel)

组件封装

demo: <handSignature uploadUrl="/sysFile/uploadFiles.html" v-model="fileUrl" @afterUpload="testSign"></handSignature>
v-model====>签名图片保存地址
afterUpload====>保存图片后回调,保存地址参数

<template>
  <div style="width: 100%;height: 100%" @touchmove.prevent>
    <!--    防止页面滚动-->
    <div class="boardBox" ref="boardBox">
      <canvas ref="board" id="canvas" @touchstart="mStart" @touchmove="mMove" @touchend="mEnd"></canvas>
    </div>
    <div class="bar">
      <div class="item" @click="goBack">
        <div class="ico">
          <img src="@/assets/img/backOut.png" srcset/>
        </div>
        <div class="text">退出</div>
      </div>
      <div class="item" @click="clearCanvas">
        <div class="ico">
          <img src="@/assets/img/refreshSign.png" srcset/>
        </div>
        <div class="text">重绘</div>
      </div>
      <div class="item" @click="getCanvas">
        <div class="ico">
          <img src="@/assets/img/sureSave.png" srcset/>
        </div>
        <div class="text">保存</div>
      </div>
    </div>
  </div>
</template>

<script>
  export default {
    name: "handSignature",
    data() {
      return {
        basedata: "",
        ctx: null,
        point: {x: 0, y: 0,},
        moving: false, // 是否正在绘制中且移动
      }
    },
    props: {
      uploadUrl: {type: String, default: ""},//上传路径
      value: {type: String, default: ""},//保存路径
    },
    methods: {
      // 触摸(开始)
      mStart(e) {
        let x = e.touches[0].clientX - e.target.offsetLeft,
          y = e.touches[0].clientY - e.target.offsetTop; // 获取触摸点在画板(canvas)的坐标
        this.point.x = x;
        this.point.y = y;
        this.ctx.beginPath();
        this.moving = true;
      },
      // 滑动中...
      mMove(e) {
        if (this.moving) {
          let x = e.touches[0].clientX - e.target.offsetLeft,
            y = e.touches[0].clientY - e.target.offsetTop; // 获取触摸点在画板(canvas)的坐标
          this.ctx.moveTo(this.point.x, this.point.y); // 把路径移动到画布中的指定点,不创建线条(起始点)
          this.ctx.lineTo(x, y); // 添加一个新点,然后创建从该点到画布中最后指定点的线条,不创建线条
          this.ctx.stroke(); // 绘制
          (this.point.x = x), (this.point.y = y); // 重置点坐标为上一个坐标
        }
      },
      // 滑动结束
      mEnd() {
        if (this.moving) {
          this.ctx.closePath(); // 停止绘制
          this.moving = false; // 关闭绘制开关
        }
      },
      getCanvas() {
        //绘画转图片
        document.getElementById("canvas").toDataURL("image/png");
        document.getElementById("canvas").toBlob(async (blobObj) => {
          var file1 = new File([blobObj], "pic.png", {
            type: blobObj.type,
            lastModified: Date.now(),
          });
          this.convertImg(file1);
        });
      },
      //旋转图片
      convertImg(file) {
        let _this = this;
        var canvas1 = document.createElement("canvas");
        var context1 = canvas1.getContext("2d");
        var oReader = new FileReader();
        oReader.readAsDataURL(file);
        oReader.onload = function (e) {
          var img = new Image();
          img.src = e.target.result;
          img.onload = function () {
            // 图片原始尺寸
            var originWidth = this.width;
            var originHeight = this.height;
            // 最大尺寸限制
            var maxWidth = 1080,
              maxHeight = 1080;
            // 目标尺寸
            var targetWidth = originWidth,
              targetHeight = originHeight;
            // 图片尺寸超过300x300的限制
            if (originWidth > maxWidth || originHeight > maxHeight) {
              if (originWidth / originHeight > maxWidth / maxHeight) {
                targetWidth = maxWidth;
                targetHeight = Math.round(
                  maxWidth * (originHeight / originWidth)
                );
              } else {
                targetHeight = maxHeight;
                targetWidth = Math.round(
                  maxHeight * (originWidth / originHeight)
                );
              }
            }
            var type = "image/jpeg";
            // canvas对图片进行缩放
            canvas1.width = targetHeight;
            canvas1.height = targetWidth;
            // 旋转90度
            context1.translate(0, 0);
            context1.rotate(Math.PI / 2);
            // (0,-imgHeight) 从旋转原理图那里获得的起始点
            // context.clearRect(0,  -targetHeight, targetWidth, targetHeight);
            context1.drawImage(img, 0, -targetHeight, targetWidth, targetHeight);
            // 将canvas的透明背景设置成白色
            var imageData = context1.getImageData(
              0,
              0,
              canvas1.width,
              canvas1.height
            );
            for (var i = 0; i < imageData.data.length; i += 4) {
              // 当该像素是透明的,则设置成白色
              if (imageData.data[i + 3] == 0) {
                imageData.data[i] = 255;
                imageData.data[i + 1] = 255;
                imageData.data[i + 2] = 255;
                imageData.data[i + 3] = 255;
              }
            }
            context1.putImageData(imageData, 0, 0);
            var dataurl = canvas1.toDataURL(type);
            _this.basedata = dataurl;
            // console.log(dataurl);
            _this.updatavue();
          };
        };
      },
      //base64转Blob
      base64ToBlob(base64Data) {
        let arr = base64Data.split(","),
          fileType = arr[0].match(/:(.*?);/)[1],
          bstr = atob(arr[1]),
          l = bstr.length,
          u8Arr = new Uint8Array(l);

        while (l--) {
          u8Arr[l] = bstr.charCodeAt(l);
        }
        return new Blob([u8Arr], {
          type: fileType,
        });
      },
      //上传图片
      async updatavue() {
        //转成file文件
        let blobObj = this.base64ToBlob(this.basedata);
        var file = new File([blobObj], "sign.png", {
          type: blobObj.type,
          lastModified: Date.now(),
        });
        //此处为发送请求给后台获取图片路径
        this.uploadImg(file);
      },
      //清除画布
      clearCanvas() {
        var c = document.getElementById("canvas");
        var cxt = c.getContext("2d");
        c.height = c.height;
        this.ctx.lineWidth = 3;
      },
      //返回上一级
      goBack() {
        this.$emit("closeSign");
      },
      uploadImg(file) {//上传签名图片
        const formData = new FormData();
        formData.append('file', file);
        this.axiosPost(this.global.sysUrl + this.uploadUrl, formData, (resData) => {
          if (resData._result.code == 200) {
            this.$emit('input', resData.uploadList[0].url);
            this.$emit("afterUpload", resData.uploadList[0].url);
          }else{
            this.$emit("afterUpload", undefined);
          }
        });
      }
    },
    mounted() {
      let board = this.$refs.board; // 获取DOM
      board.width = this.$refs.boardBox.offsetWidth; // 设置画布宽
      board.height = this.$refs.boardBox.offsetHeight; // 设置画布高
      this.ctx = board.getContext("2d"); // 二维绘图
      this.ctx.strokeStyle = "#000"; // 颜色
      this.ctx.lineWidth = 3; // 线条宽度
    },
  }
</script>

<style lang="less" scoped>
  .loadingtext {
    transform: rotate(-90deg);
    color: red;
  }

  .item {
    -webkit-transform: rotate(-90deg);

    .ico {
      width: 30px;
      height: 30px;

      img {
        width: 100%;
        height: 100%;
      }
    }
  }

  .text {
    text-align: center;
  }

  .boardBox {
    width: 100%;
    height: 90%;
    background: #f9f9f9;
  }

  .bar {
    box-sizing: border-box;
    padding: 0rem 1rem;
    display: flex;
    width: 100%;
    height: 10%;
    background-color: #ffffff;
    justify-content: space-around;
    align-items: center;
  }

  .shade {
    width: 100%;
    height: 100%;
    position: fixed;
    top: 0;
    background-color: #333333;
    z-index: 66666;
    opacity: 0.9;

    .minishade {
      width: 100%;
      height: 100%;
      display: flex;
      align-items: center;
      justify-content: center;
    }
  }
</style>

组件调用

<template>
    <div style="height: 100vh;width: 100vw">
      <handSignature uploadUrl="/sysFile/uploadFiles.html" v-model="signImgUrl" @afterUpload="afterUpload" @closeSign="closeSign"></handSignature>
    </div>
</template>

<script>
  import {Toast} from 'vant';

  export default {
      data () {
        return {
          signImgUrl: '',//签名图片地址
        }
      },
      methods:{
        afterUpload(){//图片保存上传完毕进行修改个人签名图片路径数据
        },
        closeSign(){
          this.$router.push("/h5/BehaviorSafetyMenu");
        }
      },
      watch: {

      },
      mounted() {

      }
    }
</script>

<style scoped>

</style>

签名效果

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Da白兔萘糖

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值