移动端手写签名

本文探讨了如何在前端项目中使用vue-signature-pad和react-signature-canvas组件处理横屏和竖屏切换时的签名偏移问题,并介绍了如何在全屏模式下自定义canvas行为以适应不同屏幕尺寸。
摘要由CSDN通过智能技术生成

当前端遇见了强制横屏签字的需求...

使用的是

vue:vue-signature-pad 

react:react-signature-canvas

看下效果

  1. touchstart:当用户在触摸设备上触摸屏幕时触发。相当于鼠标事件中的mousedown,表示用户开始触摸屏幕。

  2. touchmove:当用户在触摸设备上滑动手指时触发。相当于鼠标事件中的mousemove,表示用户正在移动手指。

  3. touchend:当用户在触摸设备上松开手指时触发。相当于鼠标事件中的mouseup,表示用户结束触摸操作。

  4.touchcancel:当触摸事件被取消时触发,例如由于系统事件或触摸点超出了浏览器边界。类似于mouseleave事件,表示触摸操作在某种情况下被取消
 

这里在切换成竖屏的时候会有偏移量

<template>
  <div id="app">
    <div style="background: #fff; border:1px solid red;">
      <vue-signature-pad
        id="signature"
        width="95%"
        height="400px"
        ref="signaturePad"
        :options="options"
		v-if='fullScrean'
      />
	  <vue-signature-pad
	    id="signature"
	    width="95%"
	    height="200px"
	    ref="signaturePad"
	    :options="options"
		v-if='!fullScrean'
	  />
    </div>

    <div v-for="(item, index) in imgList" :key="index">
      <img :src="item.src" alt="" width="100" />
    </div>

    <div class="buttons">
		<button @click="fullScreanFuc" class="btn">全屏</button>
      <button @click="save" class="btn">保存</button>
      <button @click="resume" class="btn">重置</button>
    </div>
  </div>
</template>

<script>
export default {
  name: 'App',
  data() {
    return {
      options: {
        penColor: '#000',
      },
      imgList: [],
      fileList: [],
	  fullScrean:false,
    }
  },
  methods: {
	  fullScreanFuc(){
		this.fullScrean = !this.fullScrean
		this.imgList = []
		this.fileList = []
		this.resume()
		if(this.fullScrean){
			console.log(this.fullScrean)
			console.log(this.$refs.signaturePad)
			var canvas = document.createElement('canvas')
			var ctx = canvas.getContext('2d')
			ctx.strokeStyle = 'black';
			        ctx.lineWidth = 2;
			
			        // 定义变量来跟踪绘画状态和偏移量
			        var isDrawing = false;
			        var offsetYPercent = 0.5; // 20%
			
			        // 计算偏移量
			        var offsetY = canvas.height * offsetYPercent;
			        
			        // 监听鼠标按下事件
			        canvas.addEventListener('mousedown', function(event) {
			            isDrawing = true;
			            // 将绘制路径的起始点移动到鼠标点击位置
			            ctx.beginPath();
			            var x = event.clientX - canvas.offsetLeft;
			            var y = event.clientY - canvas.offsetTop + offsetY; // 添加偏移量
			            ctx.moveTo(x, y);
			        });
			
			        // 监听鼠标移动事件
			        canvas.addEventListener('mousemove', function(event) {
			            if (isDrawing) {
			                var x = event.clientX - canvas.offsetLeft;
			                var y = event.clientY - canvas.offsetTop + offsetY; // 添加偏移量
			                // 绘制线条到当前鼠标位置
			                ctx.lineTo(x, y);
			                ctx.stroke();
			            }
			        });
			
			        // 监听鼠标松开事件
			        canvas.addEventListener('mouseup', function() {
			            isDrawing = false;
			        });
			
			        // 监听鼠标离开 Canvas 区域事件
			        canvas.addEventListener('mouseleave', function() {
			            isDrawing = false;
			        });
		}
	  },
    save() {
      const { isEmpty, data } = this.$refs.signaturePad.saveSignature()
		console.log(isEmpty,data)
		if(this.fullScrean){
			this.rotateBase64Img(data, 90, (res) => {
			  console.log(res) // 旋转后的base64图片src
			  this.fileList.push({
			    file: this.dataURLtoFile(res, 'sign'),
			    name: 'sign',
			  })
			  this.imgList.push({
			    src: res,
			  })
			})
		}else{
			this.imgList.push({
			  src: data,
			})
		}


    },

    // 清除重置
    resume() {
      this.$refs.signaturePad.clearSignature()
    },

    // 将base64转换为文件
    dataURLtoFile(dataurl, filename) {
      var arr = dataurl.split(','),
        mime = arr[0].match(/:(.*?);/)[1],
        bstr = atob(arr[1]),
        n = bstr.length,
        u8arr = new Uint8Array(n)

      while (n--) {
        u8arr[n] = bstr.charCodeAt(n)
      }

      return new File([u8arr], filename, { type: mime })
    },

    // 通过canvas旋转图片
    rotateBase64Img(src, edg, callback) {
      var canvas = document.createElement('canvas')
      var ctx = canvas.getContext('2d')

      var imgW //图片宽度
      var imgH //图片高度
      var size //canvas初始大小

      if (edg % 90 != 0) {
        console.error('旋转角度必须是90的倍数!')
        throw '旋转角度必须是90的倍数!'
      }
      edg < 0 && (edg = (edg % 360) + 360)
      const quadrant = (edg / 90) % 4 //旋转象限
      const cutCoor = { sx: 0, sy: 0, ex: 0, ey: 0 } //裁剪坐标

      var image = new Image()

      image.crossOrigin = 'anonymous'
      image.src = src

      image.onload = function () {
        imgW = image.width
        imgH = image.height
        size = imgW > imgH ? imgW : imgH

        canvas.width = size * 2
        canvas.height = size * 2
        switch (quadrant) {
          case 0:
            cutCoor.sx = size
            cutCoor.sy = size
            cutCoor.ex = size + imgW
            cutCoor.ey = size + imgH
            break
          case 1:
            cutCoor.sx = size - imgH
            cutCoor.sy = size
            cutCoor.ex = size
            cutCoor.ey = size + imgW
            break
          case 2:
            cutCoor.sx = size - imgW
            cutCoor.sy = size - imgH
            cutCoor.ex = size
            cutCoor.ey = size
            break
          case 3:
            cutCoor.sx = size
            cutCoor.sy = size - imgW
            cutCoor.ex = size + imgH
            cutCoor.ey = size + imgW
            break
        }

        ctx.translate(size, size)
        ctx.rotate((edg * Math.PI) / 180)
        ctx.drawImage(image, 0, 0)

        var imgData = ctx.getImageData(
          cutCoor.sx,
          cutCoor.sy,
          cutCoor.ex,
          cutCoor.ey
        )

        if (quadrant % 2 == 0) {
          canvas.width = imgW
          canvas.height = imgH
        } else {
          canvas.width = imgH
          canvas.height = imgW
        }
        ctx.putImageData(imgData, 0, 0)
        callback(canvas.toDataURL())
      }
    },
  },
}
</script>

<style lang="scss">
html,
body {
  padding: 0;
  margin: 0;
}
#app {
  width: 100vw;
  height: 100vh;
  background: #ececec;
}

.btn {
  width: 35%;
  color: #fff;
  background: #5daaf3;
  border: none;
  height: 40px;
  border-radius: 20px;
  margin-top: 20px;
  margin-left: 40px;
}
</style>

就要设置偏移量,但是这个组件内部封装了canvas,,不太好设置

于是自己写了个

<template>
  <view class="content">
    <view class="text-area">
      <canvas
        id="drawCol"
        ref="drawCol"
        v-if="isCol"
        width="400"
        height="400"
      ></canvas>
      <canvas
        id="drawRow"
        ref="drawRow"
        v-if="!isCol"
        width="400"
        height="800"
      ></canvas>
    </view>
    <img
      :src="src"
      v-if="isCol"
      style="border: 1px solid red; width: 200px; height: 200px"
    />
    <img
      :src="src"
      v-if="!isCol"
      style="border: 1px solid red; width: 400px; height: 200px"
    />
    <br />
    <button @click="drawCol('drawCol')">横屏</button>
    <br />
    <button @click="drawRow('drawRow')">竖屏</button>
    <br />
    <button @click="submit">提交</button>
    <br />
    <button @click="hasContentFuc">有值吗</button>
    {{ hasContent }}
  </view>
</template>

<script>
export default {
  data() {
    return {
      title: "Hello",
      isCol: true,
      src: "",
      hasContent: false,
    };
  },
  mounted() {},
  methods: {
    rotateBase64Img(src, edg, callback) {
      var canvas = document.createElement("canvas");
      var ctx = canvas.getContext("2d");
      var imgW; //图片宽度
      var imgH; //图片高度
      var size; //canvas初始大小

      if (edg % 90 != 0) {
        console.error("旋转角度必须是90的倍数!");
        throw "旋转角度必须是90的倍数!";
      }
      edg < 0 && (edg = (edg % 360) + 360);
      const quadrant = (edg / 90) % 4; //旋转象限
      const cutCoor = { sx: 0, sy: 0, ex: 0, ey: 0 }; //裁剪坐标
      var image = new Image();
      image.crossOrigin = "anonymous";
      image.src = src;
      image.onload = function () {
        imgW = image.width;
        imgH = image.height;
        size = imgW > imgH ? imgW : imgH;

        canvas.width = size * 2;
        canvas.height = size * 2;
        switch (quadrant) {
          case 0:
            cutCoor.sx = size;
            cutCoor.sy = size;
            cutCoor.ex = size + imgW;
            cutCoor.ey = size + imgH;
            break;
          case 1:
            cutCoor.sx = size - imgH;
            cutCoor.sy = size;
            cutCoor.ex = size;
            cutCoor.ey = size + imgW;
            break;
          case 2:
            cutCoor.sx = size - imgW;
            cutCoor.sy = size - imgH;
            cutCoor.ex = size;
            cutCoor.ey = size;
            break;
          case 3:
            cutCoor.sx = size;
            cutCoor.sy = size - imgW;
            cutCoor.ex = size + imgH;
            cutCoor.ey = size + imgW;
            break;
        }

        ctx.translate(size, size);
        ctx.rotate((edg * Math.PI) / 180);
        ctx.drawImage(image, 0, 0);

        var imgData = ctx.getImageData(
          cutCoor.sx,
          cutCoor.sy,
          cutCoor.ex,
          cutCoor.ey
        );

        if (quadrant % 2 == 0) {
          canvas.width = imgW;
          canvas.height = imgH;
        } else {
          canvas.width = imgH;
          canvas.height = imgW;
        }
        ctx.putImageData(imgData, 0, 0);
        callback(canvas.toDataURL());
      };
    },
    hasContentFuc() {
      let canvas = document.getElementsByTagName("canvas");

      console.log(1, canvas);
      let ctx = canvas.getContext("2d");
      let imgData = ctx.getImageData(0, 0, canvas.width, canvas.height);
      let data = imgData.data;
      for (let i = 0; i < data.length; i += 4) {
        if (data[i + 3] > 0) {
          this.hasContent = true;
          break;
        }
      }
    },
    submit() {
      if (this.isCol) {
        this.src = this.$refs.drawCol?.toDataURL();
      } else {
        this.rotateBase64Img(this.$refs.drawRow?.toDataURL(), 270, (res) => {
          this.src = res;
        });
      }
      console.log(this.$refs.drawCol, this.$refs.drawCol?.toDataURL());
      console.log(this.$refs.drawRow, this.$refs.drawRow?.toDataURL());
    },
    drawCol(e) {
      this.isCol = true;
      this.$nextTick(() => {
        this.startDrawn(e);
      });
    },
    drawRow(e) {
      this.isCol = false;
      this.$nextTick(() => {
        this.startDrawn(e);
      });
    },
    startDrawn(e) {
      var canvas = document.getElementById(e);
      var ctx = canvas.getContext("2d");
      ctx.strokeStyle = "black";
      ctx.lineWidth = 2;
      canvas.addEventListener("touchstart", startDrawing);
      canvas.addEventListener("touchmove", continueDrawing);
      canvas.addEventListener("touchend", stopDrawing);

      var isDrawing = false;
      function startDrawing(e) {
        isDrawing = true;
        var touch = e.touches[0];
        var x = touch.clientX;
        var y = touch.clientY;
        ctx.beginPath();
        ctx.moveTo(x, y);
      }
      function continueDrawing(e) {
        if (!isDrawing) return;
        var touch = e.touches[0];
        var x = touch.clientX;
        var y = touch.clientY;
        ctx.lineTo(x, y);
        ctx.stroke();
      }

      function stopDrawing() {
        isDrawing = false;
      }
      // canvas.addEventListener("touchcancel", function () {
      //   isDrawing = false;
      // });
    },
  },
};
</script>

<style>
/* 	.text-area {
		border:1px solid red;
	} */
canvas {
  border: #333 1px solid;
}
</style>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

每天吃饭的羊

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

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

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

打赏作者

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

抵扣说明:

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

余额充值