canvas电子签名例子,移动端PC端都可以用

@touchstart=“touchStart”:这是一个触摸事件,当用户在移动设备上触摸屏幕并开始触摸 canvas 元素时触发
@touchmove=“touchMove”:这是一个触摸后滑动事件,当用户在移动设备上触摸屏幕并在 canvas 元素上滑动时触发
@touchend=“touchEnd”:这是一个触摸=结束事件,当用户在移动设备上触摸屏幕并结束触摸 canvas 元素时触发
@mousedown=“mouseDown”:这是一个鼠标点击事件,当用户在桌面设备上点击鼠标按钮时触发
@mousemove=“mouseMove”:这是一个鼠标滑动事件,当用户在桌面设备上移动鼠标时触发
@mouseup=“mouseUp”:这是一个鼠标离开事件,当用户在桌面设备上释放鼠标按钮时触发

第一种 这个移动端PC端都可以

<template>
  <div class="homes">
    <div class="head_div imgClass">
      <img style="width: 100%;height: 100%;" :src="resultImg" alt="">
    </div>
    <div ref="canvasHW" class="body_div">
      <canvas ref="canvasF" style="width: 100%;height: 100%;" @touchstart="touchStart" @touchmove="touchMove"
        @touchend="touchEnd" @mousedown="mouseDown" @mousemove="mouseMove" @mouseup="mouseUp">
      </canvas>
    </div>
    <div class="font_div">
      <div style="margin-right: 20px;">
        背景颜色:<input type="color" v-model="canvasBGC" @change="BGC_color" />
      </div>
      <div style="margin-right: 20px;">
        文字颜色:<input type="color" v-model="strokeStyle" @change="Text_color" />
      </div>
      <el-button type="danger" @click.native.prevent="handleOverwrite()" size="mini">重签</el-button>
      <el-button type="danger" @click.native.prevent="handleSave()" size="mini">确定</el-button>

    </div>
  </div>
</template>
<style scoped>
.homes {
  width: 100%;
  height: 100vh;
  display: flex;
  flex-direction: column;
}

.homes .head_div {
  width: 100%;
  height: 80px;
  background-color: aquamarine;
}

.homes .font_div {
  width: 100%;
  height: 50px;
  background-color: aquamarine;
  display: flex;

}

.homes .imgClass {
  width: 100%;
  height: 200px;
}

.homes .body_div {
  flex: 1;
  box-sizing: border-box;
  padding: 10px;
  background-color: burlywood;
}

.body_div canvas {
  border: 1px solid #00a8ff;
}
</style>

<script>
export default {
  data() {
    return {
      resultImg: null,
      points: [],
      canvasTxt: null,
      stage_info: [],
      startX: 0,
      startY: 0,
      moveY: 0,
      moveX: 0,
      isDown: false,
      strokeStyle: '#CA6363',
      canvasBGC: '#8688D0',
      lineWidth: 5,

      config: {
        width: 0, // 宽度
        height: 0, // 高度
        can_width: 0,
        can_height: 0,

      }
    }
  },
  created() {


  },
  mounted() {
    // this.canvasBGC = '#36d'
    // this.strokeStyle = '#000'

    this.initCanvas()

  },
  methods: {
    // 初始化Canvas
    initCanvas() {
      const {
        width, height,
      } = document.querySelector('.body_div');

      this.config.width = width;
      this.config.height = height;



      let canvas = this.$refs.canvasF
      // 获取画布的宽度
      canvas.width = this.$refs.canvasHW.offsetWidth - 20
      this.config.can_width = canvas.width;

      // 获取画布的高度
      canvas.height = this.$refs.canvasHW.offsetHeight
      this.config.height = canvas.height;
      // 创建 context 对象
      this.canvasTxt = canvas.getContext('2d')
      this.stage_info = canvas.getBoundingClientRect()
      // 设置canvas背景色
      this.canvasTxt.fillStyle = this.canvasBGC;
      this.canvasTxt.fillRect(0, 0, canvas.width, canvas.height);
    },
    BGC_color(event) {
      console.log('选择的颜色', event.target.value);
      this.canvasBGC = event.target.value
      this.initCanvas()
    },
    Text_color(event) {
      this.strokeStyle = event.target.value
    },
    // 鼠标按下事件 - 准备绘画
    mouseDown(ev) {
      ev = ev || event
      ev.preventDefault()
      if (ev) {
        let obj = {
          x: ev.offsetX,
          y: ev.offsetY
        }
        this.startX = obj.x
        this.startY = obj.y
        this.canvasTxt.beginPath()
        this.canvasTxt.moveTo(this.startX, this.startY)
        this.canvasTxt.lineTo(obj.x, obj.y)
        this.canvasTxt.stroke()
        this.canvasTxt.closePath()
        this.points.push(obj)
        this.isDown = true
      }
    },
    // 触摸开始事件 - 准备绘画
    touchStart(ev) {
      ev = ev || event
      ev.preventDefault()
      if (ev.touches.length == 1) {
        let obj = {
          x: ev.targetTouches[0].clientX - this.stage_info.left,
          y: ev.targetTouches[0].clientY - this.stage_info.top
        }
        this.startX = obj.x
        this.startY = obj.y
        this.canvasTxt.beginPath()
        this.canvasTxt.moveTo(this.startX, this.startY)
        this.canvasTxt.lineTo(obj.x, obj.y)
        this.canvasTxt.stroke()
        this.canvasTxt.closePath()
        this.points.push(obj)
      }
    },
    // 鼠标移动事件 - 开始绘画
    mouseMove(ev) {
      ev = ev || event
      ev.preventDefault()
      if (this.isDown) {
        let obj = {
          x: ev.offsetX,
          y: ev.offsetY
        }
        this.moveY = obj.y
        this.moveX = obj.x
        this.canvasTxt.strokeStyle = this.strokeStyle
        this.canvasTxt.lineWidth = this.lineWidth
        this.canvasTxt.beginPath()
        this.canvasTxt.moveTo(this.startX, this.startY)
        this.canvasTxt.lineTo(obj.x, obj.y)
        this.canvasTxt.stroke()
        this.canvasTxt.closePath()
        this.startY = obj.y
        this.startX = obj.x
        this.points.push(obj)
      }
    },
    // 触摸移动事件 - 开始绘画
    touchMove(ev) {
      ev = ev || event
      ev.preventDefault()
      if (ev.touches.length == 1) {
        let obj = {
          x: ev.targetTouches[0].clientX - this.stage_info.left,
          y: ev.targetTouches[0].clientY - this.stage_info.top
        }
        this.moveY = obj.y
        this.moveX = obj.x
        // 设置线条颜色
        this.canvasTxt.strokeStyle = this.strokeStyle
        // 设置线条宽度
        this.canvasTxt.lineWidth = this.lineWidth
        // 绘制开始路径
        this.canvasTxt.beginPath()
        // 定义线条开始坐标
        this.canvasTxt.moveTo(this.startX, this.startY)
        // 定义线条结束坐标
        this.canvasTxt.lineTo(obj.x, obj.y)
        // 绘制线条
        this.canvasTxt.stroke()
        this.canvasTxt.closePath()
        this.startY = obj.y
        this.startX = obj.x
        this.points.push(obj)
      }
    },
    // 松开鼠标事件 - 停止绘画
    mouseUp(ev) {
      ev = ev || event
      ev.preventDefault()
      if (ev) {
        let obj = {
          x: ev.offsetX,
          y: ev.offsetY
        }
        this.canvasTxt.beginPath()
        this.canvasTxt.moveTo(this.startX, this.startY)
        this.canvasTxt.lineTo(obj.x, obj.y)
        this.canvasTxt.stroke()
        this.canvasTxt.closePath()
        this.points.push(obj)
        this.points.push({ x: -1, y: -1 })
        this.isDown = false
      }
    },
    // 触摸结束事件 - 停止绘画
    touchEnd(ev) {
      ev = ev || event
      ev.preventDefault()
      if (ev.touches.length == 1) {
        let obj = {
          x: ev.targetTouches[0].clientX - this.stage_info.left,
          y: ev.targetTouches[0].clientY - this.stage_info.top
        }
        this.canvasTxt.beginPath()
        this.canvasTxt.moveTo(this.startX, this.startY)
        this.canvasTxt.lineTo(obj.x, obj.y)
        this.canvasTxt.stroke()
        this.canvasTxt.closePath()
        this.points.push(obj)
        this.points.push({ x: -1, y: -1 })
      }
    },
    // 重写
    handleOverwrite() {
      this.canvasTxt.clearRect(0, 0, this.$refs.canvasF.width, this.$refs.canvasF.height)
      this.points = []
      this.resultImg = null
    },
    handleSave() {

      return new Promise((resolve, reject) => {
        if (!this.isCanvasBlank(this.$refs.canvasF)) {
          // this.rotateBase64(this.$refs.canvasF.toDataURL('image/png')).then((img) => {
          //   const imgBase64 = img;
          //   console.log(imgBase64, 'imgBase64-->>'); // base64编码
          //   this.resultImg = imgBase64
          // });
          this.resultImg = this.$refs.canvasF.toDataURL('image/png')
          console.log(this.resultImg, 'imgBase64-->>'); // base64编码
        } else {
          const err = '请签名';
          reject(err);
        }
      });
    },
    // 判断canvas对象是否空
    isCanvasBlank(canvas) {
      const blank = document.createElement('canvas'); // 系统获取一个空canvas对象
      blank.width = this.config.width;
      blank.height = this.config.height;
      return canvas.toDataURL() === blank.toDataURL(); // 比较值相等则为空
    },
    // 将base64图片旋转90度以后上传
    rotateBase64(imgBase64) {
      return new Promise((resolve) => {
        const imgView = new Image();
        imgView.src = imgBase64;
        const canvas = document.createElement('canvas');
        const context = canvas.getContext('2d');
        const cutCoor = {
          sx: 0,
          sy: 0,
          ex: 0,
          ey: 0,
        };
        // 裁剪坐标
        imgView.onload = () => {
          const imgW = imgView.width;
          const imgH = imgView.height;
          const size = imgH;
          canvas.width = size * 2;
          canvas.height = size * 2;
          cutCoor.sx = size;
          cutCoor.sy = size - imgW;
          cutCoor.ex = size + imgH;
          cutCoor.ey = size + imgW;
          context.translate(size, size);
          context.rotate((Math.PI / 2) * 3);
          context.drawImage(imgView, 0, 0);
          const imgData = context.getImageData(cutCoor.sx, cutCoor.sy, cutCoor.ex, cutCoor.ey);
          canvas.width = imgH;
          canvas.height = imgW;
          context.putImageData(imgData, 0, 0);
          resolve(canvas.toDataURL('image/png'));
        };
      });
    },
  }
}
</script>

在这里插入图片描述

第二种 这个只能移动端上

<template>
  <div class="homes">
    <div class="head_div imgClass">
      <img style="width: 100%;height: 100%;" :src="resultImg" alt="">
    </div>
    <div class="body_div">
      <canvas class="handWriting" :disable-scroll="true" @touchstart="touchStart" @touchmove="touchMove"
        @touchend="touchEnd" id="canvas" ref="canvas"></canvas>
    </div>
    <div class="head_div">
      <el-button type="danger" @click.native.prevent="handleOverwrite()" size="mini">重签</el-button>
      <el-button type="danger" @click.native.prevent="handleSave()" size="mini">确定</el-button>

    </div>

  </div>
</template>

<script>
const $ = (name) => document.querySelector(name);
// 配置内容
const config = {
  width: 0, // 宽度
  height: 0, // 高度
  lineWidth: 5, // 线宽
  strokeStyle: '#000000', // 线条颜色
  lineCap: 'round', // 设置线条两端圆角
  lineJoin: 'round', // 线条交汇处圆角
};
// 偏移量
const client = {
  offsetX: 0,
  offsetY: 0,
};

let canvas;
let ctx;
export default {
  data() {
    return {
      resultImg: null
    }
  },
  created() {

  },
  mounted() {
    this.init();
  },
  methods: {
    // 初始化画板
    init() {
      const {
        width, height, left, top,
      } = $('.body_div').getBoundingClientRect();
      config.width = width;
      config.height = height;
      client.offsetX = left;
      client.offsetY = top;
      // canvas 实例
      canvas = $('#canvas');
      // 设置宽高
      canvas.width = config.width;
      canvas.height = config.height;
      // 设置边框
      //   canvas.style.border = '1px solid #000';
      // 创建上下文
      ctx = canvas.getContext('2d');
      // 设置填充背景色
      ctx.fillStyle = 'transparent';
      // 绘制填充矩形
      ctx.fillRect(
        0, // x 轴起始绘制位置
        0, // y 轴起始绘制位置
        config.width, // 宽度
        config.height, // 高度
      );

    },
    // 触摸开始
    touchStart(event) {
      if (event.type != 'touchstart') return false;
      event.preventDefault();
      // 获取偏移量及坐标
      const { clientX, clientY } = event.changedTouches[0];
      // 清除以上一次 beginPath 之后的所有路径,进行绘制
      ctx.beginPath();
      // 根据配置文件设置相应配置
      ctx.lineWidth = config.lineWidth;
      ctx.strokeStyle = config.strokeStyle;
      ctx.lineCap = config.lineCap;
      ctx.lineJoin = config.lineJoin;
      // 设置画线起始点位(减去 左边、上方的偏移量很关键)
      ctx.moveTo(clientX - client.offsetX, clientY - client.offsetY);
      // console.log('触摸开始', e);
    },
    // 滑动开始
    touchMove(ev) {
      if (ev.type != 'touchmove') return false;
      ev.preventDefault();
      // 获取当前坐标点位
      const { clientX, clientY } = ev.changedTouches[0];
      // 根据坐标点位移动添加线条(减去 左边、上方的偏移量很关键)
      ctx.lineTo(clientX - client.offsetX, clientY - client.offsetY);
      // 绘制
      ctx.stroke();
    },
    // 离开屏幕开始
    touchEnd(ev) {
      if (ev.type != 'touchend') return 0;
      ev.preventDefault()
      ctx.closePath();
      // 移除鼠标移动或手势移动监听器
      window.removeEventListener('mousemove', this.draw);
    },
    // 重写
    handleOverwrite() {
      ctx.clearRect(0, 0, config.width, config.height);
      this.resultImg = ''
    },
    // 确定
    handleSave() {
      return new Promise((resolve, reject) => {
        if (!this.isCanvasBlank(canvas)) {
          // this.rotateBase64(canvas.toDataURL('image/png')).then((img) => {
          //   const imgBase64 = img;
          //   console.log(imgBase64, 'imgBase64-->>'); // base64编码
          //   resolve(imgBase64);
          // });
          this.resultImg = canvas.toDataURL('image/png')

        } else {
          const err = '请签名';
          reject(err);
        }
      });
    },
    // 判断canvas对象是否空
    isCanvasBlank(canvas) {
      const blank = document.createElement('canvas'); // 系统获取一个空canvas对象
      blank.width = config.width;
      blank.height = config.height;
      return canvas.toDataURL() === blank.toDataURL(); // 比较值相等则为空
    },
    // 将base64图片旋转90度以后上传
    rotateBase64(imgBase64) {
      return new Promise((resolve) => {
        const imgView = new Image();
        imgView.src = imgBase64;
        const canvas = document.createElement('canvas');
        const context = canvas.getContext('2d');
        const cutCoor = {
          sx: 0,
          sy: 0,
          ex: 0,
          ey: 0,
        };
        // 裁剪坐标
        imgView.onload = () => {
          const imgW = imgView.width;
          const imgH = imgView.height;
          const size = imgH;
          //   常量大小 = imgH;
          canvas.width = size * 2;
          canvas.height = size * 2;
          cutCoor.sx = size;
          cutCoor.sy = size - imgW;
          cutCoor.ex = size + imgH;
          cutCoor.ey = size + imgW;
          context.translate(size, size);
          context.rotate((Math.PI / 2) * 3);
          context.drawImage(imgView, 0, 0);
          const imgData = context.getImageData(cutCoor.sx, cutCoor.sy, cutCoor.ex, cutCoor.ey);
          canvas.width = imgH;
          canvas.height = imgW;
          context.putImageData(imgData, 0, 0);
          resolve(canvas.toDataURL('image/png'));
        };
      });
    },
  }
}
</script>
<style scoped>
.homes {
  width: 100%;
  height: 100vh;
  display: flex;
  flex-direction: column;
}

.homes .head_div {
  width: 100%;
  height: 50px;
  background-color: aquamarine;
}

.homes .imgClass {
  width: 100%;
  height: 200px;
}

.homes .body_div {
  flex: 1;
  background-color: burlywood;
}
</style>

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

曾不错吖

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

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

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

打赏作者

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

抵扣说明:

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

余额充值