使用canvas模拟签字功能

<template>
    <div class="indexs">
      <!-- 签名开始-->
      <div class="signature" v-if="showSignature" @click.stop>
        <div class="signature_content" style="position: relative;">
          <div style="margin-top: 20px;">
            <div style="display: flex;align-items: center;margin-left: 20px;">
              <span>客户签名:</span>
              <div v-if="signImage"
                style="width: 80px;height: 40px;border: 1px dashed red; display: flex;align-items: center;justify-content: center;margin-left: 10px;">
                <van-image v-if="signImage" :src="signImage" width="40px" height="80px"
                  style=" object-fit: contain" />
              </div>
            </div>
          </div>
          <div style="margin-top: 20px;">
            <div style="display: flex;align-items: center;margin-left: 20px;height:20px;line-height: 20px;">
              <span>签名日期:</span>
              <span style="color: #666666;margin-left: 10px;height:20px;line-height: 20px;">{{signDate}}</span>
            </div>
          </div>
        </div>
        <div class="signature_confrim" @click="signImage?signatureConfrim():startSignAndClear()">
          {{signImage?'完成签署':'点击签名'}}
        </div>
      </div>
      <div class="canvas_container" v-show="startSign">
        <canvas id="canvas" @touchstart="canvasDown($event)" @mousedown="canvasDown($event)" @mouseup="canvasUp($event)"
          @mousemove="canvasMove($event)" @touchend="canvasUp($event)" @touchmove="canvasMove($event)">
          您的浏览器不支持canvas,建议使用最新版的Chrome
        </canvas>
        <div
          style="position: absolute;top: 0px;left:20px;padding:10px 20px;">
          请投保人/被保险人张文签字
        </div>
        <div class="signature_control">
          <span @click="signBack" style="color: #999999; padding:5px">返回</span>
          <span @click="clearCanvas" v-show="hasSign">重写</span>
          <span @click="hasSign?signatureComplete():()=>{}" style="background-color:#999999">提交</span>
        </div>
      </div>
      <!-- 签名结束 -->
    </div>
</template>
<script>
let beginPoint = null;
let points = []; // 存放操作点数组  为了用二次贝塞尔曲线画圆滑的线
export default {
    data(){
        return {
            showSignature:true,
            signImage:'',
            signDate:'',
            startSign:true,
            hasSign:true,
            context: {}, //画布对象
            preDrawAry: [],// 存储当前表面状态数组-上一步
            nextDrawAry: [],// 存储当前表面状态数组-下一步
            middleAry: [],// 中间数组
            canvasMoveUse: false,
            canCompleteSignature: false,
            config: {
                lineWidth: 2,
                lineColor: '#000000',
                shadowBlur: 1
            },
        }
    },
    mounted(){
        this.showSign()
    },
    methods:{
        // 签名确认
      signatureConfrim() {
        console.log("签字", this.canCompleteSignature)
        if (!this.canCompleteSignature) {
          this.$toast({
            message: "未签字,请签字"
          })
          return
        }
        this.showSignature = false;
        this.canCompleteSignature = false;

      },
      startSignAndClear() {//点击签名
        this.startSign = true;
        this.clearCanvas();
        
      },
      //mousedown
      canvasDown(e) {
        this.canvasMoveUse = true
        points = [];
        const {
          canvasX,
          canvasY
        } = this.getPos(e);
        console.log('canvasX', canvasX, 'canvasY', canvasY)
        points.push({
          x: canvasX,
          y: canvasY
        });
        beginPoint = {
          x: canvasX,
          y: canvasY
        };
        this.setCanvasStyle() //提前设置好  删除了选择版
        // 清除子路径
        this.context.beginPath()
        this.context.moveTo(canvasX, canvasY)
        // 当前绘图表面状态
        const preData = this.context.getImageData(0, 0, 600, 400)
        // 当前绘图表面进栈
        this.preDrawAry.push(preData)
      },
       // mouseup
      canvasUp() {
        if (points.length > 3) {
          const lastTwoPoints = points.slice(-2);
          const controlPoint = lastTwoPoints[0];
          const endPoint = lastTwoPoints[1];
          this.drawLine(beginPoint, controlPoint, endPoint);
          if (!this.hasSign) {
            this.hasSign = true
          }
        }
        beginPoint = null;
        const preData = this.context.getImageData(0, 0, 600, 400)
        if (!this.nextDrawAry.length) {
          // 当前绘图表面进栈
          this.middleAry.push(preData)
        } else {
          this.middleAry = []
          this.middleAry = this.middleAry.concat(this.preDrawAry)
          this.middleAry.push(preData)
          this.nextDrawAry = []
        }
        this.canvasMoveUse = false
      },
      getPos(e) { //获取操作位置的横竖坐标
        const canvas = document.querySelector('#canvas');
        let currentX, currentY, t = e.target
        if (this.isPc()) {
          currentX = e.clientX - t.parentNode.offsetLeft
          currentY = e.clientY - t.parentNode.offsetTop
        } else {
          if (e.changedTouches && e.changedTouches.length > 0 && e.changedTouches[0].clientX) {
            currentX = e.targetTouches[0].clientX - canvas.offsetLeft
            currentY = e.targetTouches[0].clientY - canvas.offsetTop
          } else {
            currentX = e.clientX - t.parentNode.offsetLeft
            currentY = e.clientY - t.parentNode.offsetTop
          }
        }
        return {
          x: currentX,
          y: currentY
        }
      },
      drawLine(beginPoint, controlPoint, endPoint) {
        this.context.beginPath();
        if (beginPoint && beginPoint.x) {
          this.context.moveTo(beginPoint.x, beginPoint.y);
          this.context.quadraticCurveTo(controlPoint.x, controlPoint.y, endPoint.x, endPoint.y);
          this.context.stroke();
          this.context.closePath();
        }

      },
      //mousemove
      canvasMove(e) {
        if (this.canvasMoveUse) {
          const {
            x,
            y
          } = this.getPos(e);
          points.push({
            x: x,
            y: y
          });
          if (points.length > 3) {
            const lastTwoPoints = points.slice(-2);
            const controlPoint = lastTwoPoints[0];
            const endPoint = {
              x: (lastTwoPoints[0].x + lastTwoPoints[1].x) / 2,
              y: (lastTwoPoints[0].y + lastTwoPoints[1].y) / 2,
            }
            this.drawLine(beginPoint, controlPoint, endPoint);
            beginPoint = endPoint;
            if (!this.hasSign) {
              this.hasSign = true
            }
          }
        }
        e.preventDefault();
      },
      signBack() {
        this.startSign = false;
      },
      // 设置绘画配置
      setCanvasStyle() {
        this.context.lineWidth = this.config.lineWidth
        this.context.shadowBlur = this.config.shadowBlur
        this.context.shadowColor = this.config.lineColor
        this.context.strokeStyle = this.config.lineColor
        this.context.lineJoin = 'round';
        this.context.lineCap = 'round';
      },
      clearCanvas() {
        const canvas = document.querySelector('#canvas')
        this.context.clearRect(0, 0, canvas.width, canvas.height);
        points = [];
        if (this.hasSign) {
          this.hasSign = false
        }
      },
      // 签名完成
      signatureComplete() {
        this.startSign = false;
        if (points.length > 3) {
          this.canCompleteSignature = true;
        } else {
          this.canCompleteSignature = false;
        }
        this.getImage();
        this.canvasUp();
        const canvas = document.querySelector('#canvas')
        this.context.clearRect(0, 0, canvas.width, canvas.height);
      },
      // 生成图片
      getImage() {
        const canvas = document.querySelector('#canvas')
        console.log("getImage", points, points.length)
        if (points.length > 3) {
          this.signImage = canvas.toDataURL('image/png')
        } else {
          this.signImage = ""
        }
      },
      isPc() {
        const userAgentInfo = navigator.userAgent
        const Agents = ['Android', 'iPhone', 'SymbianOS', 'Windows Phone', 'iPad', 'iPod']
        let flag = true
        for (let v = 0; v < Agents.length; v++) {
          if (userAgentInfo.indexOf(Agents[v]) > 0) {
            flag = false
            break
          }
        }
        return flag
      },
      showSign() {
        this.signDate = this.$timeUtils.getDate();
        this.showSignature = true;
        const canvas = document.querySelector('#canvas')
        this.context = canvas.getContext('2d')
        this.initDraw()
        this.setCanvasStyle()
        this.anti();
        this.signImage = ""
      },
      initDraw() {
        const preData = this.context.getImageData(0, 0, 600, 400)
        // 空绘图表面进栈
        this.middleAry.push(preData)
      },
      // 当在屏幕中移动时即开始绘制准备
      anti() {
        const canvas = document.querySelector('#canvas')
        console.log(document.body.clientWidth,"啊哈哈")
        canvas.style.width = document.body.clientWidth;
        canvas.style.height = document.body.clientHeight;
        canvas.height = document.body.clientHeight;
        canvas.width = document.body.clientWidth;
      },
    }
}
</script>
<style lang="scss" scoped>
    .indexs{
        /* 签字 */
        width: 100%;
        height: 100%;
        .signature {
            width: 69%;
            height: 100%;
            background: #FFFFFF;
            border-radius: 20px 20px 0px 0px;
            position: absolute;
            bottom: 0;
            left: 0;
            .signature_content {
                width: calc(100% - 40px);
                height: calc(100% - 100px);
                padding: 10px 20px;
                background: #EBEBEB;
                overflow-y: scroll;
                overflow-x: hidden;
                -webkit-overflow-scrolling: touch;
            }
            .signature_confrim {
                width: calc(100% - 76px);
                height: 48px;
                background: linear-gradient(180deg, #FF8A2A 0%, #FE702B 100%);
                border-radius: 24px;
                margin-left: 38px;
                margin-top: 10px;
                font-size: 17px;
                font-family: PingFangSC-Regular, PingFang SC;
                font-weight: 400;
                color: #FFFFFF;
                line-height: 48px;
                text-align: center;
            }
        }
        .canvas_container {
            width: calc(69% - 4px);
            height: calc(100% - 4px);
            border: 2px dashed #979797;
            position: absolute;
            left: 0;
            top: 0;
            background-color: #ffffff;
            overflow: hidden;
            -webkit-overflow-scrolling: touch;

            .signature_control {
            position: absolute;
            left: 35px;
            bottom: 220px;
            display: flex;
            align-items: center;
            margin-top: 10px;
            width: 220px;
            padding: 10px 20px;
            justify-content: space-evenly;
            transform: rotate(90deg);
            transform-origin: left;

            }

            .signature_control span:nth-child(1) {
            font-size: 14px;
            font-family: PingFangSC-Regular, PingFang SC;
            font-weight: 400;
            color: #999999;
            }

            .signature_control span:nth-child(2) {
            font-size: 14px;
            font-family: PingFangSC-Regular, PingFang SC;
            font-weight: 400;
            color: #3E7DFE;
            }

            .signature_control span:nth-child(3) {
            font-size: 17px;
            font-family: PingFangSC-Regular, PingFang SC;
            font-weight: 400;
            color: #FFFFFF;
            width: 96px;
            height: 49px;
            line-height: 49px;
            text-align: center;
            background: linear-gradient(180deg, #FF8A2A 0%, #FE702B 100%);
            border-radius: 24px;
            }
        }
    }
</style>

在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值